From 67931bec53c847d8b15a14e66e968a2829a5546e Mon Sep 17 00:00:00 2001 From: Michael Solomon Date: Mon, 18 Mar 2024 22:28:08 +0200 Subject: [PATCH 1/5] fix readable --- .../ClientRequest/MockHttpSocket.ts | 5 +++- src/interceptors/Socket/MockSocket.ts | 2 +- .../http/compliance/http-req-write.test.ts | 28 ++++++++++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/interceptors/ClientRequest/MockHttpSocket.ts b/src/interceptors/ClientRequest/MockHttpSocket.ts index 9b969ff1..7da4725e 100644 --- a/src/interceptors/ClientRequest/MockHttpSocket.ts +++ b/src/interceptors/ClientRequest/MockHttpSocket.ts @@ -335,7 +335,10 @@ export class MockHttpSocket extends MockSocket { // this ensures that each request gets its own stream. // One Socket instance can only handle one request at a time. if (canHaveBody) { - this.requestStream = new Readable() + this.requestStream = new Readable({ + // Dummy implementation. We control the queue in the onRequestBody\End functions + read: () => {} + }) } const requestId = randomUUID() diff --git a/src/interceptors/Socket/MockSocket.ts b/src/interceptors/Socket/MockSocket.ts index d35976c9..3dc31fe7 100644 --- a/src/interceptors/Socket/MockSocket.ts +++ b/src/interceptors/Socket/MockSocket.ts @@ -34,7 +34,7 @@ export class MockSocket extends net.Socket { public write(...args: Array): boolean { const [chunk, encoding, callback] = normalizeWriteArgs(args as WriteArgs) this.options.write(chunk, encoding, callback) - return false + return true } public end(...args: Array) { diff --git a/test/modules/http/compliance/http-req-write.test.ts b/test/modules/http/compliance/http-req-write.test.ts index a1a308c2..918d062f 100644 --- a/test/modules/http/compliance/http-req-write.test.ts +++ b/test/modules/http/compliance/http-req-write.test.ts @@ -6,7 +6,8 @@ import http from 'node:http' import express from 'express' import { HttpServer } from '@open-draft/test-server/http' import { ClientRequestInterceptor } from '../../../../src/interceptors/ClientRequest' -import { waitForClientRequest } from '../../../helpers' +import { sleep, waitForClientRequest } from '../../../helpers' +import { Readable } from 'node:stream' const httpServer = new HttpServer((app) => { app.post('/resource', express.text({ type: '*/*' }), (req, res) => { @@ -93,6 +94,31 @@ it('writes Buffer request body', async () => { expect(await text()).toEqual(expectedBody) }) +it('writes Readable request body', async () => { + const req = http.request(httpServer.http.url('/resource'), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }) + + const input = ['hello', ' ', 'world', null] + const readable = new Readable({ + read: async function() { + await sleep(10) + this.push(input.shift()) + }, + }) + + readable.pipe(req) + + const { text } = await waitForClientRequest(req) + const expectedBody = 'hello world' + + expect(interceptedRequestBody).toHaveBeenCalledWith(expectedBody) + expect(await text()).toEqual(expectedBody) +}) + it('calls the callback when writing an empty string', async () => { const request = http.request(httpServer.http.url('/resource'), { method: 'POST', From 771d1c2a499deb50811299285f82ec6bf83f629b Mon Sep 17 00:00:00 2001 From: Michael Solomon Date: Wed, 20 Mar 2024 20:32:58 +0200 Subject: [PATCH 2/5] fix --- test/modules/http/compliance/http-req-write.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/modules/http/compliance/http-req-write.test.ts b/test/modules/http/compliance/http-req-write.test.ts index 918d062f..5335ec9b 100644 --- a/test/modules/http/compliance/http-req-write.test.ts +++ b/test/modules/http/compliance/http-req-write.test.ts @@ -1,13 +1,13 @@ /** * @vitest-environment node */ +import { Readable } from 'node:stream' import { vi, it, expect, beforeAll, afterEach, afterAll } from 'vitest' import http from 'node:http' import express from 'express' import { HttpServer } from '@open-draft/test-server/http' import { ClientRequestInterceptor } from '../../../../src/interceptors/ClientRequest' import { sleep, waitForClientRequest } from '../../../helpers' -import { Readable } from 'node:stream' const httpServer = new HttpServer((app) => { app.post('/resource', express.text({ type: '*/*' }), (req, res) => { @@ -94,7 +94,7 @@ it('writes Buffer request body', async () => { expect(await text()).toEqual(expectedBody) }) -it('writes Readable request body', async () => { +it('supports Readable as the request body', async () => { const req = http.request(httpServer.http.url('/resource'), { method: 'POST', headers: { From 141933705dc8b6b0ef1bf17461d475d181236376 Mon Sep 17 00:00:00 2001 From: Michael Solomon Date: Thu, 21 Mar 2024 18:32:37 +0200 Subject: [PATCH 3/5] fix --- src/interceptors/ClientRequest/MockHttpSocket.ts | 5 ++++- src/interceptors/Socket/MockSocket.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/interceptors/ClientRequest/MockHttpSocket.ts b/src/interceptors/ClientRequest/MockHttpSocket.ts index 7da4725e..656b6ac0 100644 --- a/src/interceptors/ClientRequest/MockHttpSocket.ts +++ b/src/interceptors/ClientRequest/MockHttpSocket.ts @@ -376,7 +376,9 @@ export class MockHttpSocket extends MockSocket { 'Failed to write to a request stream: stream does not exist' ) - this.requestStream.push(chunk) + if (this.requestStream.push(chunk)) { + process.nextTick(() => this.emit('drain')) + } } private onRequestEnd(): void { @@ -384,6 +386,7 @@ export class MockHttpSocket extends MockSocket { if (this.requestStream) { this.requestStream.push(null) } + process.nextTick(() => this.emit('drain')) } private onResponseStart: ResponseHeadersCompleteCallback = ( diff --git a/src/interceptors/Socket/MockSocket.ts b/src/interceptors/Socket/MockSocket.ts index 3dc31fe7..d35976c9 100644 --- a/src/interceptors/Socket/MockSocket.ts +++ b/src/interceptors/Socket/MockSocket.ts @@ -34,7 +34,7 @@ export class MockSocket extends net.Socket { public write(...args: Array): boolean { const [chunk, encoding, callback] = normalizeWriteArgs(args as WriteArgs) this.options.write(chunk, encoding, callback) - return true + return false } public end(...args: Array) { From 78ba640ecf6bf272a9b9b4464fe64cb7eda750d4 Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Thu, 21 Mar 2024 19:06:55 +0100 Subject: [PATCH 4/5] fix(MockSocket): return true from .write() --- src/interceptors/ClientRequest/MockHttpSocket.ts | 13 +++++++------ src/interceptors/Socket/MockSocket.ts | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/interceptors/ClientRequest/MockHttpSocket.ts b/src/interceptors/ClientRequest/MockHttpSocket.ts index 656b6ac0..b21863f4 100644 --- a/src/interceptors/ClientRequest/MockHttpSocket.ts +++ b/src/interceptors/ClientRequest/MockHttpSocket.ts @@ -336,8 +336,12 @@ export class MockHttpSocket extends MockSocket { // One Socket instance can only handle one request at a time. if (canHaveBody) { this.requestStream = new Readable({ - // Dummy implementation. We control the queue in the onRequestBody\End functions - read: () => {} + /** + * @note Provide the `read()` method so a `Readable` could be + * used as the actual request body (the stream calls "read()"). + * We control the queue in the onRequestBody/End functions. + */ + read: () => {}, }) } @@ -376,9 +380,7 @@ export class MockHttpSocket extends MockSocket { 'Failed to write to a request stream: stream does not exist' ) - if (this.requestStream.push(chunk)) { - process.nextTick(() => this.emit('drain')) - } + this.requestStream.push(chunk) } private onRequestEnd(): void { @@ -386,7 +388,6 @@ export class MockHttpSocket extends MockSocket { if (this.requestStream) { this.requestStream.push(null) } - process.nextTick(() => this.emit('drain')) } private onResponseStart: ResponseHeadersCompleteCallback = ( diff --git a/src/interceptors/Socket/MockSocket.ts b/src/interceptors/Socket/MockSocket.ts index d35976c9..3dc31fe7 100644 --- a/src/interceptors/Socket/MockSocket.ts +++ b/src/interceptors/Socket/MockSocket.ts @@ -34,7 +34,7 @@ export class MockSocket extends net.Socket { public write(...args: Array): boolean { const [chunk, encoding, callback] = normalizeWriteArgs(args as WriteArgs) this.options.write(chunk, encoding, callback) - return false + return true } public end(...args: Array) { From 276613547166caf22e4364ff9b359e7a3b0c4430 Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Thu, 21 Mar 2024 19:11:40 +0100 Subject: [PATCH 5/5] chore: remove leftover log in test --- test/modules/http/regressions/http-concurrent-same-host.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/modules/http/regressions/http-concurrent-same-host.test.ts b/test/modules/http/regressions/http-concurrent-same-host.test.ts index 1706aa2b..09b8b1dd 100644 --- a/test/modules/http/regressions/http-concurrent-same-host.test.ts +++ b/test/modules/http/regressions/http-concurrent-same-host.test.ts @@ -10,8 +10,6 @@ let requests: Array = [] const interceptor = new ClientRequestInterceptor() interceptor.on('request', ({ request }) => { - console.log('REQUEST', request.method, request.url) - requests.push(request) request.respondWith(new Response()) })