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

HTTP/2 server does not accept injected 'connection' events for sockets with peeked data #34532

Closed
pimterry opened this issue Jul 27, 2020 · 2 comments · Fixed by #41185
Closed
Labels
http2 Issues or PRs related to the http2 subsystem.

Comments

@pimterry
Copy link
Member

  • Version: probably all (at least 8.4.0, 10.21.0 & 12.18.3)
  • Platform: all
  • Subsystem: HTTP/2

What steps will reproduce the bug?

Start a server like so:

const net = require('net');
const http = require('http');
const http2 = require('http2');
const Wrapper = require('_stream_wrap');

const rawConnListener = (socket) => {
    const data = socket.read(3);

    if (!data) { // Repeat until data is available
        socket.once('readable', () => rawConnListener(socket));
        return;
    }

    // Put the data back, so the real server can handle it:
    socket.unshift(data);

    if (data.toString('ascii') === 'PRI') { // Very dumb preface check
        console.log('emitting h2');
        h2Server.emit('connection', socket);
    } else {
        console.log('emitting h1');
        h1Server.emit('connection', socket);
    }
}

const rawServer = net.createServer(rawConnListener);

const h1Server = http.createServer(() => {
    console.log('Got h1 request');
});

const h2Server = http2.createServer(async () => {
    console.log('Got h2 request');
});

rawServer.listen(8080);

Make a direct HTTP/2 request to it:

curl --http2-prior-knowledge http://localhost:8080

Curl will fail ('Error in the HTTP2 framing layer'), and the server will print 'emitting h2' but never 'Got h2 request'.

How often does it reproduce? Is there a required condition?

Happens every time.

What is the expected behavior?

The server should accept emitted 'connection' events for any Duplex stream, including existing sockets, as discussed in #34296 (comment):

HTTP/2 should support h2Server.emit('connection', socket), just like HTTP/1 does. I think the main problem here is the unshifted data? That’s something that we can (and should) take care of, yes.

This does work correctly for HTTP/1 servers, and HTTP/1 requests using the code above, and is documented as a supported use case. This isn't currently documented as a supported API for HTTP/2, but there's a PR open for that: #34531.

What do you see instead?

HTTP/2 connections are never successfully established for sockets emitted like this.

@lpinca lpinca added the http2 Issues or PRs related to the http2 subsystem. label Aug 9, 2020
addaleax added a commit to addaleax/node that referenced this issue Oct 6, 2020
If there is data stored in the socket, create a `JSStreamSocket`
wrapper so that no data is lost when passing the socket to the
C++ layer (which is unaware of data buffered in JS land).

Refs: nodejs#34532
addaleax added a commit to addaleax/node that referenced this issue Oct 6, 2020
If there is data stored in the socket, create a `JSStreamSocket`
wrapper so that no data is lost when passing the socket to the
C++ layer (which is unaware of data buffered in JS land).

Fixes: nodejs#34532
@leandro-manifesto
Copy link

The documentation was updated but it still doesn't work.

@pimterry
Copy link
Member Author

pimterry commented Aug 31, 2021

Just added a note in #34296 (comment) on this. To reiterate here: this is still an issue as of Node v16.8.0 (EDIT: still an issue today in v17.2.0).

I've also just found another use case: the Docker Engine BuildKit API.

This API does some fascinating & funky things to create an HTTP/2 connection. Because of that, connecting to this API always requires manually emitting a connection event to an HTTP/2 server from a raw socket after an HTTP/1.1 upgrade. Using the normal Node HTTP APIs, that means some data (the HTTP/2 preamble) has often already been read into the head buffer parameter, and so must be unshifted back into the socket before you can create the HTTP/2 connection.

This fails just like the example above, making the BuildKit API unusable in Node right now unless you manually use _stream_wrap. Fortunately it's not a properly documented or widely used API, since it's only just become stable, but it's likely to get more popular in future and it'd be very useful to be able to work with this API from Node.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http2 Issues or PRs related to the http2 subsystem.
Projects
None yet
3 participants