A one-byte asset is served with Content-Length: 0 and its byte still written, which corrupts a keep-alive connection
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 75/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- javascript, node.js, webpack
調査の方向性
バグは src/utils/compatibleAPI.js(または webpack-dev-middleware の src/utils.js)にあり、1バイトのアセットに対して byteLength が誤って計算されています。まず createReadStreamOrReadFileSync 関数と calcStartAndEnd ヘルパーを調べてください。再現スクリプトは問題を示しています。実行すると誤った Content-Length が確認できます。修正には、開始と終了が両方ともゼロだがファイルが1バイトを持つというエッジケースに対して、バイト長の計算を調整することが含まれます。
索引モデルが issue の本文から書いたものです。
説明
a compilation asset of exactly one byte is served with Content-Length: 0, and the byte is still written after the headers. on a keep-alive socket the next response then fails to parse (HPE_INVALID_CONSTANT in a Node client). an empty asset and a two-byte asset are fine
the cause is the stream length in createReadStreamOrReadFileSync, src/utils/compatibleAPI.js in 2.0.4
byteLength = end === 0 ? 0 : end - start + 1;
calcStartAndEnd clamps end to Math.max(offset, offset + len - 1), so an empty file and a one-byte file both arrive as start = 0, end = 0. the special case was written for the empty one and swallows the other. webpack-dev-middleware 8.3.0 has the same line in src/utils.js
repro
const http = require("node:http"), net = require("node:net");
const webpack = require("webpack"), middleware = require("@rspack/dev-middleware");
const { sources } = webpack;
const compiler = webpack({
mode: "development", devtool: false, context: __dirname, entry: "./src/index.js",
output: { path: __dirname + "/dist", filename: "bundle.js" },
plugins: [{ apply(c) { c.hooks.thisCompilation.tap("one", (compilation) => {
compilation.hooks.processAssets.tap({ name: "one", stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL }, () => {
compilation.emitAsset("one.txt", new sources.RawSource("a"));
compilation.emitAsset("two.txt", new sources.RawSource("ab"));
});
}); } }],
});
const mw = middleware(compiler, { stats: false });
const server = http.createServer((req, res) => mw(req, res, () => { res.statusCode = 404; res.end(); }));
server.listen(0, () => mw.waitUntilValid(async () => {
const port = server.address().port;
for (const f of ["one.txt", "two.txt"]) {
const raw = await new Promise((resolve) => {
const s = net.connect(port, "127.0.0.1", () => s.write(`GET /${f} HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n`));
const c = []; s.on("data", (d) => c.push(d)); s.on("close", () => resolve(Buffer.concat(c).toString()));
});
console.log(`--- ${f}\n${raw}`);
}
server.close(); mw.close();
}));
actual
--- one.txt
HTTP/1.1 200 OK
Content-Length: 0
a
--- two.txt
HTTP/1.1 200 OK
Content-Length: 2
ab
expected
Content-Length: 1 for one.txt
seen in practice through rspack's dev server: an empty stylesheet compiles to a single line feed in development, and every such stylesheet was served empty. I have a fix ready that sizes the stream from the byte count the caller already holds
- 主要言語
- JavaScript
- スター
- 6
- フォーク
- 1
- 平均マージ
- 6時間 36分
- マージ済み PR(30日)
- 2
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
rstackjs/rspack-dev-middleware のほかの issue
-
Dependency Dashboardオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 25/100
rstackjs/rspack-dev-middleware の issue をすべて見る
似ている issue
-
Bump Firebase JS SDK (12.19.0 → 13.0.0)対応中かも @SelaseKay が今日担当しました。 オープンNeeds Attention type: enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
invertase/react-native-firebase#9364 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
tchiotludo/akhq#3307 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1〜3時間 初心者へのやさしさ 90/100
DietrichGebert/ponytail#1063 ·
メンテナーはふだん 3 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
zen-browser/desktop#15809 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 1/5 1時間未満 初心者へのやさしさ 90/100