| #include "http2/adapter/callback_visitor.h" | 
 |  | 
 | #include "http2/adapter/nghttp2_util.h" | 
 | #include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h" | 
 | #include "common/quiche_endian.h" | 
 |  | 
 | // This visitor implementation needs visibility into the | 
 | // nghttp2_session_callbacks type. There's no public header, so we'll redefine | 
 | // the struct here. | 
 | struct nghttp2_session_callbacks { | 
 |   nghttp2_send_callback send_callback; | 
 |   nghttp2_recv_callback recv_callback; | 
 |   nghttp2_on_frame_recv_callback on_frame_recv_callback; | 
 |   nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; | 
 |   nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback; | 
 |   nghttp2_before_frame_send_callback before_frame_send_callback; | 
 |   nghttp2_on_frame_send_callback on_frame_send_callback; | 
 |   nghttp2_on_frame_not_send_callback on_frame_not_send_callback; | 
 |   nghttp2_on_stream_close_callback on_stream_close_callback; | 
 |   nghttp2_on_begin_headers_callback on_begin_headers_callback; | 
 |   nghttp2_on_header_callback on_header_callback; | 
 |   nghttp2_on_header_callback2 on_header_callback2; | 
 |   nghttp2_on_invalid_header_callback on_invalid_header_callback; | 
 |   nghttp2_on_invalid_header_callback2 on_invalid_header_callback2; | 
 |   nghttp2_select_padding_callback select_padding_callback; | 
 |   nghttp2_data_source_read_length_callback read_length_callback; | 
 |   nghttp2_on_begin_frame_callback on_begin_frame_callback; | 
 |   nghttp2_send_data_callback send_data_callback; | 
 |   nghttp2_pack_extension_callback pack_extension_callback; | 
 |   nghttp2_unpack_extension_callback unpack_extension_callback; | 
 |   nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback; | 
 |   nghttp2_error_callback error_callback; | 
 |   nghttp2_error_callback2 error_callback2; | 
 | }; | 
 |  | 
 | namespace http2 { | 
 | namespace adapter { | 
 |  | 
 | CallbackVisitor::CallbackVisitor(Perspective perspective, | 
 |                                  const nghttp2_session_callbacks& callbacks, | 
 |                                  void* user_data) | 
 |     : perspective_(perspective), | 
 |       callbacks_(MakeCallbacksPtr(nullptr)), | 
 |       user_data_(user_data) { | 
 |   nghttp2_session_callbacks* c; | 
 |   nghttp2_session_callbacks_new(&c); | 
 |   *c = callbacks; | 
 |   callbacks_ = MakeCallbacksPtr(c); | 
 | } | 
 |  | 
 | ssize_t CallbackVisitor::OnReadyToSend(absl::string_view serialized) { | 
 |   return callbacks_->send_callback(nullptr, ToUint8Ptr(serialized.data()), | 
 |                                    serialized.size(), 0, user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnConnectionError() { | 
 |   QUICHE_LOG(FATAL) << "Not implemented"; | 
 | } | 
 |  | 
 | void CallbackVisitor::OnFrameHeader(Http2StreamId stream_id, | 
 |                                     size_t length, | 
 |                                     uint8_t type, | 
 |                                     uint8_t flags) { | 
 |   // The general strategy is to clear |current_frame_| at the start of a new | 
 |   // frame, accumulate frame information from the various callback events, then | 
 |   // invoke the on_frame_recv_callback() with the accumulated frame data. | 
 |   memset(¤t_frame_, 0, sizeof(current_frame_)); | 
 |   current_frame_.hd.stream_id = stream_id; | 
 |   current_frame_.hd.length = length; | 
 |   current_frame_.hd.type = type; | 
 |   current_frame_.hd.flags = flags; | 
 |   callbacks_->on_begin_frame_callback(nullptr, ¤t_frame_.hd, user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnSettingsStart() {} | 
 |  | 
 | void CallbackVisitor::OnSetting(Http2Setting setting) { | 
 |   settings_.push_back({.settings_id = setting.id, .value = setting.value}); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnSettingsEnd() { | 
 |   current_frame_.settings.niv = settings_.size(); | 
 |   current_frame_.settings.iv = settings_.data(); | 
 |   callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 |   settings_.clear(); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnSettingsAck() { | 
 |   // ACK is part of the flags, which were set in OnFrameHeader(). | 
 |   callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnBeginHeadersForStream(Http2StreamId stream_id) { | 
 |   auto it = GetStreamInfo(stream_id); | 
 |   if (it->second->received_headers) { | 
 |     // At least one headers frame has already been received. | 
 |     current_frame_.headers.cat = NGHTTP2_HCAT_HEADERS; | 
 |   } else { | 
 |     switch (perspective_) { | 
 |       case Perspective::kClient: | 
 |         current_frame_.headers.cat = NGHTTP2_HCAT_RESPONSE; | 
 |         break; | 
 |       case Perspective::kServer: | 
 |         current_frame_.headers.cat = NGHTTP2_HCAT_REQUEST; | 
 |         break; | 
 |     } | 
 |   } | 
 |   callbacks_->on_begin_headers_callback(nullptr, ¤t_frame_, user_data_); | 
 |   it->second->received_headers = true; | 
 | } | 
 |  | 
 | Http2VisitorInterface::OnHeaderResult CallbackVisitor::OnHeaderForStream( | 
 |     Http2StreamId stream_id, absl::string_view name, absl::string_view value) { | 
 |   if (callbacks_->on_header_callback) { | 
 |     const int result = callbacks_->on_header_callback( | 
 |         nullptr, ¤t_frame_, ToUint8Ptr(name.data()), name.size(), | 
 |         ToUint8Ptr(value.data()), value.size(), NGHTTP2_NV_FLAG_NONE, | 
 |         user_data_); | 
 |     if (result == 0) { | 
 |       return HEADER_OK; | 
 |     } else if (result == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { | 
 |       return HEADER_RST_STREAM; | 
 |     } else { | 
 |       // Assume NGHTTP2_ERR_CALLBACK_FAILURE. | 
 |       return HEADER_CONNECTION_ERROR; | 
 |     } | 
 |   } | 
 |   return HEADER_OK; | 
 | } | 
 |  | 
 | void CallbackVisitor::OnEndHeadersForStream(Http2StreamId stream_id) { | 
 |   callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnBeginDataForStream(Http2StreamId stream_id, | 
 |                                            size_t payload_length) { | 
 |   // TODO(b/181586191): Interpret padding, subtract padding from | 
 |   // |remaining_data_|. | 
 |   remaining_data_ = payload_length; | 
 |   if (remaining_data_ == 0) { | 
 |     callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 |   } | 
 | } | 
 |  | 
 | void CallbackVisitor::OnDataForStream(Http2StreamId stream_id, | 
 |                                       absl::string_view data) { | 
 |   callbacks_->on_data_chunk_recv_callback(nullptr, current_frame_.hd.flags, | 
 |                                           stream_id, ToUint8Ptr(data.data()), | 
 |                                           data.size(), user_data_); | 
 |   remaining_data_ -= data.size(); | 
 |   if (remaining_data_ == 0) { | 
 |     callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 |   } | 
 | } | 
 |  | 
 | void CallbackVisitor::OnEndStream(Http2StreamId stream_id) {} | 
 |  | 
 | void CallbackVisitor::OnRstStream(Http2StreamId stream_id, | 
 |                                   Http2ErrorCode error_code) { | 
 |   current_frame_.rst_stream.error_code = static_cast<uint32_t>(error_code); | 
 |   callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnCloseStream(Http2StreamId stream_id, | 
 |                                     Http2ErrorCode error_code) { | 
 |   callbacks_->on_stream_close_callback( | 
 |       nullptr, stream_id, static_cast<uint32_t>(error_code), user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnPriorityForStream(Http2StreamId stream_id, | 
 |                                           Http2StreamId parent_stream_id, | 
 |                                           int weight, | 
 |                                           bool exclusive) { | 
 |   current_frame_.priority.pri_spec.stream_id = parent_stream_id; | 
 |   current_frame_.priority.pri_spec.weight = weight; | 
 |   current_frame_.priority.pri_spec.exclusive = exclusive; | 
 |   callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnPing(Http2PingId ping_id, bool is_ack) { | 
 |   uint64_t network_order_opaque_data = | 
 |       quiche::QuicheEndian::HostToNet64(ping_id); | 
 |   std::memcpy(current_frame_.ping.opaque_data, &network_order_opaque_data, | 
 |               sizeof(network_order_opaque_data)); | 
 |   callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnPushPromiseForStream(Http2StreamId stream_id, | 
 |                                              Http2StreamId promised_stream_id) { | 
 |   QUICHE_LOG(FATAL) << "Not implemented"; | 
 | } | 
 |  | 
 | void CallbackVisitor::OnGoAway(Http2StreamId last_accepted_stream_id, | 
 |                                Http2ErrorCode error_code, | 
 |                                absl::string_view opaque_data) { | 
 |   current_frame_.goaway.last_stream_id = last_accepted_stream_id; | 
 |   current_frame_.goaway.error_code = static_cast<uint32_t>(error_code); | 
 |   current_frame_.goaway.opaque_data = ToUint8Ptr(opaque_data.data()); | 
 |   current_frame_.goaway.opaque_data_len = opaque_data.size(); | 
 |   callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 | } | 
 |  | 
 | void CallbackVisitor::OnWindowUpdate(Http2StreamId stream_id, | 
 |                                      int window_increment) { | 
 |   current_frame_.window_update.window_size_increment = window_increment; | 
 |   if (callbacks_->on_frame_recv_callback) { | 
 |     callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 |   } | 
 | } | 
 |  | 
 | void CallbackVisitor::PopulateFrame(nghttp2_frame& frame, uint8_t frame_type, | 
 |                                     Http2StreamId stream_id, size_t length, | 
 |                                     uint8_t flags, uint32_t error_code, | 
 |                                     bool sent_headers) { | 
 |   frame.hd.type = frame_type; | 
 |   frame.hd.stream_id = stream_id; | 
 |   frame.hd.length = length; | 
 |   frame.hd.flags = flags; | 
 |   const FrameType frame_type_enum = static_cast<FrameType>(frame_type); | 
 |   if (frame_type_enum == FrameType::HEADERS) { | 
 |     if (sent_headers) { | 
 |       frame.headers.cat = NGHTTP2_HCAT_HEADERS; | 
 |     } else { | 
 |       switch (perspective_) { | 
 |         case Perspective::kClient: | 
 |           QUICHE_LOG(INFO) << "First headers sent by the client for stream " | 
 |                            << stream_id << "; these are request headers"; | 
 |           frame.headers.cat = NGHTTP2_HCAT_REQUEST; | 
 |           break; | 
 |         case Perspective::kServer: | 
 |           QUICHE_LOG(INFO) << "First headers sent by the server for stream " | 
 |                            << stream_id << "; these are response headers"; | 
 |           frame.headers.cat = NGHTTP2_HCAT_RESPONSE; | 
 |           break; | 
 |       } | 
 |     } | 
 |   } else if (frame_type_enum == FrameType::RST_STREAM) { | 
 |     frame.rst_stream.error_code = error_code; | 
 |   } else if (frame_type_enum == FrameType::GOAWAY) { | 
 |     frame.goaway.error_code = error_code; | 
 |   } | 
 | } | 
 |  | 
 | int CallbackVisitor::OnBeforeFrameSent(uint8_t frame_type, | 
 |                                        Http2StreamId stream_id, size_t length, | 
 |                                        uint8_t flags) { | 
 |   if (callbacks_->before_frame_send_callback) { | 
 |     QUICHE_LOG(INFO) << "OnBeforeFrameSent(type=" << int(frame_type) | 
 |                      << ", stream_id=" << stream_id << ", length=" << length | 
 |                      << ", flags=" << int(flags) << ")"; | 
 |     nghttp2_frame frame; | 
 |     auto it = GetStreamInfo(stream_id); | 
 |     // The implementation of the before_frame_send_callback doesn't look at the | 
 |     // error code, so for now it's populated with 0. | 
 |     PopulateFrame(frame, frame_type, stream_id, length, flags, /*error_code=*/0, | 
 |                   it->second->before_sent_headers); | 
 |     it->second->before_sent_headers = true; | 
 |     return callbacks_->before_frame_send_callback(nullptr, &frame, user_data_); | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | int CallbackVisitor::OnFrameSent(uint8_t frame_type, Http2StreamId stream_id, | 
 |                                  size_t length, uint8_t flags, | 
 |                                  uint32_t error_code) { | 
 |   if (callbacks_->on_frame_send_callback) { | 
 |     QUICHE_LOG(INFO) << "OnFrameSent(type=" << int(frame_type) | 
 |                      << ", stream_id=" << stream_id << ", length=" << length | 
 |                      << ", flags=" << int(flags) | 
 |                      << ", error_code=" << error_code << ")"; | 
 |     nghttp2_frame frame; | 
 |     auto it = GetStreamInfo(stream_id); | 
 |     PopulateFrame(frame, frame_type, stream_id, length, flags, error_code, | 
 |                   it->second->sent_headers); | 
 |     it->second->sent_headers = true; | 
 |     return callbacks_->on_frame_send_callback(nullptr, &frame, user_data_); | 
 |   } | 
 |   return 0; | 
 | } | 
 |  | 
 | void CallbackVisitor::OnReadyToSendDataForStream(Http2StreamId stream_id, | 
 |                                                  char* destination_buffer, | 
 |                                                  size_t length, | 
 |                                                  ssize_t* written, | 
 |                                                  bool* end_stream) { | 
 |   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, | 
 |                                                      ssize_t* written) { | 
 |   QUICHE_LOG(FATAL) << "Not implemented"; | 
 | } | 
 |  | 
 | void CallbackVisitor::OnBeginMetadataForStream(Http2StreamId stream_id, | 
 |                                                size_t payload_length) { | 
 |   QUICHE_VLOG(1) << "OnBeginMetadataForStream(stream_id=" << stream_id | 
 |                  << ", payload_length=" << payload_length << ")"; | 
 | } | 
 |  | 
 | void CallbackVisitor::OnMetadataForStream(Http2StreamId stream_id, | 
 |                                           absl::string_view metadata) { | 
 |   QUICHE_VLOG(1) << "OnMetadataForStream(stream_id=" << stream_id | 
 |                  << ", len=" << metadata.size() << ")"; | 
 |   if (callbacks_->on_extension_chunk_recv_callback) { | 
 |     int result = callbacks_->on_extension_chunk_recv_callback( | 
 |         nullptr, ¤t_frame_.hd, ToUint8Ptr(metadata.data()), | 
 |         metadata.size(), user_data_); | 
 |     QUICHE_DCHECK_EQ(0, result); | 
 |   } | 
 | } | 
 |  | 
 | bool CallbackVisitor::OnMetadataEndForStream(Http2StreamId stream_id) { | 
 |   QUICHE_LOG_IF(DFATAL, current_frame_.hd.flags != kMetadataEndFlag); | 
 |   QUICHE_VLOG(1) << "OnMetadataEndForStream(stream_id=" << stream_id << ")"; | 
 |   if (callbacks_->unpack_extension_callback) { | 
 |     void* payload; | 
 |     int result = callbacks_->unpack_extension_callback( | 
 |         nullptr, &payload, ¤t_frame_.hd, user_data_); | 
 |     if (callbacks_->on_frame_recv_callback) { | 
 |       current_frame_.ext.payload = payload; | 
 |       callbacks_->on_frame_recv_callback(nullptr, ¤t_frame_, user_data_); | 
 |     } | 
 |     return (result == 0); | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | void CallbackVisitor::OnErrorDebug(absl::string_view message) { | 
 |   if (callbacks_->error_callback2) { | 
 |     callbacks_->error_callback2(nullptr, -1, message.data(), message.size(), | 
 |                                 user_data_); | 
 |   } | 
 | } | 
 |  | 
 | CallbackVisitor::StreamInfoMap::iterator CallbackVisitor::GetStreamInfo( | 
 |     Http2StreamId stream_id) { | 
 |   auto it = stream_map_.find(stream_id); | 
 |   if (it == stream_map_.end()) { | 
 |     auto p = stream_map_.insert({stream_id, absl::make_unique<StreamInfo>()}); | 
 |     it = p.first; | 
 |   } | 
 |   return it; | 
 | } | 
 |  | 
 | }  // namespace adapter | 
 | }  // namespace http2 |