Skip to content

Commit

Permalink
http2: close stream and session on frameError
Browse files Browse the repository at this point in the history
PR-URL: #42147
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
  • Loading branch information
RafaelGSS authored and sxa committed Mar 7, 2022
1 parent ce40927 commit 9bc7a95
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 3 deletions.
2 changes: 2 additions & 0 deletions lib/internal/http2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,8 @@ function onFrameError(id, type, code) {
const emitter = session[kState].streams.get(id) || session;
emitter[kUpdateTimer]();
emitter.emit('frameError', type, code, id);
session[kState].streams.get(id).close(code);
session.close();
}

function onAltSvc(stream, origin, alt) {
Expand Down
23 changes: 22 additions & 1 deletion src/node_http2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,27 @@ void Http2Session::DecrefHeaders(const nghttp2_frame* frame) {
}
}

uint32_t TranslateNghttp2ErrorCode(const int libErrorCode) {
switch (libErrorCode) {
case NGHTTP2_ERR_STREAM_CLOSED:
return NGHTTP2_STREAM_CLOSED;
case NGHTTP2_ERR_HEADER_COMP:
return NGHTTP2_COMPRESSION_ERROR;
case NGHTTP2_ERR_FRAME_SIZE_ERROR:
return NGHTTP2_FRAME_SIZE_ERROR;
case NGHTTP2_ERR_FLOW_CONTROL:
return NGHTTP2_FLOW_CONTROL_ERROR;
case NGHTTP2_ERR_REFUSED_STREAM:
return NGHTTP2_REFUSED_STREAM;
case NGHTTP2_ERR_PROTO:
case NGHTTP2_ERR_HTTP_HEADER:
case NGHTTP2_ERR_HTTP_MESSAGING:
return NGHTTP2_PROTOCOL_ERROR;
default:
return NGHTTP2_INTERNAL_ERROR;
}
}

// If nghttp2 is unable to send a queued up frame, it will call this callback
// to let us know. If the failure occurred because we are in the process of
// closing down the session or stream, we go ahead and ignore it. We don't
Expand Down Expand Up @@ -1060,7 +1081,7 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
Local<Value> argv[3] = {
Integer::New(isolate, frame->hd.stream_id),
Integer::New(isolate, frame->hd.type),
Integer::New(isolate, error_code)
Integer::New(isolate, TranslateNghttp2ErrorCode(error_code))
};
session->MakeCallback(
env->http2session_on_frame_error_function(),
Expand Down
51 changes: 51 additions & 0 deletions test/parallel/test-http2-exceeds-server-trailer-size.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');

const assert = require('assert');
const { createServer, constants, connect } = require('http2');

const server = createServer();

server.on('stream', (stream, headers) => {
stream.respond(undefined, { waitForTrailers: true });

stream.on('data', common.mustNotCall());

stream.on('wantTrailers', common.mustCall(() => {
// Trigger a frame error by sending a trailer that is too large
stream.sendTrailers({ 'test-trailer': 'X'.repeat(64 * 1024) });
}));

stream.on('frameError', common.mustCall((frameType, errorCode) => {
assert.strictEqual(errorCode, constants.NGHTTP2_FRAME_SIZE_ERROR);
}));

stream.on('error', common.expectsError({
code: 'ERR_HTTP2_STREAM_ERROR',
}));

stream.on('close', common.mustCall());

stream.end();
});

server.listen(0, () => {
const clientSession = connect(`http://localhost:${server.address().port}`);

clientSession.on('frameError', common.mustNotCall());
clientSession.on('close', common.mustCall(() => {
server.close();
}));

const clientStream = clientSession.request();

clientStream.on('close', common.mustCall());
// These events mustn't be called once the frame size error is from the server
clientStream.on('frameError', common.mustNotCall());
clientStream.on('error', common.mustNotCall());

clientStream.end();
});
4 changes: 2 additions & 2 deletions test/parallel/test-http2-options-max-headers-block-length.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ server.listen(0, common.mustCall(() => {
}));

req.on('frameError', common.mustCall((type, code) => {
assert.strictEqual(code, h2.constants.NGHTTP2_ERR_FRAME_SIZE_ERROR);
assert.strictEqual(code, h2.constants.NGHTTP2_FRAME_SIZE_ERROR);
}));

req.on('error', common.expectsError({
code: 'ERR_HTTP2_STREAM_ERROR',
name: 'Error',
message: 'Stream closed with error code NGHTTP2_REFUSED_STREAM'
message: 'Stream closed with error code NGHTTP2_FRAME_SIZE_ERROR'
}));
}));

0 comments on commit 9bc7a95

Please sign in to comment.