`text/plain` request bodies are parsed instead of streamed: non-UTF-8 bytes corrupted, bodies over 1 MiB rejected with 413

オープン 初心者向け
#493 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
2/5
見積もり時間
1〜3時間
初心者へのやさしさ
82/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
javascript, node.js

調査の方向性

プロキシがボディパーサーを登録している index.js の718~719行あたりから始め、近くにあるパーサーのテストを読んでください。非UTF-8バイトを含む text/plain のアップロードと、デフォルトのボディ上限を超えるボディをテスト対象に追加してください。次に必要なパーサーを登録し、テストで413レスポンスなしにバイト単位でそのまま転送されることを確認してください。

索引モデルが issue の本文から書いたものです。

説明

Prerequisites
  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported
Fastify version

5.12.8

Plugin version

@fastify/http-proxy 11.6.6 (also main at ce4d419), @fastify/reply-from 12.8.1

Node.js version

26.13.0

Operating system

Linux

Operating system version (i.e. 20.04, 11.3, 10)

Debian 12

Description

The README says non-JSON payloads are streamed directly to the destination. That holds for text/csv or application/octet-stream, but not for text/plain: Fastify's built-in text/plain parser still runs, because the plugin only overrides application/json and * (index.js lines 718-719). The body is decoded as UTF-8 and re-sent as a string, so a text/plain upload in any other charset is corrupted, and a text/plain upload larger than bodyLimit gets a 413 instead of being streamed.

// repro.mjs
import http from 'node:http';
import Fastify from 'fastify';
import proxy from '@fastify/http-proxy';

// upstream: report how many bytes arrived and the first bytes
http.createServer((req, res) => {
  const chunks = [];
  req.on('data', (c) => chunks.push(c));
  req.on('end', () => {
    const b = Buffer.concat(chunks);
    res.end(`got ${b.length} bytes: ${b.subarray(0, 6).toString('hex')}`);
  });
}).listen(3001);

const app = Fastify();
app.register(proxy, { upstream: 'http://127.0.0.1:3001' });
await app.listen({ port: 3000 });

const send = async (type, body) => {
  const r = await fetch('http://127.0.0.1:3000/upload', { method: 'POST', headers: { 'content-type': type }, body });
  console.log(type.padEnd(30), `sent ${body.length} bytes: ${body.subarray(0, 6).toString('hex')} ->`, r.status, await r.text());
};
const latin1 = Buffer.from('café crème', 'latin1');
await send('text/plain; charset=iso-8859-1', latin1);
await send('text/csv; charset=iso-8859-1', latin1);
await send('text/plain', Buffer.alloc(2 * 1024 * 1024, 'a'));
await send('application/octet-stream', Buffer.alloc(2 * 1024 * 1024, 'a'));
process.exit(0);

node repro.mjs prints:

text/plain; charset=iso-8859-1 sent 10 bytes: 636166e92063 -> 200 got 14 bytes: 636166efbfbd
text/csv; charset=iso-8859-1   sent 10 bytes: 636166e92063 -> 200 got 10 bytes: 636166e92063
text/plain                     sent 2097152 bytes: 616161616161 -> 413 {"statusCode":413,"code":"FST_ERR_CTP_BODY_TOO_LARGE","error":"Payload Too Large","message":"Request body is too large"}
application/octet-stream       sent 2097152 bytes: 616161616161 -> 200 got 2097152 bytes: 616161616161
Link to code that reproduces the bug

https://gist.github.com/trackoor/a9dc305ecd07ded3018ca5fea688a582

Expected Behavior

text/plain bodies reach the upstream byte for byte, like every other non-JSON type: 10 bytes 636166e92063 in the first line and 200 got 2097152 bytes in the third. Registering bodyParser for text/plain next to application/json should be enough.

Found with a differential-testing tool; the report was drafted with AI assistance and reviewed and edited by me.

Credit: https://ucsbplse.github.io/

主要言語
JavaScript
スター
394
フォーク
105
平均マージ
9時間 34分
マージ済み PR(30日)
2

環境構築

はじめの一歩

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

fastify/fastify-http-proxy のほかの issue

fastify/fastify-http-proxy の issue をすべて見る

似ている issue

JavaScript の issue をもっと見る

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

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