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
diff --git a/http2/adapter/nghttp2_callbacks.h b/http2/adapter/nghttp2_callbacks.h
new file mode 100644
index 0000000..7e6ce7c
--- /dev/null
+++ b/http2/adapter/nghttp2_callbacks.h
@@ -0,0 +1,52 @@
+#ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_CALLBACKS_H_
+#define QUICHE_HTTP2_ADAPTER_NGHTTP2_CALLBACKS_H_
+
+#include "http2/adapter/http2_protocol.h"
+#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+
+namespace http2 {
+namespace adapter {
+
+// The following functions are nghttp2 callbacks that Nghttp2Adapter sets at the
+// beginning of its lifetime. It is expected that |user_data| holds an
+// Http2VisitorInterface.
+
+// Callback once a frame header has been received.
+int OnBeginFrame(nghttp2_session* session, const nghttp2_frame_hd* header,
+                 void* user_data);
+
+// Callback once a complete frame has been received.
+int OnFrameReceived(nghttp2_session* session, const nghttp2_frame* frame,
+                    void* user_data);
+
+// Callback at the start of a frame carrying headers.
+int OnBeginHeaders(nghttp2_session* session,
+                   const nghttp2_frame* frame,
+                   void* user_data);
+
+// Callback once a name-value header has been received.
+int OnHeader(nghttp2_session* session, const nghttp2_frame* frame,
+             nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t flags,
+             void* user_data);
+
+// Callback once 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,
+                void* user_data);
+
+// Callback once a stream has been closed.
+int OnStreamClosed(nghttp2_session* session, Http2StreamId stream_id,
+                   uint32_t error_code, void* user_data);
+
+// Callback once nghttp2 is ready to read data from |source| into |dest_buffer|.
+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);
+
+}  // namespace adapter
+}  // namespace http2
+
+#endif  // QUICHE_HTTP2_ADAPTER_NGHTTP2_CALLBACKS_H_
diff --git a/http2/adapter/nghttp2_util.cc b/http2/adapter/nghttp2_util.cc
new file mode 100644
index 0000000..e134148
--- /dev/null
+++ b/http2/adapter/nghttp2_util.cc
@@ -0,0 +1,82 @@
+#include "http2/adapter/nghttp2_util.h"
+
+#include <cstdint>
+
+#include "absl/strings/string_view.h"
+#include "http2/adapter/http2_protocol.h"
+#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+#include "common/platform/api/quiche_logging.h"
+
+namespace http2 {
+namespace adapter {
+
+uint8_t* ToUint8Ptr(char* str) { return reinterpret_cast<uint8_t*>(str); }
+uint8_t* ToUint8Ptr(const char* str) {
+  return const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(str));
+}
+
+absl::string_view ToStringView(nghttp2_rcbuf* rc_buffer) {
+  nghttp2_vec buffer = nghttp2_rcbuf_get_buf(rc_buffer);
+  return absl::string_view(reinterpret_cast<const char*>(buffer.base),
+                           buffer.len);
+}
+
+absl::string_view ToStringView(uint8_t* pointer, size_t length) {
+  return absl::string_view(reinterpret_cast<const char*>(pointer), length);
+}
+
+std::vector<nghttp2_nv> GetRequestNghttp2Nvs(absl::Span<const Header> headers) {
+  const int num_headers = headers.size();
+  auto nghttp2_nvs = std::vector<nghttp2_nv>(num_headers);
+  for (int i = 0; i < num_headers; ++i) {
+    nghttp2_nv header;
+    header.name = ToUint8Ptr(&headers[i].first[0]);
+    header.namelen = headers[i].first.size();
+    header.value = ToUint8Ptr(&headers[i].second[0]);
+    header.valuelen = headers[i].second.size();
+    header.flags = NGHTTP2_FLAG_NONE;
+    nghttp2_nvs.push_back(std::move(header));
+  }
+
+  return nghttp2_nvs;
+}
+
+std::vector<nghttp2_nv> GetResponseNghttp2Nvs(
+    const spdy::Http2HeaderBlock& headers,
+    absl::string_view response_code) {
+  // Allocate enough for all headers and also the :status pseudoheader.
+  const int num_headers = headers.size();
+  auto nghttp2_nvs = std::vector<nghttp2_nv>(num_headers + 1);
+
+  // Add the :status pseudoheader first.
+  nghttp2_nv status;
+  status.name = ToUint8Ptr(kHttp2StatusPseudoHeader);
+  status.namelen = strlen(kHttp2StatusPseudoHeader);
+  status.value = ToUint8Ptr(response_code.data());
+  status.valuelen = response_code.size();
+  status.flags = NGHTTP2_FLAG_NONE;
+  nghttp2_nvs.push_back(std::move(status));
+
+  // Add the remaining headers.
+  for (const auto header_pair : headers) {
+    nghttp2_nv header;
+    header.name = ToUint8Ptr(header_pair.first.data());
+    header.namelen = header_pair.first.size();
+    header.value = ToUint8Ptr(header_pair.second.data());
+    header.valuelen = header_pair.second.size();
+    header.flags = NGHTTP2_FLAG_NONE;
+    nghttp2_nvs.push_back(std::move(header));
+  }
+
+  return nghttp2_nvs;
+}
+
+Http2ErrorCode ToHttp2ErrorCode(uint32_t wire_error_code) {
+  if (wire_error_code > static_cast<int>(Http2ErrorCode::MAX_ERROR_CODE)) {
+    return Http2ErrorCode::INTERNAL_ERROR;
+  }
+  return static_cast<Http2ErrorCode>(wire_error_code);
+}
+
+}  // namespace adapter
+}  // namespace http2
diff --git a/http2/adapter/nghttp2_util.h b/http2/adapter/nghttp2_util.h
new file mode 100644
index 0000000..3fcbfcd
--- /dev/null
+++ b/http2/adapter/nghttp2_util.h
@@ -0,0 +1,48 @@
+// Various utility/conversion functions for compatibility with the nghttp2 API.
+
+#ifndef QUICHE_HTTP2_ADAPTER_NGHTTP2_UTIL_H_
+#define QUICHE_HTTP2_ADAPTER_NGHTTP2_UTIL_H_
+
+#include <cstdint>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "http2/adapter/http2_protocol.h"
+#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
+#include "spdy/core/spdy_header_block.h"
+
+namespace http2 {
+namespace adapter {
+
+// Return codes to represent various errors.
+inline constexpr int kStreamCallbackFailureStatus =
+    NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
+inline constexpr int kCancelStatus = NGHTTP2_ERR_CANCEL;
+
+uint8_t* ToUint8Ptr(char* str);
+uint8_t* ToUint8Ptr(const char* str);
+
+absl::string_view ToStringView(nghttp2_rcbuf* rc_buffer);
+absl::string_view ToStringView(uint8_t* pointer, size_t length);
+
+// Returns the nghttp2 header structure from the given request |headers|, which
+// must have the correct pseudoheaders preceding other headers.
+std::vector<nghttp2_nv> GetRequestNghttp2Nvs(absl::Span<const Header> headers);
+
+// Returns the nghttp2 header structure from the given response |headers|, with
+// the :status pseudoheader first based on the given |response_code|. The
+// |response_code| is passed in separately from |headers| for lifetime reasons.
+std::vector<nghttp2_nv> GetResponseNghttp2Nvs(
+    const spdy::Http2HeaderBlock& headers,
+    absl::string_view response_code);
+
+// Returns the HTTP/2 error code corresponding to the raw wire value, as defined
+// in RFC 7540 Section 7. Unrecognized error codes are treated as INTERNAL_ERROR
+// based on the RFC 7540 Section 7 suggestion.
+Http2ErrorCode ToHttp2ErrorCode(uint32_t wire_error_code);
+
+}  // namespace adapter
+}  // namespace http2
+
+#endif  // QUICHE_HTTP2_ADAPTER_NGHTTP2_UTIL_H_