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..8f5b62806 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,79 @@ describe('NodeRequestHandler', () => { scope.done(); }); + describe('multi-byte unicode data', () => { + let server: http.Server; + let port: number; + let receivedBody: string; + + beforeAll(async () => { + nock.enableNetConnect('localhost'); + server = http.createServer((req, res) => { + const chunks: Buffer[] = []; + req.on('data', (chunk: Buffer) => chunks.push(chunk)); + req.on('end', () => { + receivedBody = Buffer.concat(chunks).toString('utf8'); + 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 correctly transmit ASCII data', async () => { + const data = '{"key":"value"}'; + + const { responsePromise } = nodeRequestHandler.makeRequest( + `http://localhost:${port}/test`, + { 'content-type': 'application/json' }, + 'POST', + data, + ); + await responsePromise; + + expect(receivedBody).toBe(data); + }); + + it('should correctly transmit emoji data', async () => { + const data = JSON.stringify({ message: '🚀 launch' }); + + const { responsePromise } = nodeRequestHandler.makeRequest( + `http://localhost:${port}/test`, + { 'content-type': 'application/json' }, + 'POST', + data, + ); + await responsePromise; + + expect(receivedBody).toBe(data); + }); + + it('should correctly transmit CJK character data', async () => { + const data = JSON.stringify({ greeting: '你好世界' }); + + const { responsePromise } = nodeRequestHandler.makeRequest( + `http://localhost:${port}/test`, + { 'content-type': 'application/json' }, + 'POST', + data, + ); + await responsePromise; + + expect(receivedBody).toBe(data); + }); + }); + 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; }