-
-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(ClientRequest): use ServerResponse
to build the HTTP response string
#596
Conversation
callback() | ||
}, | ||
}) | ||
const fakeResponse = new ServerResponse(new IncomingMessage(new net.Socket())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any chance this can also handle compression for us?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm.. good point.
I thought we would get this OOTB from Node.js when the response goes back to the client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope so! I think we have some tests for compression but I got a new issue about it in MSW recently, which made me think something was still missing. Anyways, we'll see.
const httpHeaders: Array<Buffer> = [] | ||
// Create a `ServerResponse` instance to delegate HTTP message parsing, | ||
// Transfer-Encoding, and other things to Node.js internals. | ||
const serverResponse = new ServerResponse(new IncomingMessage(this)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I passed this
socket instance as the socket of the IncomingMessage
used here. I think it should be more error-proof in case this server response decides to terminate the socket (rightfully), for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used a new socket because I was afraid of infinity loops, but if it makes sense to you, I'm ok with this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't arrive at the infinite loops. This logic is only triggered on respondWith()
, and that's called by the developer. If I spot any issues with this, will revert back to use an empty socket.
* @see https://github.com/nodejs/node/blob/10099bb3f7fd97bb9dd9667188426866b3098e07/test/parallel/test-http-server-response-standalone.js#L32 | ||
*/ | ||
serverResponse.assignSocket( | ||
new MockSocket({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used the MockSocket
we already have instead of Writable
. While sockets extend streams, they implement additional methods on top. I know Node.js uses a simple Writable
in their test but we have to acknowledge that test case is controlled. Our use cases are going to be everything out there. Using an actual socket-compatible instance is crucial here.
// An empty line separating headers from the body. | ||
httpHeaders.push(Buffer.from('\r\n')) | ||
|
||
const flushHeaders = (value?: Uint8Array) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like that we can drop this now. Fantastic!
const { res, text } = await waitForClientRequest(request) | ||
|
||
expect(res.statusCode).toBe(200) | ||
expect(res.headers).toHaveProperty('transfer-encoding', 'chunked') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a few assertions that the client also receives the right response headers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nothing short of fantastic. Great job, @mikicho 👏
Regarding the I've noticed we don't have any tests for strict response header equality either. Perhaps this is the time we added them. @mikicho, what do you think about this? |
From the RFC9110, the Date section:
So the addition of the Now, it's debatable who should be in charge of this. I think it's may be okay for us to leave those headers if they mean better spec compatibility out of the box. It'd even be good, in fact. As long as we are sure that the developer can override (or remove) those headers if they want to. |
ServerResponse
to build the HTTP response string
My opinion on this jumps back and forth every time I think about it. The connection and date headers make sense from the server's perspective. Request listeners are not technically servers. You can emulate one if you want, but you are not required to. Moreover, Let's remove those server headers. Keep the existing behavior, make the mocked response a WYSIWYG. |
Me, too. I think we are a mocking tool and the DX > compatibility. It may be confusing because, as a mocking tool, compatibility IS better than DX (it prevents terrible production errors). |
@mikicho, agreed. Pushed the fix and also adjusted the test for the empty mocked response to assert that the received |
) | ||
serverResponse.statusCode = response.status |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing these server response headers before the developer-sent headers. They can always set these if they wish to.
Released: v0.32.1 🎉This has been released in v0.32.1! Make sure to always update to the latest version ( Predictable release automation by @ossjs/release. |
I still need to give meaningful names to some variables, but it works as expected.
The idea here is that MSW acts as a Node.js server and uses the
ServerResponse
class to build the final chunks that we later push to oursocket
.The only caveat I found is that we are maybe TOO close to the Node.js implementation 😅. For example, Nock's tests found that
ServerResponse
automatically returnsconnection
anddate
headers, which we now add to the response.I think we can remove this default behavior, but I first want to know how you see it.
@kettanaito