@@ -32,6 +32,7 @@ using v8::Integer;
3232using v8::Isolate;
3333using v8::Local;
3434using v8::MaybeLocal;
35+ using v8::NewStringType;
3536using v8::Number;
3637using v8::Object;
3738using v8::ObjectTemplate;
@@ -732,7 +733,7 @@ ssize_t Http2Session::OnMaxFrameSizePadding(size_t frameLen,
732733// various callback functions. Each of these will typically result in a call
733734// out to JavaScript so this particular function is rather hot and can be
734735// quite expensive. This is a potential performance optimization target later.
735- ssize_t Http2Session::ConsumeHTTP2Data () {
736+ void Http2Session::ConsumeHTTP2Data () {
736737 CHECK_NOT_NULL (stream_buf_.base );
737738 CHECK_LE (stream_buf_offset_, stream_buf_.len );
738739 size_t read_len = stream_buf_.len - stream_buf_offset_;
@@ -742,12 +743,14 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
742743 read_len,
743744 nghttp2_session_want_read (session_.get ()));
744745 set_receive_paused (false );
746+ custom_recv_error_code_ = nullptr ;
745747 ssize_t ret =
746748 nghttp2_session_mem_recv (session_.get (),
747749 reinterpret_cast <uint8_t *>(stream_buf_.base ) +
748750 stream_buf_offset_,
749751 read_len);
750752 CHECK_NE (ret, NGHTTP2_ERR_NOMEM);
753+ CHECK_IMPLIES (custom_recv_error_code_ != nullptr , ret < 0 );
751754
752755 if (is_receive_paused ()) {
753756 CHECK (is_reading_stopped ());
@@ -759,7 +762,7 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
759762 // Even if all bytes were received, a paused stream may delay the
760763 // nghttp2_on_frame_recv_callback which may have an END_STREAM flag.
761764 stream_buf_offset_ += ret;
762- return ret ;
765+ goto done ;
763766 }
764767
765768 // We are done processing the current input chunk.
@@ -769,14 +772,34 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
769772 stream_buf_allocation_.clear ();
770773 stream_buf_ = uv_buf_init (nullptr , 0 );
771774
772- if (ret < 0 )
773- return ret;
774-
775775 // Send any data that was queued up while processing the received data.
776- if (!is_destroyed ()) {
776+ if (ret >= 0 && !is_destroyed ()) {
777777 SendPendingData ();
778778 }
779- return ret;
779+
780+ done:
781+ if (UNLIKELY (ret < 0 )) {
782+ Isolate* isolate = env ()->isolate ();
783+ Debug (this ,
784+ " fatal error receiving data: %d (%s)" ,
785+ ret,
786+ custom_recv_error_code_ != nullptr ?
787+ custom_recv_error_code_ : " (no custom error code)" );
788+ Local<Value> args[] = {
789+ Integer::New (isolate, static_cast <int32_t >(ret)),
790+ Null (isolate)
791+ };
792+ if (custom_recv_error_code_ != nullptr ) {
793+ args[1 ] = String::NewFromUtf8 (
794+ isolate,
795+ custom_recv_error_code_,
796+ NewStringType::kInternalized ).ToLocalChecked ();
797+ }
798+ MakeCallback (
799+ env ()->http2session_on_error_function (),
800+ arraysize (args),
801+ args);
802+ }
780803}
781804
782805
@@ -900,14 +923,17 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle,
900923 int lib_error_code,
901924 void * user_data) {
902925 Http2Session* session = static_cast <Http2Session*>(user_data);
926+ const uint32_t max_invalid_frames = session->js_fields_ ->max_invalid_frames ;
903927
904928 Debug (session,
905929 " invalid frame received (%u/%u), code: %d" ,
906930 session->invalid_frame_count_ ,
907- session-> js_fields_ -> max_invalid_frames ,
931+ max_invalid_frames,
908932 lib_error_code);
909- if (session->invalid_frame_count_ ++ > session->js_fields_ ->max_invalid_frames )
933+ if (session->invalid_frame_count_ ++ > max_invalid_frames) {
934+ session->custom_recv_error_code_ = " ERR_HTTP2_TOO_MANY_INVALID_FRAMES" ;
910935 return 1 ;
936+ }
911937
912938 // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error
913939 if (nghttp2_is_fatal (lib_error_code) ||
@@ -1286,6 +1312,7 @@ int Http2Session::HandleDataFrame(const nghttp2_frame* frame) {
12861312 stream->EmitRead (UV_EOF);
12871313 } else if (frame->hd .length == 0 ) {
12881314 if (invalid_frame_count_++ > js_fields_->max_invalid_frames ) {
1315+ custom_recv_error_code_ = " ERR_HTTP2_TOO_MANY_INVALID_FRAMES" ;
12891316 Debug (this , " rejecting empty-frame-without-END_STREAM flood\n " );
12901317 // Consider a flood of 0-length frames without END_STREAM an error.
12911318 return 1 ;
@@ -1470,7 +1497,7 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) {
14701497 ConsumeHTTP2Data ();
14711498 }
14721499
1473- if (!is_write_scheduled ()) {
1500+ if (!is_write_scheduled () && ! is_destroyed () ) {
14741501 // Schedule a new write if nghttp2 wants to send data.
14751502 MaybeScheduleWrite ();
14761503 }
@@ -1798,21 +1825,12 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) {
17981825 // offset of a DATA frame's data into the socket read buffer.
17991826 stream_buf_ = uv_buf_init (buf.data (), static_cast <unsigned int >(nread));
18001827
1801- Isolate* isolate = env ()->isolate ();
1802-
18031828 // Store this so we can create an ArrayBuffer for read data from it.
18041829 // DATA frames will be emitted as slices of that ArrayBuffer to avoid having
18051830 // to copy memory.
18061831 stream_buf_allocation_ = std::move (buf);
18071832
1808- ssize_t ret = ConsumeHTTP2Data ();
1809-
1810- if (UNLIKELY (ret < 0 )) {
1811- Debug (this , " fatal error receiving data: %d" , ret);
1812- Local<Value> arg = Integer::New (isolate, static_cast <int32_t >(ret));
1813- MakeCallback (env ()->http2session_on_error_function (), 1 , &arg);
1814- return ;
1815- }
1833+ ConsumeHTTP2Data ();
18161834
18171835 MaybeStopReading ();
18181836}
0 commit comments