-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Response object for HTTP Upgrade/CONNECT #3036
Conversation
I have a branch of einaros/ws on top of this, with working tests and examples: https://github.com/stephank/ws/tree/upgrade-response |
More branches of websocket projects:
These are all examples of projects reimplementing basic ServerResponse functionality. I noticed a recurring pattern, along the lines of:
Perhaps we can simply emit |
I went ahead and rebased on current master, and implemented the earlier thought of simply emitting The branches of ws and faye-websocket have also been updated. (Sockjs didn't need any further updates.) |
@isaacs Could you flush the moderation queue for nodejs-dev? (or give me / @bnoordhuis admin rights) |
I think that @stephank's the new API is really nice. However, it breaks backward-compatibility. Because Instead, I think that Node should close the connection when pipelined Upgrade/CONNECT request was detected. Because:
|
Thanks for pitching in! That will, of course, solve the problem as well. You're absolutely right when it comes to the API. I was still hoping we could move mountains by showing the API can be better, and demonstrating that I was willing to put work in projects currently using Is there a minimum set of projects using |
@stephank - What WebSocket's client does pipeline the Upgrade request? |
There isn't any I'm aware of, that test is theoretical. Though it does actually work and triggers the bad HTTP server behavior on 0.6.14 and 0.7.7. I'm in this for the response object, which can be very useful in combination with middleware. What led me to doing this was trying to add Briefly, that pull works, but a lot of middleware really wants to manipulate the response, e.g. add cookies, other headers, sometimes even abort the request (ie. basicAuth wants to reply with 401). |
For reference, linking mailing list thread: |
By the discussion in the ML, we will revisit in v0.9. Reopening. |
I just found this issue. I've been wanting this feature for a long time! What can I do to help? Basically my goal is to be able to handle websockets 100% from a handler function without needing access to the server object. This makes composing layers (like connect middlewares) possible when using websockets. |
|
Rebased. Test results still good. |
I tried to create a test case for the issue with Nagle's. I basically disable Nagle's directly after https://gist.github.com/3426025 The test currently passes. (ie. I only get one Can someone with a bit more knowledge of |
Any progress on this proposal getting into one of 0.9.x unstable releases? The lack of a response object is making it hard for frameworks to implement upgrade request routing easily, meaning that upgrades are typically handed manually with a lot more work required. As to the issue of backwards-compatibility that everyone seems to be hanging on, even with this proposed API break, wouldn't the only change required be something like this or am I missing something?
|
PR no longer applies. Is this still an issue with master and if so, why? |
@adammw: Sure, but API compatibility means things have to stay working without code changes to the libraries/applications using the API. @bnoordhuis: It is still an issue. This is a proposal to change the I'll take a look at another rebase on current master. |
Here we go, rebased against c6e958d. Some things don't sit quite right with me, though:
|
Rebased on current master (8e311d2). Now using Remaining points:
|
Right, with #4488 closed, I rebased on master (926c90b) again. The Remaining:
|
Should @stephank keep working on this? I'd hate to see him do all this work if there is no intention to accept it. This is a feature I would really like, but it is a change to the HTTP API which is stable now. @stephank, is your change backwards incompatible with any existing node code? And if so, how significant is the change? My approach of late has been to replace the http module entirely in userspace. (see |
@creationix Yes, it's an API change to the It's the difference between this server we have right now:
And this server on my branch:
A server that handles both is slightly finicky, though:
|
There's now a package @adammw suggested I look into backwards compatibility, but this is kind of the reverse. I can transform an 'old style' upgrade into a 'new-style' this way, but I've not yet figured out how to make 'old style' work on top of 'new style'. |
@stephank So, it seems like the main blocker is that now you do |
That's worrying in of itself.
My suggestion is using different event name is used in place of upgrade (upgradeEx, upgrade-req, upgrade2, etc.) like is done in the update-ex npm package. Depending on which events have listeners depends on which behaviour is taken within the http module. And since they have different names, this also has the plus of being able to be detected which is being listened to, and allowing different signatures. I really like this proposal, and think it works a lot better than the existing upgrade event behaviour, although I worry that now it is a npm module its inclusion into the nodejs core may be questioned, even though it is an improvement/replacement for existing functionality. And considering the breakage and updates that will be needed from modifications of streams in 0.9/0.10, I think considering this sooner would be better than later. |
Can one of the admins verify this patch? |
Seems like there may now be some motivation to make some changes to upgrade, but in a rather different way at #4813. |
The last push contains a rebase on master (46da8c2), and reworded commit messages. I had to change |
Rebase against current master (b255f4c). I used to have a commit that removed So the commit was no longer necessary, and I'm no longer touching the HTTP client code. I do, however, remove the empty buffer on the HTTP server, considering this is an API change any way. I also removed the commit that fixed |
@stephank What benefit does this change have over the current approach? |
Can't see why jenkins is failing, but I believe these are not failures related to the pull. @bnoordhuis In short: middleware. See also #4813. |
Right. @isaacs, this one's for you. |
There are no known real world clients that actually send an upgrade request pipelined after a regular request. Nonetheless, this tests conformance to the standard.
Introduces `switchProtocols()` on the response, which is an alternate `end()` for responses that hijack the socket. The common part for building a ServerResponse, shared with regular requests, is extracted to a `createResponse()` function.
Rebase against current master (b26d346). You guys are really good at breaking my stuff for years on end. :(
|
@stephank Sorry for the absurd delay on this. I will review this in more detail very soon (today or Monday). The main blocker that I can see right off the bat is that we probably can't get away with changing the Thanks again, and my apologies for letting it linger so long. This is next in my queue after the OutgoingMessage refactoring. |
If my understanding is correct, there is no intention of merging this, and I have no intention of updating it any further. Unless you guys want to keep this issue around for a hypothetical 2.0, you may as well close it. Path ahead seems to be a separate module and getting people to use that. I might at some point in the future revive upgradeEx, but I'm currently otherwise occupied. |
Keeping this around and open for 2+ years after the original poster gave up on updating it isn't helpful. Looks like there's been recent albeit not particularly active discussion on it on the io.js side. Given that it's unlike this specific PR will ever land in v0.x, closing. Can reopen if necessary. |
Backport 1ee712ab8687e5f4dec93d45da068d37d28feb8b from V8 upstream. Original commit message: Add SetAbortOnUncaughtExceptionCallback API The --abort-on-uncaught-exception command line switch makes Isolate::Throw abort if the error being thrown cannot be caught by a try/catch block. Embedders may want to use other mechanisms than try/catch blocks to handle uncaught exceptions. For instance, Node.js has "domain" objects that have error handlers that can handle uncaught exception like following: var d = domain.create(); d.on('error', function onError(err) { console.log('Handling error'); }); d.run(function() { throw new Error("boom"); }); These error handlers are called by isolates' message listeners. If --abort-on-uncaught-exception is *not* used, the isolate's message listener will be called, which will in turn call the domain's error handler. The process will output 'Handling error' and will exit successfully (not due to an uncaught exception). This is the behavior that Node.js users expect. However, if --abort-on-uncaught-exception is used and when throwing an error within a domain that has an error handler, the process will abort and the domain's error handler will not be called. This is not the behavior that Node.js users expect. Having a SetAbortOnUncaughtExceptionCallback API allows embedders to determine when it's not appropriate to abort and instead handle the exception via the isolate's message listener. In the example above, Node.js would set a custom callback with SetAbortOnUncaughtExceptionCallback that would be implemented as following (the sample code has been simplified to remove what's not relevant to this change): bool ShouldAbortOnUncaughtException(Isolate* isolate) { return !IsDomainActive(); } Now when --abort-on-uncaught-exception is used, Isolate::Throw would call that callback and determine that it should not abort if a domain with an error handler is active. Instead, the isolate's message listener would be called and the error would be handled by the domain's error handler. I believe this can also be useful for other embedders. BUG= [email protected] Review URL: https://codereview.chromium.org/1375933003 Cr-Commit-Position: refs/heads/master@{#31111} PR: nodejs#3036 PR-URL: nodejs/node#3036 Reviewed-By: Ben Noordhuis <[email protected]>
Revert 0af4c9e, parts of 921f2de and port nodejs#25835 from v0.12 to master so that node aborts at the right time when an error is thrown and --abort-on-uncaught-exception is used. Fixes nodejs#3035. PR: nodejs#3036 PR-URL: nodejs/node#3036 Reviewed-By: Ben Noordhuis <[email protected]>
Backport 1ee712ab8687e5f4dec93d45da068d37d28feb8b from V8 upstream. Original commit message: Add SetAbortOnUncaughtExceptionCallback API The --abort-on-uncaught-exception command line switch makes Isolate::Throw abort if the error being thrown cannot be caught by a try/catch block. Embedders may want to use other mechanisms than try/catch blocks to handle uncaught exceptions. For instance, Node.js has "domain" objects that have error handlers that can handle uncaught exception like following: var d = domain.create(); d.on('error', function onError(err) { console.log('Handling error'); }); d.run(function() { throw new Error("boom"); }); These error handlers are called by isolates' message listeners. If --abort-on-uncaught-exception is *not* used, the isolate's message listener will be called, which will in turn call the domain's error handler. The process will output 'Handling error' and will exit successfully (not due to an uncaught exception). This is the behavior that Node.js users expect. However, if --abort-on-uncaught-exception is used and when throwing an error within a domain that has an error handler, the process will abort and the domain's error handler will not be called. This is not the behavior that Node.js users expect. Having a SetAbortOnUncaughtExceptionCallback API allows embedders to determine when it's not appropriate to abort and instead handle the exception via the isolate's message listener. In the example above, Node.js would set a custom callback with SetAbortOnUncaughtExceptionCallback that would be implemented as following (the sample code has been simplified to remove what's not relevant to this change): bool ShouldAbortOnUncaughtException(Isolate* isolate) { return !IsDomainActive(); } Now when --abort-on-uncaught-exception is used, Isolate::Throw would call that callback and determine that it should not abort if a domain with an error handler is active. Instead, the isolate's message listener would be called and the error would be handled by the domain's error handler. I believe this can also be useful for other embedders. BUG= [email protected] Review URL: https://codereview.chromium.org/1375933003 Cr-Commit-Position: refs/heads/master@{#31111} PR: nodejs#3036 PR-URL: nodejs/node#3036 Reviewed-By: Ben Noordhuis <[email protected]>
Revert 0af4c9e, parts of 921f2de and port nodejs#25835 from v0.12 to master so that node aborts at the right time when an error is thrown and --abort-on-uncaught-exception is used. Fixes nodejs#3035. PR: nodejs#3036 PR-URL: nodejs/node#3036 Reviewed-By: Ben Noordhuis <[email protected]>
Backport 1ee712ab8687e5f4dec93d45da068d37d28feb8b from V8 upstream. Original commit message: Add SetAbortOnUncaughtExceptionCallback API The --abort-on-uncaught-exception command line switch makes Isolate::Throw abort if the error being thrown cannot be caught by a try/catch block. Embedders may want to use other mechanisms than try/catch blocks to handle uncaught exceptions. For instance, Node.js has "domain" objects that have error handlers that can handle uncaught exception like following: var d = domain.create(); d.on('error', function onError(err) { console.log('Handling error'); }); d.run(function() { throw new Error("boom"); }); These error handlers are called by isolates' message listeners. If --abort-on-uncaught-exception is *not* used, the isolate's message listener will be called, which will in turn call the domain's error handler. The process will output 'Handling error' and will exit successfully (not due to an uncaught exception). This is the behavior that Node.js users expect. However, if --abort-on-uncaught-exception is used and when throwing an error within a domain that has an error handler, the process will abort and the domain's error handler will not be called. This is not the behavior that Node.js users expect. Having a SetAbortOnUncaughtExceptionCallback API allows embedders to determine when it's not appropriate to abort and instead handle the exception via the isolate's message listener. In the example above, Node.js would set a custom callback with SetAbortOnUncaughtExceptionCallback that would be implemented as following (the sample code has been simplified to remove what's not relevant to this change): bool ShouldAbortOnUncaughtException(Isolate* isolate) { return !IsDomainActive(); } Now when --abort-on-uncaught-exception is used, Isolate::Throw would call that callback and determine that it should not abort if a domain with an error handler is active. Instead, the isolate's message listener would be called and the error would be handled by the domain's error handler. I believe this can also be useful for other embedders. BUG= [email protected] Review URL: https://codereview.chromium.org/1375933003 Cr-Commit-Position: refs/heads/master@{#31111} Ref: nodejs#3036 PR-URL: nodejs/node#3481 Reviewed-By: targos - Michaël Zasso <[email protected]>
I started a thread on nodejs-dev about this, but it looks like my posts are still in the moderator queue. Will link it here once it gets through.
Briefly:
upgrade
handler gets immediate access to the socket, while other requests may still be processing async.Regarding
make test
, I have one failing test both before and after this patch set, namelysimple/test-http-full-response
. I consider this a platform irk.make test-debug
plain crashes the test suite onsimple/test-repl
(also before and after), but a fair amount of HTTP tests do pass.