This change copies utility methods and basic nghttp2 callback implementations from //net/http2/server/lib/internal/h2.
PiperOrigin-RevId: 367033357
Change-Id: I00b7d16ecdebe8f3a003ba53cab10e6dc3b98d50
diff --git a/http2/adapter/nghttp2_callbacks.cc b/http2/adapter/nghttp2_callbacks.cc
new file mode 100644
index 0000000..337f230
--- /dev/null
+++ b/http2/adapter/nghttp2_callbacks.cc
@@ -0,0 +1,189 @@
+#include "http2/adapter/nghttp2_callbacks.h"
+
+#include <cstdint>
+#include <cstring>
+
+#include "absl/strings/string_view.h"
+#include "http2/adapter/http2_protocol.h"
+#include "http2/adapter/http2_visitor_interface.h"
+#include "http2/adapter/nghttp2_util.h"
+#include "third_party/nghttp2/nghttp2.h"
+#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+#include "common/platform/api/quiche_logging.h"
+#include "common/quiche_endian.h"
+
+namespace http2 {
+namespace adapter {
+
+int OnBeginFrame(nghttp2_session* /* session */,
+ const nghttp2_frame_hd* header,
+ void* user_data) {
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ if (header->type == NGHTTP2_DATA) {
+ visitor->OnBeginDataForStream(header->stream_id, header->length);
+ }
+ return 0;
+}
+
+int OnFrameReceived(nghttp2_session* /* session */,
+ const nghttp2_frame* frame,
+ void* user_data) {
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ const Http2StreamId stream_id = frame->hd.stream_id;
+ switch (frame->hd.type) {
+ // The beginning of the DATA frame is handled in OnBeginFrame(), and the
+ // beginning of the header block is handled in client/server-specific
+ // callbacks. This callback handles the point at which the entire logical
+ // frame has been received and processed.
+ case NGHTTP2_DATA:
+ if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ visitor->OnEndStream(stream_id);
+ }
+ break;
+ case NGHTTP2_HEADERS: {
+ if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) {
+ visitor->OnEndHeadersForStream(stream_id);
+ }
+ if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ visitor->OnEndStream(stream_id);
+ }
+ break;
+ }
+ case NGHTTP2_PRIORITY: {
+ nghttp2_priority_spec priority_spec = frame->priority.pri_spec;
+ visitor->OnPriorityForStream(stream_id, priority_spec.stream_id,
+ priority_spec.weight,
+ priority_spec.exclusive != 0);
+ break;
+ }
+ case NGHTTP2_RST_STREAM: {
+ visitor->OnRstStream(stream_id,
+ ToHttp2ErrorCode(frame->rst_stream.error_code));
+ break;
+ }
+ case NGHTTP2_SETTINGS:
+ if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
+ visitor->OnSettingsAck();
+ } else {
+ visitor->OnSettingsStart();
+ for (int i = 0; i < frame->settings.niv; ++i) {
+ nghttp2_settings_entry entry = frame->settings.iv[i];
+ // The nghttp2_settings_entry uses int32_t for the ID; we must cast.
+ visitor->OnSetting(Http2Setting{
+ .id = static_cast<Http2SettingsId>(entry.settings_id),
+ .value = entry.value});
+ }
+ visitor->OnSettingsEnd();
+ }
+ break;
+ case NGHTTP2_PUSH_PROMISE:
+ // This case is handled by headers-related callbacks:
+ // 1. visitor->OnPushPromiseForStream() is invoked in the client-side
+ // OnHeadersStart() adapter callback, as nghttp2 only allows clients
+ // to receive PUSH_PROMISE frames.
+ // 2. visitor->OnHeaderForStream() is invoked for each server push
+ // request header in the PUSH_PROMISE header block.
+ // 3. This switch statement is reached once all server push request
+ // headers have been parsed.
+ break;
+ case NGHTTP2_PING: {
+ Http2PingId ping_id;
+ std::memcpy(&ping_id, frame->ping.opaque_data, sizeof(Http2PingId));
+ visitor->OnPing(quiche::QuicheEndian::NetToHost64(ping_id),
+ (frame->hd.flags & NGHTTP2_FLAG_ACK) != 0);
+ break;
+ }
+ case NGHTTP2_GOAWAY: {
+ absl::string_view opaque_data(
+ reinterpret_cast<const char*>(frame->goaway.opaque_data),
+ frame->goaway.opaque_data_len);
+ visitor->OnGoAway(frame->goaway.last_stream_id,
+ ToHttp2ErrorCode(frame->goaway.error_code),
+ opaque_data);
+ break;
+ }
+ case NGHTTP2_WINDOW_UPDATE: {
+ visitor->OnWindowUpdate(stream_id,
+ frame->window_update.window_size_increment);
+ break;
+ }
+ case NGHTTP2_CONTINUATION:
+ // This frame type should not be passed to any callbacks, according to
+ // https://nghttp2.org/documentation/enums.html#c.NGHTTP2_CONTINUATION.
+ QUICHE_LOG(ERROR) << "Unexpected receipt of NGHTTP2_CONTINUATION type!";
+ break;
+ case NGHTTP2_ALTSVC:
+ break;
+ case NGHTTP2_ORIGIN:
+ break;
+ }
+
+ return 0;
+}
+
+int OnBeginHeaders(nghttp2_session* /* session */,
+ const nghttp2_frame* frame,
+ void* user_data) {
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ visitor->OnBeginHeadersForStream(frame->hd.stream_id);
+ return 0;
+}
+
+int OnHeader(nghttp2_session* /* session */,
+ const nghttp2_frame* frame,
+ nghttp2_rcbuf* name,
+ nghttp2_rcbuf* value,
+ uint8_t flags,
+ void* user_data) {
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ visitor->OnHeaderForStream(frame->hd.stream_id, ToStringView(name),
+ ToStringView(value));
+ return 0;
+}
+
+int OnDataChunk(nghttp2_session* /* session */,
+ uint8_t flags,
+ Http2StreamId stream_id,
+ const uint8_t* data,
+ size_t len,
+ void* user_data) {
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ visitor->OnDataForStream(
+ stream_id, absl::string_view(reinterpret_cast<const char*>(data), len));
+ return 0;
+}
+
+int OnStreamClosed(nghttp2_session* /* session */,
+ Http2StreamId stream_id,
+ uint32_t error_code,
+ void* user_data) {
+ auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
+ if (error_code == static_cast<uint32_t>(Http2ErrorCode::NO_ERROR)) {
+ visitor->OnCloseStream(stream_id);
+ } else {
+ visitor->OnAbortStream(stream_id, ToHttp2ErrorCode(error_code));
+ }
+ return 0;
+}
+
+ssize_t OnReadyToReadDataForStream(nghttp2_session* /* session */,
+ Http2StreamId stream_id,
+ uint8_t* dest_buffer,
+ size_t max_length,
+ uint32_t* data_flags,
+ nghttp2_data_source* source,
+ void* user_data) {
+ auto* visitor = static_cast<Http2VisitorInterface*>(source->ptr);
+ ssize_t bytes_to_send = 0;
+ bool end_stream = false;
+ visitor->OnReadyToSendDataForStream(stream_id,
+ reinterpret_cast<char*>(dest_buffer),
+ max_length, &bytes_to_send, &end_stream);
+ if (bytes_to_send >= 0 && end_stream) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+ return bytes_to_send;
+}
+
+} // namespace adapter
+} // namespace http2