Description
- 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.