From f0262f2d4776ec44d66bb412bc9baa36b80be851 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 12 Jun 2026 18:08:15 +0600 Subject: [PATCH 1/2] [FSSDK-12759] fix content-length header in NodeRequestHandler --- .../request_handler.node.spec.ts | 85 +++++++++++++++++++ .../request_handler.node.ts | 7 +- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/lib/utils/http_request_handler/request_handler.node.spec.ts b/lib/utils/http_request_handler/request_handler.node.spec.ts index 33f190676..d0caa5396 100644 --- a/lib/utils/http_request_handler/request_handler.node.spec.ts +++ b/lib/utils/http_request_handler/request_handler.node.spec.ts @@ -16,6 +16,7 @@ import { describe, beforeEach, afterEach, beforeAll, afterAll, it, vi, expect } from 'vitest'; +import http from 'http'; import nock from 'nock'; import zlib from 'zlib'; import { NodeRequestHandler } from './request_handler.node'; @@ -202,6 +203,90 @@ describe('NodeRequestHandler', () => { scope.done(); }); + describe('content-length header', () => { + let server: http.Server; + let port: number; + let receivedContentLength: string | undefined; + let receivedBodyByteLength: number; + + beforeAll(async () => { + nock.enableNetConnect('localhost'); + server = http.createServer((req, res) => { + receivedContentLength = req.headers['content-length']; + + const chunks: Buffer[] = []; + req.on('data', (chunk: Buffer) => chunks.push(chunk)); + req.on('end', () => { + receivedBodyByteLength = Buffer.concat(chunks).length; + res.writeHead(200, { 'content-type': 'application/json' }); + res.end(JSON.stringify({ ok: true })); + }); + }); + + await new Promise((resolve) => { + server.listen(0, () => { + port = (server.address() as { port: number }).port; + resolve(); + }); + }); + }); + + afterAll(async () => { + await new Promise((resolve) => server.close(() => resolve())); + nock.disableNetConnect(); + }); + + it('should set correct content-length for ASCII-only data', async () => { + const data = '{"key":"value"}'; + + const { responsePromise } = nodeRequestHandler.makeRequest( + `http://localhost:${port}/test`, + { 'content-type': 'application/json' }, + 'POST', + data, + ); + await responsePromise; + + const expectedByteLength = Buffer.byteLength(data, 'utf8'); + expect(Number(receivedContentLength)).toBe(expectedByteLength); + expect(Number(receivedContentLength)).toBe(receivedBodyByteLength); + }); + + it('should set correct content-length for multi-byte UTF-8 data (emoji)', async () => { + const data = JSON.stringify({ message: '🚀 launch' }); + + const { responsePromise } = nodeRequestHandler.makeRequest( + `http://localhost:${port}/test`, + { 'content-type': 'application/json' }, + 'POST', + data, + ); + await responsePromise; + + const expectedByteLength = Buffer.byteLength(data, 'utf8'); + expect(data.length).not.toBe(expectedByteLength); + expect(Number(receivedContentLength)).toBe(expectedByteLength); + expect(Number(receivedContentLength)).toBe(receivedBodyByteLength); + }); + + it('should set correct content-length for multi-byte UTF-8 data (CJK characters)', async () => { + const data = JSON.stringify({ greeting: '你好世界' }); + + const { responsePromise } = nodeRequestHandler.makeRequest( + `http://localhost:${port}/test`, + { 'content-type': 'application/json' }, + 'POST', + data, + ); + await responsePromise; + + const expectedByteLength = Buffer.byteLength(data, 'utf8'); + expect(data.length).not.toBe(expectedByteLength); + expect(Number(receivedContentLength)).toBe(expectedByteLength); + expect(Number(receivedContentLength)).toBe(receivedBodyByteLength); + }); + }); + describe('timeout', () => { beforeEach(() => { vi.useFakeTimers(); diff --git a/lib/utils/http_request_handler/request_handler.node.ts b/lib/utils/http_request_handler/request_handler.node.ts index b972c0526..1c7ef4435 100644 --- a/lib/utils/http_request_handler/request_handler.node.ts +++ b/lib/utils/http_request_handler/request_handler.node.ts @@ -62,16 +62,13 @@ export class NodeRequestHandler implements RequestHandler { headers: { ...headers, 'accept-encoding': 'gzip,deflate', - 'content-length': String(data?.length || 0) + 'content-length': String(data ? Buffer.byteLength(data) : 0), }, timeout: this.timeout, }); const abortableRequest = this.getAbortableRequestFromRequest(request); - if (data) { - request.write(data); - } - request.end(); + request.end(data); return abortableRequest; } From fb7a063e531ff34da9ff634437c75857dd491250 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Fri, 12 Jun 2026 19:51:12 +0600 Subject: [PATCH 2/2] upd --- .../request_handler.node.spec.ts | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/lib/utils/http_request_handler/request_handler.node.spec.ts b/lib/utils/http_request_handler/request_handler.node.spec.ts index d0caa5396..8f5b62806 100644 --- a/lib/utils/http_request_handler/request_handler.node.spec.ts +++ b/lib/utils/http_request_handler/request_handler.node.spec.ts @@ -203,21 +203,18 @@ describe('NodeRequestHandler', () => { scope.done(); }); - describe('content-length header', () => { + describe('multi-byte unicode data', () => { let server: http.Server; let port: number; - let receivedContentLength: string | undefined; - let receivedBodyByteLength: number; + let receivedBody: string; beforeAll(async () => { nock.enableNetConnect('localhost'); server = http.createServer((req, res) => { - receivedContentLength = req.headers['content-length']; - const chunks: Buffer[] = []; req.on('data', (chunk: Buffer) => chunks.push(chunk)); req.on('end', () => { - receivedBodyByteLength = Buffer.concat(chunks).length; + receivedBody = Buffer.concat(chunks).toString('utf8'); res.writeHead(200, { 'content-type': 'application/json' }); res.end(JSON.stringify({ ok: true })); }); @@ -236,7 +233,7 @@ describe('NodeRequestHandler', () => { nock.disableNetConnect(); }); - it('should set correct content-length for ASCII-only data', async () => { + it('should correctly transmit ASCII data', async () => { const data = '{"key":"value"}'; const { responsePromise } = nodeRequestHandler.makeRequest( @@ -247,12 +244,10 @@ describe('NodeRequestHandler', () => { ); await responsePromise; - const expectedByteLength = Buffer.byteLength(data, 'utf8'); - expect(Number(receivedContentLength)).toBe(expectedByteLength); - expect(Number(receivedContentLength)).toBe(receivedBodyByteLength); + expect(receivedBody).toBe(data); }); - it('should set correct content-length for multi-byte UTF-8 data (emoji)', async () => { + it('should correctly transmit emoji data', async () => { const data = JSON.stringify({ message: '🚀 launch' }); const { responsePromise } = nodeRequestHandler.makeRequest( @@ -263,13 +258,10 @@ describe('NodeRequestHandler', () => { ); await responsePromise; - const expectedByteLength = Buffer.byteLength(data, 'utf8'); - expect(data.length).not.toBe(expectedByteLength); - expect(Number(receivedContentLength)).toBe(expectedByteLength); - expect(Number(receivedContentLength)).toBe(receivedBodyByteLength); + expect(receivedBody).toBe(data); }); - it('should set correct content-length for multi-byte UTF-8 data (CJK characters)', async () => { + it('should correctly transmit CJK character data', async () => { const data = JSON.stringify({ greeting: '你好世界' }); const { responsePromise } = nodeRequestHandler.makeRequest( @@ -280,10 +272,7 @@ describe('NodeRequestHandler', () => { ); await responsePromise; - const expectedByteLength = Buffer.byteLength(data, 'utf8'); - expect(data.length).not.toBe(expectedByteLength); - expect(Number(receivedContentLength)).toBe(expectedByteLength); - expect(Number(receivedContentLength)).toBe(receivedBodyByteLength); + expect(receivedBody).toBe(data); }); });