You mention some valid points, which I believe I tackled in the branch, at the 
cost of breaking API. I'm just going to explain how that works, but I currently 
have no way of doing this _without_ breaking API.

On Friday 13 April 2012 at 00:38, Brian wrote:

> That's probably still not enough, because you need to be able to push data 
> out the socket immediately after the actual HTTP response, ideally even in 
> the same TCP frame if possible. So calling response.end() doesn't make sense, 
> and letting that stuff be transparently queued doesn't make sense, because 
> you still have immediate access to the raw socket and could inject websocket 
> data before the HTTP 101 response has been written.

The idea is to not get access to the socket until after the response finishes 
writing. On the branch, I solved this by having an alternate `end` taking a 
callback that fired roughly after the ServerResponse `finish` event and 
`res.detachSocket()`:

    server.on('upgrade', function(req, res) {
      res.writeHead(101, { 'Connection': 'Upgrade', 'Upgrade': 'Foobar' });
      res.switchProtocols(function(socket) {
        // write data
      });
    });

I should've verified this with a network capture, but I believe any data 
immediately sent from that inner callback is also tacked onto the same TCP 
frame. I assumed this because some of Node's tests on this actually test frame 
by frame, and they're not failing. Writes from ServerResponse and the user 
callback to `switchProtocols` happen within the same tick, I believe.
> The main thing you need is to wait until all previous pending requests have 
> been fulfilled before emitting the 'upgrade' event.

This can be done, and can be done right now, if we really want to prevent 
people from talking on the socket before other responses are written.

Though, there's not a client out there that actually pipelines requests; the 
test case I made for this was purely theoretical. Koichi's idea on this was to 
simply close the socket if someone tried. But in the end, none of it went 
through because there's no real world issue.
> The only major remaining issue is that node assumes that the 'upgrade' 
> handler is going to take over the connection and there's no way for that 
> handler to respond with, say, HTTP 404 and continue using the HTTP/1.1 
> keep-alive connection for further HTTP requests.

Definitely, but you don't want to continue using the connection, because it is 
no longer a keep-alive connection after the upgrade request. For the same 
reason we send inner-protocol stuff in a single frame, the client may have done 
so as well.
>  
> Maybe just provide the ServerResponse object to to the 'upgrade' event 
> handler the upgrade event handler would ONLY use it if it decided NOT to 
> follow through with the HTTP Upgrade request? i.e. res.writeHead(101) would 
> be illegal. But your race condition is solved by waiting until all prior http 
> requests have been responded to before emitting the 'upgrade' event.

Then, where is the 101 response written? And how does that code decide if the 
callback is going to cancel, before the callback is called? (Once it _is_ 
called, your socket is gone.)

The race condition is theoretical, and doesn't appear anywhere in practice. 
Perhaps I emphasized it too much. I'm in it for the response object. :)

--  
Stéphan Kochen
Two Screen, Angry Bytes


Reply via email to