@@ -857,6 +857,7 @@ bool Http2Session::CanAddStream() {
857857}
858858
859859void Http2Session::AddStream (Http2Stream* stream) {
860+ Debug (this , " Adding stream: %d" , stream->id ());
860861 CHECK_GE (++statistics_.stream_count , 0 );
861862 streams_[stream->id ()] = BaseObjectPtr<Http2Stream>(stream);
862863 size_t size = streams_.size ();
@@ -867,6 +868,7 @@ void Http2Session::AddStream(Http2Stream* stream) {
867868
868869
869870BaseObjectPtr<Http2Stream> Http2Session::RemoveStream (int32_t id) {
871+ Debug (this , " Removing stream: %d" , id);
870872 BaseObjectPtr<Http2Stream> stream;
871873 if (streams_.empty ())
872874 return stream;
@@ -1043,6 +1045,7 @@ int Http2Session::OnHeaderCallback(nghttp2_session* handle,
10431045 if (!stream) [[unlikely]]
10441046 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE ;
10451047
1048+ Debug (session, " handling header key/pair for stream %d" , id);
10461049 // If the stream has already been destroyed, ignore.
10471050 if (!stream->is_destroyed () && !stream->AddHeader (name, value, flags)) {
10481051 // This will only happen if the connected peer sends us more
@@ -1112,9 +1115,21 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle,
11121115 return 1 ;
11131116 }
11141117
1115- // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error
1118+ // If the error is fatal or if error code is one of the following
1119+ // we emit and error:
1120+ //
1121+ // ERR_STREAM_CLOSED: An invalid frame has been received in a closed stream.
1122+ //
1123+ // ERR_PROTO: The RFC 7540 specifies:
1124+ // "An endpoint that encounters a connection error SHOULD first send a GOAWAY
1125+ // frame (Section 6.8) with the stream identifier of the last stream that it
1126+ // successfully received from its peer.
1127+ // The GOAWAY frame includes an error code that indicates the type of error"
1128+ // The GOAWAY frame is already sent by nghttp2. We emit the error
1129+ // to liberate the Http2Session to destroy.
11161130 if (nghttp2_is_fatal (lib_error_code) ||
1117- lib_error_code == NGHTTP2_ERR_STREAM_CLOSED ) {
1131+ lib_error_code == NGHTTP2_ERR_STREAM_CLOSED ||
1132+ lib_error_code == NGHTTP2_ERR_PROTO ) {
11181133 Environment* env = session->env ();
11191134 Isolate* isolate = env->isolate ();
11201135 HandleScope scope (isolate);
@@ -1177,7 +1192,6 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
11771192 Debug (session, " frame type %d was not sent, code: %d" ,
11781193 frame->hd .type , error_code);
11791194
1180- // Do not report if the frame was not sent due to the session closing
11811195 if (error_code == NGHTTP2_ERR_SESSION_CLOSING ||
11821196 error_code == NGHTTP2_ERR_STREAM_CLOSED ||
11831197 error_code == NGHTTP2_ERR_STREAM_CLOSING ) {
@@ -1186,7 +1200,15 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
11861200 // to destroy the session completely.
11871201 // Further information see: https://github.com/nodejs/node/issues/35233
11881202 session->DecrefHeaders (frame);
1189- return 0 ;
1203+ // Currently, nghttp2 doesn't not inform us when is the best
1204+ // time to call session.close(). It relies on a closing connection
1205+ // from peer. If that doesn't happen, the nghttp2_session will be
1206+ // closed but the Http2Session will still be up causing a memory leak.
1207+ // Therefore, if the GOAWAY frame couldn't be send due to
1208+ // ERR_SESSION_CLOSING we should force close from our side.
1209+ if (frame->hd .type != 0x03 ) {
1210+ return 0 ;
1211+ }
11901212 }
11911213
11921214 Isolate* isolate = env->isolate ();
@@ -1252,12 +1274,15 @@ int Http2Session::OnStreamClose(nghttp2_session* handle,
12521274// ignore these. If this callback was not provided, nghttp2 would handle
12531275// invalid headers strictly and would shut down the stream. We are intentionally
12541276// being more lenient here although we may want to revisit this choice later.
1255- int Http2Session::OnInvalidHeader (nghttp2_session* session ,
1277+ int Http2Session::OnInvalidHeader (nghttp2_session* handle ,
12561278 const nghttp2_frame* frame,
12571279 nghttp2_rcbuf* name,
12581280 nghttp2_rcbuf* value,
12591281 uint8_t flags,
12601282 void * user_data) {
1283+ Http2Session* session = static_cast <Http2Session*>(user_data);
1284+ int32_t id = GetFrameID (frame);
1285+ Debug (session, " invalid header received for stream %d" , id);
12611286 // Ignore invalid header fields by default.
12621287 return 0 ;
12631288}
@@ -1653,6 +1678,7 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) {
16531678
16541679// Called by OnFrameReceived when a complete SETTINGS frame has been received.
16551680void Http2Session::HandleSettingsFrame (const nghttp2_frame* frame) {
1681+ Debug (this , " handling settings frame" );
16561682 bool ack = frame->hd .flags & NGHTTP2_FLAG_ACK ;
16571683 if (!ack) {
16581684 js_fields_->bitfield &= ~(1 << kSessionRemoteSettingsIsUpToDate );
0 commit comments