Skip to content

http2: The http2 server does not quickly close the stream after a frame error if a 'data' event handler is set #35133

Description

@murgatroid99

What steps will reproduce the bug?

consthttp2=require('http2');constserver=http2.createServer();consttrailerValue='X'.repeat(64*1024);server.on('stream',(stream,headers)=>{stream.respond(undefined,{waitForTrailers: true});// This line is necessary to trigger the bugstream.on('data',()=>{});stream.on('wantTrailers',()=>{console.log(`${newDate().toISOString()} Server sending trailers`);// Trigger a frame error by sending a trailer that is too largestream.sendTrailers({'test-trailer': trailerValue});});stream.on('frameError',(frameType,errorCode)=>{console.log(`${newDate().toISOString()} Server frameError frameType: ${frameType}, errorCode: ${errorCode}`);});stream.on('close',()=>{console.log(`${newDate().toISOString()} Server stream close event`);});stream.end();});server.listen(3000,()=>{constclientSession=http2.connect('http://localhost:3000');clientSession.on('frameError',(type,errorCode,streamId)=>{console.log(`${newDate().toISOString()} Client session frameError frameType: ${type}, errorCode: ${errorCode}, streamId: ${streamId}`);});clientSession.on('close',()=>{console.log(`${newDate().toISOString()} Client session close event`);server.close();});constclientStream=clientSession.request();console.log(`${newDate().toISOString()} Starting client stream`);clientStream.on('close',()=>{console.log(`${newDate().toISOString()} Client stream close event`);});clientStream.on('frameError',(type,errorCode,streamId)=>{console.log(`${newDate().toISOString()} Client stream frameError frameType: ${type}, errorCode: ${errorCode}, streamId: ${streamId}`);});// This should cause the server to finish readingclientStream.end();})

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

This reproduction is 100% consistent.

What is the expected behavior?

From the Http2Stream#frameError event documentation:

The 'frameError' event is emitted when an error occurs while attempting to send a frame. When invoked, the handler function will receive an integer argument identifying the frame type, and an integer argument identifying the error code. The Http2Stream instance will be destroyed immediately after the 'frameError' event is emitted.

Also, from the http2.createServer options documentation:

maxSendHeaderBlockLength<number> Sets the maximum allowed size for a serialized, compressed block of headers. Attempts to send headers that exceed this limit will result in a 'frameError' event being emitted and the stream being closed and destroyed.

Both the client and the server should see their corresponding stream object's close event soon after the 'frameError' event is emitted

What do you see instead?

On Node 10 and 12, there is a 2 minute delay between the frameError event and the close events. On Node 14, the close events are never emitted.

Node 10 and 12

2020-09-09T20:46:02.215Z Starting client stream
2020-09-09T20:46:02.227Z Server sending trailers
2020-09-09T20:46:02.228Z Server frameError frameType: 1, errorCode: -522
<2 minute delay>
2020-09-09T20:48:02.231Z Server stream close event
2020-09-09T20:48:02.233Z Client session close event
2020-09-09T20:48:02.234Z Client stream close event

Node 14

2020-09-09T20:37:45.101Z Starting client stream
2020-09-09T20:37:45.110Z Server sending trailers
2020-09-09T20:37:45.111Z Server frameError frameType: 1, errorCode: -522
<No further log lines after at least 10 minutes>

Additional information

If the data event handler is not set on the server's stream, the close event is emitted immediately on all tested Node versions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    http2Issues or PRs related to the http2 subsystem.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions