Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Socket.readerString corrupts UTF-8 characters split across TCP chunks

クローズ
#9,486 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 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 7D give ["{}", "{}"] 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 を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Effect-TS/effect のほかの issue

Effect-TS/effect の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。