@@ -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;
@@ -782,7 +783,7 @@ ssize_t Http2Session::OnMaxFrameSizePadding(size_t frameLen,
782
783
// various callback functions. Each of these will typically result in a call
783
784
// out to JavaScript so this particular function is rather hot and can be
784
785
// quite expensive. This is a potential performance optimization target later.
785
- ssize_t Http2Session::ConsumeHTTP2Data () {
786
+ void Http2Session::ConsumeHTTP2Data () {
786
787
CHECK_NOT_NULL (stream_buf_.base );
787
788
CHECK_LE (stream_buf_offset_, stream_buf_.len );
788
789
size_t read_len = stream_buf_.len - stream_buf_offset_;
@@ -792,12 +793,14 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
792
793
read_len,
793
794
nghttp2_session_want_read (session_.get ()));
794
795
set_receive_paused (false );
796
+ custom_recv_error_code_ = nullptr ;
795
797
ssize_t ret =
796
798
nghttp2_session_mem_recv (session_.get (),
797
799
reinterpret_cast <uint8_t *>(stream_buf_.base ) +
798
800
stream_buf_offset_,
799
801
read_len);
800
802
CHECK_NE (ret, NGHTTP2_ERR_NOMEM);
803
+ CHECK_IMPLIES (custom_recv_error_code_ != nullptr , ret < 0 );
801
804
802
805
if (is_receive_paused ()) {
803
806
CHECK (is_reading_stopped ());
@@ -809,7 +812,7 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
809
812
// Even if all bytes were received, a paused stream may delay the
810
813
// nghttp2_on_frame_recv_callback which may have an END_STREAM flag.
811
814
stream_buf_offset_ += ret;
812
- return ret ;
815
+ goto done ;
813
816
}
814
817
815
818
// We are done processing the current input chunk.
@@ -819,14 +822,34 @@ ssize_t Http2Session::ConsumeHTTP2Data() {
819
822
stream_buf_allocation_.clear ();
820
823
stream_buf_ = uv_buf_init (nullptr , 0 );
821
824
822
- if (ret < 0 )
823
- return ret;
824
-
825
825
// Send any data that was queued up while processing the received data.
826
- if (!is_destroyed ()) {
826
+ if (ret >= 0 && !is_destroyed ()) {
827
827
SendPendingData ();
828
828
}
829
- return ret;
829
+
830
+ done:
831
+ if (UNLIKELY (ret < 0 )) {
832
+ Isolate* isolate = env ()->isolate ();
833
+ Debug (this ,
834
+ " fatal error receiving data: %d (%s)" ,
835
+ ret,
836
+ custom_recv_error_code_ != nullptr ?
837
+ custom_recv_error_code_ : " (no custom error code)" );
838
+ Local<Value> args[] = {
839
+ Integer::New (isolate, static_cast <int32_t >(ret)),
840
+ Null (isolate)
841
+ };
842
+ if (custom_recv_error_code_ != nullptr ) {
843
+ args[1 ] = String::NewFromUtf8 (
844
+ isolate,
845
+ custom_recv_error_code_,
846
+ NewStringType::kInternalized ).ToLocalChecked ();
847
+ }
848
+ MakeCallback (
849
+ env ()->http2session_on_error_function (),
850
+ arraysize (args),
851
+ args);
852
+ }
830
853
}
831
854
832
855
@@ -950,14 +973,17 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle,
950
973
int lib_error_code,
951
974
void * user_data) {
952
975
Http2Session* session = static_cast <Http2Session*>(user_data);
976
+ const uint32_t max_invalid_frames = session->js_fields_ ->max_invalid_frames ;
953
977
954
978
Debug (session,
955
979
" invalid frame received (%u/%u), code: %d" ,
956
980
session->invalid_frame_count_ ,
957
- session-> js_fields_ -> max_invalid_frames ,
981
+ max_invalid_frames,
958
982
lib_error_code);
959
- if (session->invalid_frame_count_ ++ > session->js_fields_ ->max_invalid_frames )
983
+ if (session->invalid_frame_count_ ++ > max_invalid_frames) {
984
+ session->custom_recv_error_code_ = " ERR_HTTP2_TOO_MANY_INVALID_FRAMES" ;
960
985
return 1 ;
986
+ }
961
987
962
988
// If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error
963
989
if (nghttp2_is_fatal (lib_error_code) ||
@@ -1336,6 +1362,7 @@ int Http2Session::HandleDataFrame(const nghttp2_frame* frame) {
1336
1362
stream->EmitRead (UV_EOF);
1337
1363
} else if (frame->hd .length == 0 ) {
1338
1364
if (invalid_frame_count_++ > js_fields_->max_invalid_frames ) {
1365
+ custom_recv_error_code_ = " ERR_HTTP2_TOO_MANY_INVALID_FRAMES" ;
1339
1366
Debug (this , " rejecting empty-frame-without-END_STREAM flood\n " );
1340
1367
// Consider a flood of 0-length frames without END_STREAM an error.
1341
1368
return 1 ;
@@ -1520,7 +1547,7 @@ void Http2Session::OnStreamAfterWrite(WriteWrap* w, int status) {
1520
1547
ConsumeHTTP2Data ();
1521
1548
}
1522
1549
1523
- if (!is_write_scheduled ()) {
1550
+ if (!is_write_scheduled () && ! is_destroyed () ) {
1524
1551
// Schedule a new write if nghttp2 wants to send data.
1525
1552
MaybeScheduleWrite ();
1526
1553
}
@@ -1848,21 +1875,12 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) {
1848
1875
// offset of a DATA frame's data into the socket read buffer.
1849
1876
stream_buf_ = uv_buf_init (buf.data (), static_cast <unsigned int >(nread));
1850
1877
1851
- Isolate* isolate = env ()->isolate ();
1852
-
1853
1878
// Store this so we can create an ArrayBuffer for read data from it.
1854
1879
// DATA frames will be emitted as slices of that ArrayBuffer to avoid having
1855
1880
// to copy memory.
1856
1881
stream_buf_allocation_ = std::move (buf);
1857
1882
1858
- ssize_t ret = ConsumeHTTP2Data ();
1859
-
1860
- if (UNLIKELY (ret < 0 )) {
1861
- Debug (this , " fatal error receiving data: %d" , ret);
1862
- Local<Value> arg = Integer::New (isolate, static_cast <int32_t >(ret));
1863
- MakeCallback (env ()->http2session_on_error_function (), 1 , &arg);
1864
- return ;
1865
- }
1883
+ ConsumeHTTP2Data ();
1866
1884
1867
1885
MaybeStopReading ();
1868
1886
}
0 commit comments