Skip to content

Commit f0262f2

Browse files
committed
[FSSDK-12759] fix content-length header in NodeRequestHandler
1 parent 80e4c84 commit f0262f2

2 files changed

Lines changed: 87 additions & 5 deletions

File tree

lib/utils/http_request_handler/request_handler.node.spec.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import { describe, beforeEach, afterEach, beforeAll, afterAll, it, vi, expect } from 'vitest';
1818

19+
import http from 'http';
1920
import nock from 'nock';
2021
import zlib from 'zlib';
2122
import { NodeRequestHandler } from './request_handler.node';
@@ -202,6 +203,90 @@ describe('NodeRequestHandler', () => {
202203
scope.done();
203204
});
204205

206+
describe('content-length header', () => {
207+
let server: http.Server;
208+
let port: number;
209+
let receivedContentLength: string | undefined;
210+
let receivedBodyByteLength: number;
211+
212+
beforeAll(async () => {
213+
nock.enableNetConnect('localhost');
214+
server = http.createServer((req, res) => {
215+
receivedContentLength = req.headers['content-length'];
216+
217+
const chunks: Buffer[] = [];
218+
req.on('data', (chunk: Buffer) => chunks.push(chunk));
219+
req.on('end', () => {
220+
receivedBodyByteLength = Buffer.concat(chunks).length;
221+
res.writeHead(200, { 'content-type': 'application/json' });
222+
res.end(JSON.stringify({ ok: true }));
223+
});
224+
});
225+
226+
await new Promise<void>((resolve) => {
227+
server.listen(0, () => {
228+
port = (server.address() as { port: number }).port;
229+
resolve();
230+
});
231+
});
232+
});
233+
234+
afterAll(async () => {
235+
await new Promise<void>((resolve) => server.close(() => resolve()));
236+
nock.disableNetConnect();
237+
});
238+
239+
it('should set correct content-length for ASCII-only data', async () => {
240+
const data = '{"key":"value"}';
241+
242+
const { responsePromise } = nodeRequestHandler.makeRequest(
243+
`http://localhost:${port}/test`,
244+
{ 'content-type': 'application/json' },
245+
'POST',
246+
data,
247+
);
248+
await responsePromise;
249+
250+
const expectedByteLength = Buffer.byteLength(data, 'utf8');
251+
expect(Number(receivedContentLength)).toBe(expectedByteLength);
252+
expect(Number(receivedContentLength)).toBe(receivedBodyByteLength);
253+
});
254+
255+
it('should set correct content-length for multi-byte UTF-8 data (emoji)', async () => {
256+
const data = JSON.stringify({ message: '🚀 launch' });
257+
258+
const { responsePromise } = nodeRequestHandler.makeRequest(
259+
`http://localhost:${port}/test`,
260+
{ 'content-type': 'application/json' },
261+
'POST',
262+
data,
263+
);
264+
await responsePromise;
265+
266+
const expectedByteLength = Buffer.byteLength(data, 'utf8');
267+
expect(data.length).not.toBe(expectedByteLength);
268+
expect(Number(receivedContentLength)).toBe(expectedByteLength);
269+
expect(Number(receivedContentLength)).toBe(receivedBodyByteLength);
270+
});
271+
272+
it('should set correct content-length for multi-byte UTF-8 data (CJK characters)', async () => {
273+
const data = JSON.stringify({ greeting: '你好世界' });
274+
275+
const { responsePromise } = nodeRequestHandler.makeRequest(
276+
`http://localhost:${port}/test`,
277+
{ 'content-type': 'application/json' },
278+
'POST',
279+
data,
280+
);
281+
await responsePromise;
282+
283+
const expectedByteLength = Buffer.byteLength(data, 'utf8');
284+
expect(data.length).not.toBe(expectedByteLength);
285+
expect(Number(receivedContentLength)).toBe(expectedByteLength);
286+
expect(Number(receivedContentLength)).toBe(receivedBodyByteLength);
287+
});
288+
});
289+
205290
describe('timeout', () => {
206291
beforeEach(() => {
207292
vi.useFakeTimers();

lib/utils/http_request_handler/request_handler.node.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,13 @@ export class NodeRequestHandler implements RequestHandler {
6262
headers: {
6363
...headers,
6464
'accept-encoding': 'gzip,deflate',
65-
'content-length': String(data?.length || 0)
65+
'content-length': String(data ? Buffer.byteLength(data) : 0),
6666
},
6767
timeout: this.timeout,
6868
});
6969
const abortableRequest = this.getAbortableRequestFromRequest(request);
7070

71-
if (data) {
72-
request.write(data);
73-
}
74-
request.end();
71+
request.end(data);
7572

7673
return abortableRequest;
7774
}

0 commit comments

Comments
 (0)