Socket.readerString corrupts UTF-8 characters split across TCP chunks
メンテナーはふだん 1 日以内に返信
関連するプルリクエストがすでにマージされています。
- #9487 @jayhemnani9910 による — マージ済み
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 35/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- nodejs, typescript
- 領域
- networking
調査の方向性
Start with the reproduction in the issue and inspect packages/effect/src/socket/Socket.ts:268, where Socket.readerString decodes each item independently. Determine whether streaming decoding should apply only to byte-stream transports or also to WebSocket binary messages, especially for BOM handling, truncated input, and normal close. Add regression tests covering UTF-8 characters split across TCP chunks and the agreed WebSocket behavior.
索引モデルが issue の本文から書いたものです。
説明
What version of Effect is running?
effect 4.0.5, @effect/platform-node 4.0.5 (same code on main 7f32606ba)
What steps can reproduce the bug?
A TCP server sends café ☕ done one byte at a time. The client reads it with Socket.readerString over NodeSocket.makeNet:
import * as net from "node:net"
import { Effect } from "effect"
import * as Socket from "effect/socket/Socket"
import * as NodeSocket from "@effect/platform-node/NodeSocket"
const bytes = Buffer.from("café ☕ done", "utf8")
const server = net.createServer(async (c) => {
for (let i = 0; i < bytes.length; i++) {
c.write(bytes.subarray(i, i + 1))
await new Promise((r) => setTimeout(r, 5))
}
c.end()
})
await new Promise<void>((r) => server.listen(0, "127.0.0.1", r))
const { port } = server.address() as net.AddressInfo
const text = await Effect.gen(function*() {
const socket = yield* NodeSocket.makeNet({ host: "127.0.0.1", port })
const pull = yield* Socket.readerString(socket)
let text = ""
while (true) {
const r = yield* Effect.result(pull)
if (r._tag === "Failure") return text
text += r.success.join("")
}
}).pipe(Effect.scoped, Effect.runPromise)
console.log(JSON.stringify(text))
server.close()
What is the expected behavior?
"café ☕ done"
What do you see instead?
"caf�� ��� done"
Additional information
readerString creates one TextDecoder per acquisition but calls decoder.decode(item) without { stream: true } (packages/effect/src/socket/Socket.ts:268), so every chunk is decoded on its own. TCP chunk boundaries are arbitrary, so any multi-byte character can land on one. toChannelString uses the same reader, and DevTools runs NDJSON over it.
Decoding in streaming mode fixes TCP, but it changes WebSocket binary messages, which are complete on their own:
- A UTF-8 BOM is stripped from every message today; in streaming mode only from the first. Two binary messages
EF BB BF 7B 7Dgive["{}", "{}"]today and["{}", "{}"]in streaming mode. - A truncated message's leftover bytes would join the next message instead of becoming U+FFFD.
I have a fix that decodes in streaming mode and, like #9436 for Channel.decodeText, flushes incomplete trailing bytes as U+FFFD on a normal close (code 1000), with tests. Is that the behaviour you want for WebSocket too, or should streaming decode apply only to byte-stream transports? Happy to open a PR either way.
- 主要言語
- TypeScript
- スター
- 16.7k
- フォーク
- 808
- 平均マージ
- 10時間 12分
- マージ済み PR(30日)
- 813
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Effect-TS/effect のほかの issue
-
BrowserWorkerRunner: port finalizer throws when the worker global has no close() (Bun)対応中かも @santiago-ramos-02 が 20 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
Effect-TS/effect#8635 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
Support {self: this} for fnUntraced再び着手できるかも @ArjunCodess が 31 日前に担当しましたが、オープン中のプルリクエストはありません。 オープンenhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
Effect-TS/effect#8101 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 5/5 1週間以上 初心者へのやさしさ 32/100
Effect-TS/effect#9610 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
Schema.isStringFinite (and Number string matching) takes quadratic time on long digit runs対応中かも @magoz が今日担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 66/100
メンテナーはふだん 1 日以内に返信
-
Schema.isBase64 / isBase64Url throw RangeError on multi-megabyte strings対応中かも @magoz が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 20/100
メンテナーはふだん 1 日以内に返信
Effect-TS/effect の issue をすべて見る
似ている issue
-
engine-rs P3 review-deferral wasm
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
maniator/verticopolis#1154 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
メンテナーはふだん 1 日以内に返信
-
Bug
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
telerik/kendo-react#3861 ·
メンテナーはふだん 1 日以内に返信
-
[bug]: use-mobile.ts uses pixel logic, causing UI state de-sync with Tailwind CSS rem breakpointsオープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
task
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100