Skip to content
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

Feat: Added response.destroy method #1675

Merged
merged 4 commits into from Aug 20, 2019
Merged

Feat: Added response.destroy method #1675

merged 4 commits into from Aug 20, 2019

Conversation

b4dnewz
Copy link
Contributor

@b4dnewz b4dnewz commented Aug 15, 2019

It adds the destroy method to the response to manually terminate a connection and optionally raise an error captured from listeners bound to request error event.

As discussed in #1669


I'm still not sure if this is enough, I took a look at http_incoming from node http core:

IncomingMessage.prototype.destroy = function destroy(error) {
  if (this.socket)
    this.socket.destroy(error);
};

And from http_client also from node http core:

if (ret instanceof Error) {
    prepareError(ret, parser, d);
    debug('parse error', ret);
    freeParser(parser, req, socket);
    socket.destroy();
    req.socket._hadError = true;
    req.emit('error', ret);
} ...

And based on the description provided in the documentation:

https://nodejs.org/api/http.html#http_class_http_incomingmessage

It will raise the error event only if called with an actual Error instance.

But since I'm not familiar with node http core lib I'm not sure if this will terminate the connection properly or is required to manually perform other tasks on the request object.

Calls destroy() on the socket that received the IncomingMessage. If error is provided, an 'error' event is emitted and error is passed as an argument to any listeners on the event.
@paulmelnikow
Copy link
Member

Thanks for digging into this! 💯

A couple follow-up thoughts:

… I took a look at http_incoming from node http core:

IncomingMessage.prototype.destroy = function destroy(error) {
  if (this.socket)
    this.socket.destroy(error);
};

This is a bit curious given the description in the documentation. Does socket.destroy() somehow trigger the behavior of emitting the error event on the request?

And from http_client also from node http core:

if (ret instanceof Error) {
    prepareError(ret, parser, d);
    debug('parse error', ret);
    freeParser(parser, req, socket);
    socket.destroy();
    req.socket._hadError = true;
    req.emit('error', ret);
} ...

Regarding this piece of code – I'm not sure – is it relevant to req.destroy()?

@b4dnewz
Copy link
Contributor Author

b4dnewz commented Aug 15, 2019

Regarding this piece of code – I'm not sure – is it relevant to req.destroy()?

I'm not sure either, but might be the part that raise the error event on the request, I could not find any other relevant look-a-like code in http_client file.

Does socket.destroy() somehow trigger the behavior of emitting the error event on the request?

I really don't know, from what I see in the http_client.js core file look it won't.. but I didn't test it without, I mostly looked at the implementation of abort on request_overrider.js file from nock and used the emitError method.

Also maybe we should check for the socket existence before calling the destroy method to avoid calling on an already destroyed socket as said in the description (I cutted out) from the first file:

It's possible that the socket will be destroyed, and removed from
any messages, before ever calling this. In that case, just skip
it, since something else is destroying this connection anyway.

Fix the failing lint tests
@mastermatt
Copy link
Member

I don't think overriding the destroy method on the response is the right approach. The response is an actual instance of IncomingMessage, which means it already has the correct method available.

The bit that's lacking is our Socket surrogate class. Comparing the current implementation vs the docs, shows that it doesn't handle being passed an error correctly.

If exception is specified, an 'error' event will be emitted and any listeners for that event will receive exception as an argument.

nock/lib/socket.js

Lines 57 to 60 in 4d0fbbf

destroy() {
this.destroyed = true
this.readable = this.writable = false
}

If you're curious what Node does normally, the this.socket.destroy(error); line in IncomingMessage.destroy kicks off this function.

Updating the destroy method in our Socket class to something like the following should get us close enough to the actual logic.

  destroy(err) {
    this.destroyed = true
    this.readable = this.writable = false
    if (err) {
      this.emit('error', err)
    }
    return this
  }

The one other thing that would need to change would be to update this line so that error events also propagate through the request.

propagate(['timeout'], req.socket, req)

This is where the propagation happens in Node. It probably doesn't make sense to add the other missing events at this time. Wait to see if there is an actual need.

With those two changes, the tests @b4dnewz wrote for this PR passed for me locally.

@b4dnewz
Copy link
Contributor Author

b4dnewz commented Aug 16, 2019

I don't think overriding the destroy method on the response is the right approach.

I also initially had some doubt about it, your observations are good and a cleaner way to do it, I will update the code with your suggestions.

This reverts commit b68e87b.
This add suggested code to socket class on destroy method
if (err) {
this.emit('error', err)
}
return this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is return this needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the documented behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see it now. Thanks.

@nockbot
Copy link
Collaborator

nockbot commented Aug 20, 2019

🎉 This PR is included in version 11.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

gr2m pushed a commit that referenced this pull request Sep 4, 2019
Calling `destroy` on the the response, with an error, not correctly propagates through the socket and then the request object. This aligns with Node's native functionality. 

Fixes: #1669
gr2m pushed a commit that referenced this pull request Sep 4, 2019
Calling `destroy` on the the response, with an error, not correctly propagates through the socket and then the request object. This aligns with Node's native functionality. 

Fixes: #1669
@nockbot
Copy link
Collaborator

nockbot commented Sep 4, 2019

🎉 This PR is included in version 11.2.0 🎉

The release is available on npm package (@latest dist-tag)

Your semantic-release bot 📦🚀

gr2m pushed a commit that referenced this pull request Sep 5, 2019
Calling `destroy` on the the response, with an error, not correctly propagates through the socket and then the request object. This aligns with Node's native functionality.

Fixes: #1669
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants