@@ -750,6 +750,7 @@ bool Http2Session::CanAddStream() {
750
750
}
751
751
752
752
void Http2Session::AddStream (Http2Stream* stream) {
753
+ Debug (this , " Adding stream: %d" , stream->id ());
753
754
CHECK_GE (++statistics_.stream_count , 0 );
754
755
streams_[stream->id ()] = BaseObjectPtr<Http2Stream>(stream);
755
756
size_t size = streams_.size ();
@@ -760,6 +761,7 @@ void Http2Session::AddStream(Http2Stream* stream) {
760
761
761
762
762
763
BaseObjectPtr<Http2Stream> Http2Session::RemoveStream (int32_t id) {
764
+ Debug (this , " Removing stream: %d" , id);
763
765
BaseObjectPtr<Http2Stream> stream;
764
766
if (streams_.empty ())
765
767
return stream;
@@ -936,6 +938,7 @@ int Http2Session::OnHeaderCallback(nghttp2_session* handle,
936
938
if (UNLIKELY (!stream))
937
939
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
938
940
941
+ Debug (session, " handling header key/pair for stream %d" , id);
939
942
// If the stream has already been destroyed, ignore.
940
943
if (!stream->is_destroyed () && !stream->AddHeader (name, value, flags)) {
941
944
// This will only happen if the connected peer sends us more
@@ -1005,9 +1008,21 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle,
1005
1008
return 1 ;
1006
1009
}
1007
1010
1008
- // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error
1011
+ // If the error is fatal or if error code is one of the following
1012
+ // we emit and error:
1013
+ //
1014
+ // ERR_STREAM_CLOSED: An invalid frame has been received in a closed stream.
1015
+ //
1016
+ // ERR_PROTO: The RFC 7540 specifies:
1017
+ // "An endpoint that encounters a connection error SHOULD first send a GOAWAY
1018
+ // frame (Section 6.8) with the stream identifier of the last stream that it
1019
+ // successfully received from its peer.
1020
+ // The GOAWAY frame includes an error code that indicates the type of error"
1021
+ // The GOAWAY frame is already sent by nghttp2. We emit the error
1022
+ // to liberate the Http2Session to destroy.
1009
1023
if (nghttp2_is_fatal (lib_error_code) ||
1010
- lib_error_code == NGHTTP2_ERR_STREAM_CLOSED) {
1024
+ lib_error_code == NGHTTP2_ERR_STREAM_CLOSED ||
1025
+ lib_error_code == NGHTTP2_ERR_PROTO) {
1011
1026
Environment* env = session->env ();
1012
1027
Isolate* isolate = env->isolate ();
1013
1028
HandleScope scope (isolate);
@@ -1070,7 +1085,6 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
1070
1085
Debug (session, " frame type %d was not sent, code: %d" ,
1071
1086
frame->hd .type , error_code);
1072
1087
1073
- // Do not report if the frame was not sent due to the session closing
1074
1088
if (error_code == NGHTTP2_ERR_SESSION_CLOSING ||
1075
1089
error_code == NGHTTP2_ERR_STREAM_CLOSED ||
1076
1090
error_code == NGHTTP2_ERR_STREAM_CLOSING) {
@@ -1079,7 +1093,15 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle,
1079
1093
// to destroy the session completely.
1080
1094
// Further information see: https://github.com/nodejs/node/issues/35233
1081
1095
session->DecrefHeaders (frame);
1082
- return 0 ;
1096
+ // Currently, nghttp2 doesn't not inform us when is the best
1097
+ // time to call session.close(). It relies on a closing connection
1098
+ // from peer. If that doesn't happen, the nghttp2_session will be
1099
+ // closed but the Http2Session will still be up causing a memory leak.
1100
+ // Therefore, if the GOAWAY frame couldn't be send due to
1101
+ // ERR_SESSION_CLOSING we should force close from our side.
1102
+ if (frame->hd .type != 0x03 ) {
1103
+ return 0 ;
1104
+ }
1083
1105
}
1084
1106
1085
1107
Isolate* isolate = env->isolate ();
@@ -1145,12 +1167,15 @@ int Http2Session::OnStreamClose(nghttp2_session* handle,
1145
1167
// ignore these. If this callback was not provided, nghttp2 would handle
1146
1168
// invalid headers strictly and would shut down the stream. We are intentionally
1147
1169
// being more lenient here although we may want to revisit this choice later.
1148
- int Http2Session::OnInvalidHeader (nghttp2_session* session ,
1170
+ int Http2Session::OnInvalidHeader (nghttp2_session* handle ,
1149
1171
const nghttp2_frame* frame,
1150
1172
nghttp2_rcbuf* name,
1151
1173
nghttp2_rcbuf* value,
1152
1174
uint8_t flags,
1153
1175
void * user_data) {
1176
+ Http2Session* session = static_cast <Http2Session*>(user_data);
1177
+ int32_t id = GetFrameID (frame);
1178
+ Debug (session, " invalid header received for stream %d" , id);
1154
1179
// Ignore invalid header fields by default.
1155
1180
return 0 ;
1156
1181
}
@@ -1544,6 +1569,7 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) {
1544
1569
1545
1570
// Called by OnFrameReceived when a complete SETTINGS frame has been received.
1546
1571
void Http2Session::HandleSettingsFrame (const nghttp2_frame* frame) {
1572
+ Debug (this , " handling settings frame" );
1547
1573
bool ack = frame->hd .flags & NGHTTP2_FLAG_ACK;
1548
1574
if (!ack) {
1549
1575
js_fields_->bitfield &= ~(1 << kSessionRemoteSettingsIsUpToDate );
0 commit comments