Adds Http2VisitorInterface::OnInvalidFrame() and associate plumbing. This method notifies the visitor that the library has processed a frame that it considers invalid.
PiperOrigin-RevId: 381287141
diff --git a/http2/adapter/callback_visitor.cc b/http2/adapter/callback_visitor.cc
index fa1e0f7..a86e795 100644
--- a/http2/adapter/callback_visitor.cc
+++ b/http2/adapter/callback_visitor.cc
@@ -279,6 +279,17 @@
QUICHE_LOG(FATAL) << "Not implemented";
}
+bool CallbackVisitor::OnInvalidFrame(Http2StreamId stream_id, int error_code) {
+ QUICHE_LOG(INFO) << "OnInvalidFrame(" << stream_id << ", " << error_code
+ << ")";
+ QUICHE_DCHECK_EQ(stream_id, current_frame_.hd.stream_id);
+ if (callbacks_->on_invalid_frame_recv_callback) {
+ return 0 == callbacks_->on_invalid_frame_recv_callback(
+ nullptr, ¤t_frame_, error_code, user_data_);
+ }
+ return true;
+}
+
void CallbackVisitor::OnReadyToSendMetadataForStream(Http2StreamId stream_id,
char* buffer,
size_t length,
diff --git a/http2/adapter/callback_visitor.h b/http2/adapter/callback_visitor.h
index fe728ba..f00307d 100644
--- a/http2/adapter/callback_visitor.h
+++ b/http2/adapter/callback_visitor.h
@@ -62,6 +62,7 @@
size_t length,
ssize_t* written,
bool* end_stream) override;
+ bool OnInvalidFrame(Http2StreamId stream_id, int error_code) override;
void OnReadyToSendMetadataForStream(Http2StreamId stream_id,
char* buffer,
size_t length,
diff --git a/http2/adapter/http2_visitor_interface.h b/http2/adapter/http2_visitor_interface.h
index 4250a5f..a707568 100644
--- a/http2/adapter/http2_visitor_interface.h
+++ b/http2/adapter/http2_visitor_interface.h
@@ -163,6 +163,12 @@
ssize_t* written,
bool* end_stream) = 0;
+ // Called when the connection receives an invalid frame. |error_code| is a
+ // negative integer error code generated by the library. A return value of
+ // false will result in the connection entering an error state, with no
+ // further frame processing possible.
+ virtual bool OnInvalidFrame(Http2StreamId stream_id, int error_code) = 0;
+
// Called when the connection is ready to write metadata for |stream_id| to
// the wire. The implementation should write at most |length| bytes of the
// serialized metadata payload to the |buffer| and set |written| to the number
diff --git a/http2/adapter/mock_http2_visitor.h b/http2/adapter/mock_http2_visitor.h
index f3ab9ec..b07f8e3 100644
--- a/http2/adapter/mock_http2_visitor.h
+++ b/http2/adapter/mock_http2_visitor.h
@@ -13,6 +13,7 @@
public:
MockHttp2Visitor() {
ON_CALL(*this, OnHeaderForStream).WillByDefault(testing::Return(true));
+ ON_CALL(*this, OnInvalidFrame).WillByDefault(testing::Return(true));
}
MOCK_METHOD(ssize_t,
@@ -97,11 +98,15 @@
(uint8_t frame_type, Http2StreamId stream_id, size_t length,
uint8_t flags),
(override));
+
MOCK_METHOD(int, OnFrameSent,
(uint8_t frame_type, Http2StreamId stream_id, size_t length,
uint8_t flags, uint32_t error_code),
(override));
+ MOCK_METHOD(bool, OnInvalidFrame, (Http2StreamId stream_id, int error_code),
+ (override));
+
MOCK_METHOD(void,
OnReadyToSendDataForStream,
(Http2StreamId stream_id,
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index 4ca1fab..9e5308b 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -397,6 +397,7 @@
visitor,
OnErrorDebug("Invalid HTTP header field was received: frame type: 1, "
"stream: 1, name: [:bad-status], value: [9000]"));
+ EXPECT_CALL(visitor, OnInvalidFrame(1, -531));
// Bad status trailer will cause a PROTOCOL_ERROR. The header is never
// delivered in an OnHeaderForStream callback.
diff --git a/http2/adapter/nghttp2_callbacks.cc b/http2/adapter/nghttp2_callbacks.cc
index c88dd8b..fd7b786 100644
--- a/http2/adapter/nghttp2_callbacks.cc
+++ b/http2/adapter/nghttp2_callbacks.cc
@@ -188,6 +188,16 @@
frame->hd.length, frame->hd.flags, error_code);
}
+int OnInvalidFrameReceived(nghttp2_session* /* session */,
+ const nghttp2_frame* frame, int lib_error_code,
+ void* user_data) {
+ QUICHE_CHECK_NE(user_data, nullptr);
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ const bool result =
+ visitor->OnInvalidFrame(frame->hd.stream_id, lib_error_code);
+ return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
+}
+
int OnDataChunk(nghttp2_session* /* session */,
uint8_t flags,
Http2StreamId stream_id,
@@ -238,9 +248,12 @@
nghttp2_session_callbacks_set_before_frame_send_callback(callbacks,
&OnBeforeFrameSent);
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, &OnFrameSent);
+ nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
+ callbacks, &OnInvalidFrameReceived);
nghttp2_session_callbacks_set_error_callback2(callbacks, &OnError);
nghttp2_session_callbacks_set_send_data_callback(
callbacks, &DataFrameSourceSendCallback);
+
return MakeCallbacksPtr(callbacks);
}
diff --git a/http2/adapter/nghttp2_callbacks.h b/http2/adapter/nghttp2_callbacks.h
index faa8f9a..37b4536 100644
--- a/http2/adapter/nghttp2_callbacks.h
+++ b/http2/adapter/nghttp2_callbacks.h
@@ -46,6 +46,10 @@
int OnFrameSent(nghttp2_session* session, const nghttp2_frame* frame,
void* user_data);
+// Invoked when an invalid frame is received.
+int OnInvalidFrameReceived(nghttp2_session* session, const nghttp2_frame* frame,
+ int lib_error_code, void* user_data);
+
// Invoked when a chunk of data (from a DATA frame payload) has been received.
int OnDataChunk(nghttp2_session* session, uint8_t flags,
Http2StreamId stream_id, const uint8_t* data, size_t len,
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index 82b70b0..40387cc 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -134,7 +134,8 @@
}
// TODO(birenroy): Validate headers and re-enable this test. The library should
-// invoke OnErrorDebug() with an error message for the invalid header.
+// invoke OnErrorDebug() with an error message for the invalid header. The
+// library should also invoke OnInvalidFrame() for the invalid HEADERS frame.
TEST(OgHttp2AdapterClientTest, DISABLED_ClientHandlesInvalidTrailers) {
DataSavingVisitor visitor;
OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
diff --git a/http2/adapter/recording_http2_visitor.cc b/http2/adapter/recording_http2_visitor.cc
index a752e52..1ccc68e 100644
--- a/http2/adapter/recording_http2_visitor.cc
+++ b/http2/adapter/recording_http2_visitor.cc
@@ -134,6 +134,13 @@
return 0;
}
+bool RecordingHttp2Visitor::OnInvalidFrame(Http2StreamId stream_id,
+ int error_code) {
+ events_.push_back(
+ absl::StrFormat("OnInvalidFrame %d %d", stream_id, error_code));
+ return true;
+}
+
void RecordingHttp2Visitor::OnReadyToSendDataForStream(Http2StreamId stream_id,
char* destination_buffer,
size_t length,
diff --git a/http2/adapter/recording_http2_visitor.h b/http2/adapter/recording_http2_visitor.h
index d286112..e3e40e1 100644
--- a/http2/adapter/recording_http2_visitor.h
+++ b/http2/adapter/recording_http2_visitor.h
@@ -55,6 +55,7 @@
size_t length, uint8_t flags) override;
int OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, size_t length,
uint8_t flags, uint32_t error_code) override;
+ bool OnInvalidFrame(Http2StreamId stream_id, int error_code) override;
void OnReadyToSendDataForStream(Http2StreamId stream_id,
char* destination_buffer,
size_t length,