@@ -858,6 +858,7 @@ bool Http2Session::CanAddStream() {
858
858
}
859
859
860
860
void Http2Session::AddStream (Http2Stream* stream) {
861
+ Debug (this , " Adding stream: %d" , stream->id ());
861
862
CHECK_GE (++statistics_.stream_count , 0 );
862
863
streams_[stream->id ()] = BaseObjectPtr<Http2Stream>(stream);
863
864
size_t size = streams_.size ();
@@ -868,6 +869,7 @@ void Http2Session::AddStream(Http2Stream* stream) {
868
869
869
870
870
871
BaseObjectPtr<Http2Stream> Http2Session::RemoveStream (int32_t id) {
872
+ Debug (this , " Removing stream: %d" , id);
871
873
BaseObjectPtr<Http2Stream> stream;
872
874
if (streams_.empty ())
873
875
return stream;
@@ -1044,6 +1046,7 @@ int Http2Session::OnHeaderCallback(nghttp2_session* handle,
1044
1046
if (!stream) [[unlikely]]
1045
1047
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1046
1048
1049
+ Debug (session, " handling header key/pair for stream %d" , id);
1047
1050
// If the stream has already been destroyed, ignore.
1048
1051
if (!stream->is_destroyed () && !stream->AddHeader (name, value, flags)) {
1049
1052
// This will only happen if the connected peer sends us more
@@ -1113,9 +1116,21 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle,
1113
1116
return 1 ;
1114
1117
}
1115
1118
1116
- // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error
1119
+ // If the error is fatal or if error code is one of the following
1120
+ // we emit and error:
1121
+ //
1122
+ // ERR_STREAM_CLOSED: An invalid frame has been received in a closed stream.
1123
+ //
1124
+ // ERR_PROTO: The RFC 7540 specifies:
1125
+ // "An endpoint that encounters a connection error SHOULD first send a GOAWAY
1126
+ // frame (Section 6.8) with the stream identifier of the last stream that it
1127
+ // successfully received from its peer.
1128
+ // The GOAWAY frame includes an error code that indicates the type of error"
1129
+ // The GOAWAY frame is already sent by nghttp2. We emit the error
1130
+ // to liberate the Http2Session to destroy.
1117
1131
if (nghttp2_is_fatal (lib_error_code) ||
1118
- lib_error_code == NGHTTP2_ERR_STREAM_CLOSED) {
1132
+ lib_error_code == NGHTTP2_ERR_STREAM_CLOSED ||
1133
+ lib_error_code == NGHTTP2_ERR_PROTO) {
1119
1134
Environment* env = session->env ();
1120
1135
Isolate* isolate = env->isolate ();
1121
1136
HandleScope scope (isolate);
@@ -1178,7 +1193,6 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
1178
1193
Debug (session, " frame type %d was not sent, code: %d" ,
1179
1194
frame->hd .type , error_code);
1180
1195
1181
- // Do not report if the frame was not sent due to the session closing
1182
1196
if (error_code == NGHTTP2_ERR_SESSION_CLOSING ||
1183
1197
error_code == NGHTTP2_ERR_STREAM_CLOSED ||
1184
1198
error_code == NGHTTP2_ERR_STREAM_CLOSING) {
@@ -1187,7 +1201,15 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
1187
1201
// to destroy the session completely.
1188
1202
// Further information see: https://github.com/nodejs/node/issues/35233
1189
1203
session->DecrefHeaders (frame);
1190
- return 0 ;
1204
+ // Currently, nghttp2 doesn't not inform us when is the best
1205
+ // time to call session.close(). It relies on a closing connection
1206
+ // from peer. If that doesn't happen, the nghttp2_session will be
1207
+ // closed but the Http2Session will still be up causing a memory leak.
1208
+ // Therefore, if the GOAWAY frame couldn't be send due to
1209
+ // ERR_SESSION_CLOSING we should force close from our side.
1210
+ if (frame->hd .type != 0x03 ) {
1211
+ return 0 ;
1212
+ }
1191
1213
}
1192
1214
1193
1215
Isolate* isolate = env->isolate ();
@@ -1253,12 +1275,15 @@ int Http2Session::OnStreamClose(nghttp2_session* handle,
1253
1275
// ignore these. If this callback was not provided, nghttp2 would handle
1254
1276
// invalid headers strictly and would shut down the stream. We are intentionally
1255
1277
// being more lenient here although we may want to revisit this choice later.
1256
- int Http2Session::OnInvalidHeader (nghttp2_session* session ,
1278
+ int Http2Session::OnInvalidHeader (nghttp2_session* handle ,
1257
1279
const nghttp2_frame* frame,
1258
1280
nghttp2_rcbuf* name,
1259
1281
nghttp2_rcbuf* value,
1260
1282
uint8_t flags,
1261
1283
void * user_data) {
1284
+ Http2Session* session = static_cast <Http2Session*>(user_data);
1285
+ int32_t id = GetFrameID (frame);
1286
+ Debug (session, " invalid header received for stream %d" , id);
1262
1287
// Ignore invalid header fields by default.
1263
1288
return 0 ;
1264
1289
}
@@ -1654,6 +1679,7 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) {
1654
1679
1655
1680
// Called by OnFrameReceived when a complete SETTINGS frame has been received.
1656
1681
void Http2Session::HandleSettingsFrame (const nghttp2_frame* frame) {
1682
+ Debug (this , " handling settings frame" );
1657
1683
bool ack = frame->hd .flags & NGHTTP2_FLAG_ACK;
1658
1684
if (!ack) {
1659
1685
js_fields_->bitfield &= ~(1 << kSessionRemoteSettingsIsUpToDate );
0 commit comments