@@ -32,6 +32,7 @@ using v8::Integer;
32
32
using v8::Isolate;
33
33
using v8::Local;
34
34
using v8::MaybeLocal;
35
+ using v8::NewStringType;
35
36
using v8::Number;
36
37
using v8::Object;
37
38
using v8::ObjectTemplate;
@@ -732,7 +733,7 @@ ssize_t Http2Session::OnMaxFrameSizePadding(size_t frameLen,
732
733
// various callback functions. Each of these will typically result in a call
733
734
// out to JavaScript so this particular function is rather hot and can be
734
735
// quite expensive. This is a potential performance optimization target later.
735
- ssize_t Http2Session::ConsumeHTTP2Data () {
736
+ void Http2Session::ConsumeHTTP2Data () {
736
737
CHECK_NOT_NULL (stream_buf_.base );
737
738
CHECK_LE (stream_buf_offset_, stream_buf_.len );
738
739
size_t read_len = stream_buf_.len - stream_buf_offset_;
@@ -742,12 +743,14 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
742
743
read_len,
743
744
nghttp2_session_want_read (session_.get ()));
744
745
set_receive_paused (false );
746
+ custom_recv_error_code_ = nullptr ;
745
747
ssize_t ret =
746
748
nghttp2_session_mem_recv (session_.get (),
747
749
reinterpret_cast <uint8_t *>(stream_buf_.base ) +
748
750
stream_buf_offset_,
749
751
read_len);
750
752
CHECK_NE (ret, NGHTTP2_ERR_NOMEM);
753
+ CHECK_IMPLIES (custom_recv_error_code_ != nullptr , ret < 0 );
751
754
752
755
if (is_receive_paused ()) {
753
756
CHECK (is_reading_stopped ());
@@ -759,7 +762,7 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
759
762
// Even if all bytes were received, a paused stream may delay the
760
763
// nghttp2_on_frame_recv_callback which may have an END_STREAM flag.
761
764
stream_buf_offset_ += ret;
762
- return ret ;
765
+ goto done ;
763
766
}
764
767
765
768
// We are done processing the current input chunk.
@@ -769,14 +772,34 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
769
772
stream_buf_allocation_.clear ();
770
773
stream_buf_ = uv_buf_init (nullptr , 0 );
771
774
772
- if (ret < 0 )
773
- return ret;
774
-
775
775
// Send any data that was queued up while processing the received data.
776
- if (!is_destroyed ()) {
776
+ if (ret >= 0 && !is_destroyed ()) {
777
777
SendPendingData ();
778
778
}
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
+ }
780
803
}
781
804
782
805
@@ -900,14 +923,17 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle,
900
923
int lib_error_code,
901
924
void * user_data) {
902
925
Http2Session* session = static_cast <Http2Session*>(user_data);
926
+ const uint32_t max_invalid_frames = session->js_fields_ ->max_invalid_frames ;
903
927
904
928
Debug (session,
905
929
" invalid frame received (%u/%u), code: %d" ,
906
930
session->invalid_frame_count_ ,
907
- session-> js_fields_ -> max_invalid_frames ,
931
+ max_invalid_frames,
908
932
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" ;
910
935
return 1 ;
936
+ }
911
937
912
938
// If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error
913
939
if (nghttp2_is_fatal (lib_error_code) ||
@@ -1286,6 +1312,7 @@ int Http2Session::HandleDataFrame(const nghttp2_frame* frame) {
1286
1312
stream->EmitRead (UV_EOF);
1287
1313
} else if (frame->hd .length == 0 ) {
1288
1314
if (invalid_frame_count_++ > js_fields_->max_invalid_frames ) {
1315
+ custom_recv_error_code_ = " ERR_HTTP2_TOO_MANY_INVALID_FRAMES" ;
1289
1316
Debug (this , " rejecting empty-frame-without-END_STREAM flood\n " );
1290
1317
// Consider a flood of 0-length frames without END_STREAM an error.
1291
1318
return 1 ;
@@ -1470,7 +1497,7 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) {
1470
1497
ConsumeHTTP2Data ();
1471
1498
}
1472
1499
1473
- if (!is_write_scheduled ()) {
1500
+ if (!is_write_scheduled () && ! is_destroyed () ) {
1474
1501
// Schedule a new write if nghttp2 wants to send data.
1475
1502
MaybeScheduleWrite ();
1476
1503
}
@@ -1798,21 +1825,12 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) {
1798
1825
// offset of a DATA frame's data into the socket read buffer.
1799
1826
stream_buf_ = uv_buf_init (buf.data (), static_cast <unsigned int >(nread));
1800
1827
1801
- Isolate* isolate = env ()->isolate ();
1802
-
1803
1828
// Store this so we can create an ArrayBuffer for read data from it.
1804
1829
// DATA frames will be emitted as slices of that ArrayBuffer to avoid having
1805
1830
// to copy memory.
1806
1831
stream_buf_allocation_ = std::move (buf);
1807
1832
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 ();
1816
1834
1817
1835
MaybeStopReading ();
1818
1836
}
0 commit comments