• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "quiche/http2/adapter/callback_visitor.h"
2 
3 #include "absl/strings/escaping.h"
4 #include "quiche/http2/adapter/http2_util.h"
5 #include "quiche/http2/adapter/nghttp2_util.h"
6 #include "quiche/common/quiche_endian.h"
7 
8 // This visitor implementation needs visibility into the
9 // nghttp2_session_callbacks type. There's no public header, so we'll redefine
10 // the struct here.
11 struct nghttp2_session_callbacks {
12   nghttp2_send_callback send_callback;
13   nghttp2_recv_callback recv_callback;
14   nghttp2_on_frame_recv_callback on_frame_recv_callback;
15   nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback;
16   nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback;
17   nghttp2_before_frame_send_callback before_frame_send_callback;
18   nghttp2_on_frame_send_callback on_frame_send_callback;
19   nghttp2_on_frame_not_send_callback on_frame_not_send_callback;
20   nghttp2_on_stream_close_callback on_stream_close_callback;
21   nghttp2_on_begin_headers_callback on_begin_headers_callback;
22   nghttp2_on_header_callback on_header_callback;
23   nghttp2_on_header_callback2 on_header_callback2;
24   nghttp2_on_invalid_header_callback on_invalid_header_callback;
25   nghttp2_on_invalid_header_callback2 on_invalid_header_callback2;
26   nghttp2_select_padding_callback select_padding_callback;
27   nghttp2_data_source_read_length_callback read_length_callback;
28   nghttp2_on_begin_frame_callback on_begin_frame_callback;
29   nghttp2_send_data_callback send_data_callback;
30   nghttp2_pack_extension_callback pack_extension_callback;
31   nghttp2_unpack_extension_callback unpack_extension_callback;
32   nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback;
33   nghttp2_error_callback error_callback;
34   nghttp2_error_callback2 error_callback2;
35 };
36 
37 namespace http2 {
38 namespace adapter {
39 
CallbackVisitor(Perspective perspective,const nghttp2_session_callbacks & callbacks,void * user_data)40 CallbackVisitor::CallbackVisitor(Perspective perspective,
41                                  const nghttp2_session_callbacks& callbacks,
42                                  void* user_data)
43     : perspective_(perspective),
44       callbacks_(MakeCallbacksPtr(nullptr)),
45       user_data_(user_data) {
46   nghttp2_session_callbacks* c;
47   nghttp2_session_callbacks_new(&c);
48   *c = callbacks;
49   callbacks_ = MakeCallbacksPtr(c);
50   memset(&current_frame_, 0, sizeof(current_frame_));
51 }
52 
OnReadyToSend(absl::string_view serialized)53 int64_t CallbackVisitor::OnReadyToSend(absl::string_view serialized) {
54   if (!callbacks_->send_callback) {
55     return kSendError;
56   }
57   int64_t result = callbacks_->send_callback(
58       nullptr, ToUint8Ptr(serialized.data()), serialized.size(), 0, user_data_);
59   QUICHE_VLOG(1) << "CallbackVisitor::OnReadyToSend called with "
60                  << serialized.size() << " bytes, returning " << result;
61   QUICHE_VLOG(2) << (perspective_ == Perspective::kClient ? "Client" : "Server")
62                  << " sending: [" << absl::CEscape(serialized) << "]";
63   if (result > 0) {
64     return result;
65   } else if (result == NGHTTP2_ERR_WOULDBLOCK) {
66     return kSendBlocked;
67   } else {
68     return kSendError;
69   }
70 }
71 
OnConnectionError(ConnectionError)72 void CallbackVisitor::OnConnectionError(ConnectionError /*error*/) {
73   QUICHE_VLOG(1) << "OnConnectionError not implemented";
74 }
75 
OnFrameHeader(Http2StreamId stream_id,size_t length,uint8_t type,uint8_t flags)76 bool CallbackVisitor::OnFrameHeader(Http2StreamId stream_id, size_t length,
77                                     uint8_t type, uint8_t flags) {
78   QUICHE_VLOG(1) << "CallbackVisitor::OnFrameHeader(stream_id=" << stream_id
79                  << ", type=" << int(type) << ", length=" << length
80                  << ", flags=" << int(flags) << ")";
81   if (static_cast<FrameType>(type) == FrameType::CONTINUATION) {
82     if (static_cast<FrameType>(current_frame_.hd.type) != FrameType::HEADERS ||
83         current_frame_.hd.stream_id == 0 ||
84         current_frame_.hd.stream_id != stream_id) {
85       // CONTINUATION frames must follow HEADERS on the same stream. If no
86       // frames have been received, the type is initialized to zero, and the
87       // comparison will fail.
88       return false;
89     }
90     current_frame_.hd.length += length;
91     current_frame_.hd.flags |= flags;
92     QUICHE_DLOG_IF(ERROR, length == 0) << "Empty CONTINUATION!";
93     // Still need to deliver the CONTINUATION to the begin frame callback.
94     nghttp2_frame_hd hd;
95     memset(&hd, 0, sizeof(hd));
96     hd.stream_id = stream_id;
97     hd.length = length;
98     hd.type = type;
99     hd.flags = flags;
100     if (callbacks_->on_begin_frame_callback) {
101       const int result =
102           callbacks_->on_begin_frame_callback(nullptr, &hd, user_data_);
103       return result == 0;
104     }
105     return true;
106   }
107   // The general strategy is to clear |current_frame_| at the start of a new
108   // frame, accumulate frame information from the various callback events, then
109   // invoke the on_frame_recv_callback() with the accumulated frame data.
110   memset(&current_frame_, 0, sizeof(current_frame_));
111   current_frame_.hd.stream_id = stream_id;
112   current_frame_.hd.length = length;
113   current_frame_.hd.type = type;
114   current_frame_.hd.flags = flags;
115   if (callbacks_->on_begin_frame_callback) {
116     const int result = callbacks_->on_begin_frame_callback(
117         nullptr, &current_frame_.hd, user_data_);
118     return result == 0;
119   }
120   return true;
121 }
122 
OnSettingsStart()123 void CallbackVisitor::OnSettingsStart() {}
124 
OnSetting(Http2Setting setting)125 void CallbackVisitor::OnSetting(Http2Setting setting) {
126   settings_.push_back({setting.id, setting.value});
127 }
128 
OnSettingsEnd()129 void CallbackVisitor::OnSettingsEnd() {
130   current_frame_.settings.niv = settings_.size();
131   current_frame_.settings.iv = settings_.data();
132   QUICHE_VLOG(1) << "OnSettingsEnd, received settings of size "
133                  << current_frame_.settings.niv;
134   if (callbacks_->on_frame_recv_callback) {
135     const int result = callbacks_->on_frame_recv_callback(
136         nullptr, &current_frame_, user_data_);
137     QUICHE_DCHECK_EQ(0, result);
138   }
139   settings_.clear();
140 }
141 
OnSettingsAck()142 void CallbackVisitor::OnSettingsAck() {
143   // ACK is part of the flags, which were set in OnFrameHeader().
144   QUICHE_VLOG(1) << "OnSettingsAck()";
145   if (callbacks_->on_frame_recv_callback) {
146     const int result = callbacks_->on_frame_recv_callback(
147         nullptr, &current_frame_, user_data_);
148     QUICHE_DCHECK_EQ(0, result);
149   }
150 }
151 
OnBeginHeadersForStream(Http2StreamId stream_id)152 bool CallbackVisitor::OnBeginHeadersForStream(Http2StreamId stream_id) {
153   auto it = GetStreamInfo(stream_id);
154   if (it->second.received_headers) {
155     // At least one headers frame has already been received.
156     QUICHE_VLOG(1)
157         << "Headers already received for stream " << stream_id
158         << ", these are trailers or headers following a 100 response";
159     current_frame_.headers.cat = NGHTTP2_HCAT_HEADERS;
160   } else {
161     switch (perspective_) {
162       case Perspective::kClient:
163         QUICHE_VLOG(1) << "First headers at the client for stream " << stream_id
164                        << "; these are response headers";
165         current_frame_.headers.cat = NGHTTP2_HCAT_RESPONSE;
166         break;
167       case Perspective::kServer:
168         QUICHE_VLOG(1) << "First headers at the server for stream " << stream_id
169                        << "; these are request headers";
170         current_frame_.headers.cat = NGHTTP2_HCAT_REQUEST;
171         break;
172     }
173   }
174   it->second.received_headers = true;
175   if (callbacks_->on_begin_headers_callback) {
176     const int result = callbacks_->on_begin_headers_callback(
177         nullptr, &current_frame_, user_data_);
178     return result == 0;
179   }
180   return true;
181 }
182 
OnHeaderForStream(Http2StreamId stream_id,absl::string_view name,absl::string_view value)183 Http2VisitorInterface::OnHeaderResult CallbackVisitor::OnHeaderForStream(
184     Http2StreamId stream_id, absl::string_view name, absl::string_view value) {
185   QUICHE_VLOG(2) << "OnHeaderForStream(stream_id=" << stream_id << ", name=["
186                  << absl::CEscape(name) << "], value=[" << absl::CEscape(value)
187                  << "])";
188   if (callbacks_->on_header_callback) {
189     const int result = callbacks_->on_header_callback(
190         nullptr, &current_frame_, ToUint8Ptr(name.data()), name.size(),
191         ToUint8Ptr(value.data()), value.size(), NGHTTP2_NV_FLAG_NONE,
192         user_data_);
193     if (result == 0) {
194       return HEADER_OK;
195     } else if (result == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
196       return HEADER_RST_STREAM;
197     } else {
198       // Assume NGHTTP2_ERR_CALLBACK_FAILURE.
199       return HEADER_CONNECTION_ERROR;
200     }
201   }
202   return HEADER_OK;
203 }
204 
OnEndHeadersForStream(Http2StreamId stream_id)205 bool CallbackVisitor::OnEndHeadersForStream(Http2StreamId stream_id) {
206   QUICHE_VLOG(1) << "OnEndHeadersForStream(stream_id=" << stream_id << ")";
207   if (callbacks_->on_frame_recv_callback) {
208     const int result = callbacks_->on_frame_recv_callback(
209         nullptr, &current_frame_, user_data_);
210     return result == 0;
211   }
212   return true;
213 }
214 
OnDataPaddingLength(Http2StreamId,size_t padding_length)215 bool CallbackVisitor::OnDataPaddingLength(Http2StreamId /*stream_id*/,
216                                           size_t padding_length) {
217   QUICHE_DCHECK_GE(remaining_data_, padding_length);
218   current_frame_.data.padlen = padding_length;
219   remaining_data_ -= padding_length;
220   if (remaining_data_ == 0 &&
221       (current_frame_.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0 &&
222       callbacks_->on_frame_recv_callback != nullptr) {
223     const int result = callbacks_->on_frame_recv_callback(
224         nullptr, &current_frame_, user_data_);
225     return result == 0;
226   }
227   return true;
228 }
229 
OnBeginDataForStream(Http2StreamId,size_t payload_length)230 bool CallbackVisitor::OnBeginDataForStream(Http2StreamId /*stream_id*/,
231                                            size_t payload_length) {
232   remaining_data_ = payload_length;
233   if (remaining_data_ == 0 &&
234       (current_frame_.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0 &&
235       callbacks_->on_frame_recv_callback != nullptr) {
236     const int result = callbacks_->on_frame_recv_callback(
237         nullptr, &current_frame_, user_data_);
238     return result == 0;
239   }
240   return true;
241 }
242 
OnDataForStream(Http2StreamId stream_id,absl::string_view data)243 bool CallbackVisitor::OnDataForStream(Http2StreamId stream_id,
244                                       absl::string_view data) {
245   QUICHE_VLOG(1) << "OnDataForStream(stream_id=" << stream_id
246                  << ", data.size()=" << data.size() << ")";
247   int result = 0;
248   if (callbacks_->on_data_chunk_recv_callback) {
249     result = callbacks_->on_data_chunk_recv_callback(
250         nullptr, current_frame_.hd.flags, stream_id, ToUint8Ptr(data.data()),
251         data.size(), user_data_);
252   }
253   remaining_data_ -= data.size();
254   if (result == 0 && remaining_data_ == 0 &&
255       (current_frame_.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0 &&
256       callbacks_->on_frame_recv_callback) {
257     // If the DATA frame contains the END_STREAM flag, `on_frame_recv` is
258     // invoked later.
259     result = callbacks_->on_frame_recv_callback(nullptr, &current_frame_,
260                                                 user_data_);
261   }
262   return result == 0;
263 }
264 
OnEndStream(Http2StreamId stream_id)265 bool CallbackVisitor::OnEndStream(Http2StreamId stream_id) {
266   QUICHE_VLOG(1) << "OnEndStream(stream_id=" << stream_id << ")";
267   int result = 0;
268   if (static_cast<FrameType>(current_frame_.hd.type) == FrameType::DATA &&
269       (current_frame_.hd.flags & NGHTTP2_FLAG_END_STREAM) != 0 &&
270       callbacks_->on_frame_recv_callback) {
271     // `on_frame_recv` is invoked here to ensure that the Http2Adapter
272     // implementation has successfully validated and processed the entire DATA
273     // frame.
274     result = callbacks_->on_frame_recv_callback(nullptr, &current_frame_,
275                                                 user_data_);
276   }
277   return result == 0;
278 }
279 
OnRstStream(Http2StreamId stream_id,Http2ErrorCode error_code)280 void CallbackVisitor::OnRstStream(Http2StreamId stream_id,
281                                   Http2ErrorCode error_code) {
282   QUICHE_VLOG(1) << "OnRstStream(stream_id=" << stream_id
283                  << ", error_code=" << static_cast<int>(error_code) << ")";
284   current_frame_.rst_stream.error_code = static_cast<uint32_t>(error_code);
285   if (callbacks_->on_frame_recv_callback) {
286     const int result = callbacks_->on_frame_recv_callback(
287         nullptr, &current_frame_, user_data_);
288     QUICHE_DCHECK_EQ(0, result);
289   }
290 }
291 
OnCloseStream(Http2StreamId stream_id,Http2ErrorCode error_code)292 bool CallbackVisitor::OnCloseStream(Http2StreamId stream_id,
293                                     Http2ErrorCode error_code) {
294   QUICHE_VLOG(1) << "OnCloseStream(stream_id=" << stream_id
295                  << ", error_code=" << static_cast<int>(error_code) << ")";
296   int result = 0;
297   if (callbacks_->on_stream_close_callback) {
298     result = callbacks_->on_stream_close_callback(
299         nullptr, stream_id, static_cast<uint32_t>(error_code), user_data_);
300   }
301   stream_map_.erase(stream_id);
302   if (stream_close_listener_) {
303     stream_close_listener_(stream_id);
304   }
305   return result == 0;
306 }
307 
OnPriorityForStream(Http2StreamId,Http2StreamId parent_stream_id,int weight,bool exclusive)308 void CallbackVisitor::OnPriorityForStream(Http2StreamId /*stream_id*/,
309                                           Http2StreamId parent_stream_id,
310                                           int weight, bool exclusive) {
311   current_frame_.priority.pri_spec.stream_id = parent_stream_id;
312   current_frame_.priority.pri_spec.weight = weight;
313   current_frame_.priority.pri_spec.exclusive = exclusive;
314   if (callbacks_->on_frame_recv_callback) {
315     const int result = callbacks_->on_frame_recv_callback(
316         nullptr, &current_frame_, user_data_);
317     QUICHE_DCHECK_EQ(0, result);
318   }
319 }
320 
OnPing(Http2PingId ping_id,bool is_ack)321 void CallbackVisitor::OnPing(Http2PingId ping_id, bool is_ack) {
322   QUICHE_VLOG(1) << "OnPing(ping_id=" << static_cast<int64_t>(ping_id)
323                  << ", is_ack=" << is_ack << ")";
324   uint64_t network_order_opaque_data =
325       quiche::QuicheEndian::HostToNet64(ping_id);
326   std::memcpy(current_frame_.ping.opaque_data, &network_order_opaque_data,
327               sizeof(network_order_opaque_data));
328   if (callbacks_->on_frame_recv_callback) {
329     const int result = callbacks_->on_frame_recv_callback(
330         nullptr, &current_frame_, user_data_);
331     QUICHE_DCHECK_EQ(0, result);
332   }
333 }
334 
OnPushPromiseForStream(Http2StreamId,Http2StreamId)335 void CallbackVisitor::OnPushPromiseForStream(
336     Http2StreamId /*stream_id*/, Http2StreamId /*promised_stream_id*/) {
337   QUICHE_LOG(DFATAL) << "Not implemented";
338 }
339 
OnGoAway(Http2StreamId last_accepted_stream_id,Http2ErrorCode error_code,absl::string_view opaque_data)340 bool CallbackVisitor::OnGoAway(Http2StreamId last_accepted_stream_id,
341                                Http2ErrorCode error_code,
342                                absl::string_view opaque_data) {
343   QUICHE_VLOG(1) << "OnGoAway(last_accepted_stream_id="
344                  << last_accepted_stream_id
345                  << ", error_code=" << static_cast<int>(error_code)
346                  << ", opaque_data=[" << absl::CEscape(opaque_data) << "])";
347   current_frame_.goaway.last_stream_id = last_accepted_stream_id;
348   current_frame_.goaway.error_code = static_cast<uint32_t>(error_code);
349   current_frame_.goaway.opaque_data = ToUint8Ptr(opaque_data.data());
350   current_frame_.goaway.opaque_data_len = opaque_data.size();
351   if (callbacks_->on_frame_recv_callback) {
352     const int result = callbacks_->on_frame_recv_callback(
353         nullptr, &current_frame_, user_data_);
354     return result == 0;
355   }
356   return true;
357 }
358 
OnWindowUpdate(Http2StreamId stream_id,int window_increment)359 void CallbackVisitor::OnWindowUpdate(Http2StreamId stream_id,
360                                      int window_increment) {
361   QUICHE_VLOG(1) << "OnWindowUpdate(stream_id=" << stream_id
362                  << ", delta=" << window_increment << ")";
363   current_frame_.window_update.window_size_increment = window_increment;
364   if (callbacks_->on_frame_recv_callback) {
365     const int result = callbacks_->on_frame_recv_callback(
366         nullptr, &current_frame_, user_data_);
367     QUICHE_DCHECK_EQ(0, result);
368   }
369 }
370 
PopulateFrame(nghttp2_frame & frame,uint8_t frame_type,Http2StreamId stream_id,size_t length,uint8_t flags,uint32_t error_code,bool sent_headers)371 void CallbackVisitor::PopulateFrame(nghttp2_frame& frame, uint8_t frame_type,
372                                     Http2StreamId stream_id, size_t length,
373                                     uint8_t flags, uint32_t error_code,
374                                     bool sent_headers) {
375   frame.hd.type = frame_type;
376   frame.hd.stream_id = stream_id;
377   frame.hd.length = length;
378   frame.hd.flags = flags;
379   const FrameType frame_type_enum = static_cast<FrameType>(frame_type);
380   if (frame_type_enum == FrameType::HEADERS) {
381     if (sent_headers) {
382       frame.headers.cat = NGHTTP2_HCAT_HEADERS;
383     } else {
384       switch (perspective_) {
385         case Perspective::kClient:
386           QUICHE_VLOG(1) << "First headers sent by the client for stream "
387                          << stream_id << "; these are request headers";
388           frame.headers.cat = NGHTTP2_HCAT_REQUEST;
389           break;
390         case Perspective::kServer:
391           QUICHE_VLOG(1) << "First headers sent by the server for stream "
392                          << stream_id << "; these are response headers";
393           frame.headers.cat = NGHTTP2_HCAT_RESPONSE;
394           break;
395       }
396     }
397   } else if (frame_type_enum == FrameType::RST_STREAM) {
398     frame.rst_stream.error_code = error_code;
399   } else if (frame_type_enum == FrameType::GOAWAY) {
400     frame.goaway.error_code = error_code;
401   }
402 }
403 
OnBeforeFrameSent(uint8_t frame_type,Http2StreamId stream_id,size_t length,uint8_t flags)404 int CallbackVisitor::OnBeforeFrameSent(uint8_t frame_type,
405                                        Http2StreamId stream_id, size_t length,
406                                        uint8_t flags) {
407   QUICHE_VLOG(1) << "OnBeforeFrameSent(stream_id=" << stream_id
408                  << ", type=" << int(frame_type) << ", length=" << length
409                  << ", flags=" << int(flags) << ")";
410   if (callbacks_->before_frame_send_callback) {
411     nghttp2_frame frame;
412     auto it = GetStreamInfo(stream_id);
413     // The implementation of the before_frame_send_callback doesn't look at the
414     // error code, so for now it's populated with 0.
415     PopulateFrame(frame, frame_type, stream_id, length, flags, /*error_code=*/0,
416                   it->second.before_sent_headers);
417     it->second.before_sent_headers = true;
418     return callbacks_->before_frame_send_callback(nullptr, &frame, user_data_);
419   }
420   return 0;
421 }
422 
OnFrameSent(uint8_t frame_type,Http2StreamId stream_id,size_t length,uint8_t flags,uint32_t error_code)423 int CallbackVisitor::OnFrameSent(uint8_t frame_type, Http2StreamId stream_id,
424                                  size_t length, uint8_t flags,
425                                  uint32_t error_code) {
426   QUICHE_VLOG(1) << "OnFrameSent(stream_id=" << stream_id
427                  << ", type=" << int(frame_type) << ", length=" << length
428                  << ", flags=" << int(flags) << ", error_code=" << error_code
429                  << ")";
430   if (callbacks_->on_frame_send_callback) {
431     nghttp2_frame frame;
432     auto it = GetStreamInfo(stream_id);
433     PopulateFrame(frame, frame_type, stream_id, length, flags, error_code,
434                   it->second.sent_headers);
435     it->second.sent_headers = true;
436     return callbacks_->on_frame_send_callback(nullptr, &frame, user_data_);
437   }
438   return 0;
439 }
440 
OnInvalidFrame(Http2StreamId stream_id,InvalidFrameError error)441 bool CallbackVisitor::OnInvalidFrame(Http2StreamId stream_id,
442                                      InvalidFrameError error) {
443   QUICHE_VLOG(1) << "OnInvalidFrame(" << stream_id << ", "
444                  << InvalidFrameErrorToString(error) << ")";
445   QUICHE_DCHECK_EQ(stream_id, current_frame_.hd.stream_id);
446   if (callbacks_->on_invalid_frame_recv_callback) {
447     return 0 ==
448            callbacks_->on_invalid_frame_recv_callback(
449                nullptr, &current_frame_, ToNgHttp2ErrorCode(error), user_data_);
450   }
451   return true;
452 }
453 
OnBeginMetadataForStream(Http2StreamId stream_id,size_t payload_length)454 void CallbackVisitor::OnBeginMetadataForStream(Http2StreamId stream_id,
455                                                size_t payload_length) {
456   QUICHE_VLOG(1) << "OnBeginMetadataForStream(stream_id=" << stream_id
457                  << ", payload_length=" << payload_length << ")";
458 }
459 
OnMetadataForStream(Http2StreamId stream_id,absl::string_view metadata)460 bool CallbackVisitor::OnMetadataForStream(Http2StreamId stream_id,
461                                           absl::string_view metadata) {
462   QUICHE_VLOG(1) << "OnMetadataForStream(stream_id=" << stream_id
463                  << ", len=" << metadata.size() << ")";
464   if (callbacks_->on_extension_chunk_recv_callback) {
465     int result = callbacks_->on_extension_chunk_recv_callback(
466         nullptr, &current_frame_.hd, ToUint8Ptr(metadata.data()),
467         metadata.size(), user_data_);
468     return result == 0;
469   }
470   return true;
471 }
472 
OnMetadataEndForStream(Http2StreamId stream_id)473 bool CallbackVisitor::OnMetadataEndForStream(Http2StreamId stream_id) {
474   QUICHE_LOG_IF(DFATAL, current_frame_.hd.flags != kMetadataEndFlag);
475   QUICHE_VLOG(1) << "OnMetadataEndForStream(stream_id=" << stream_id << ")";
476   if (callbacks_->unpack_extension_callback) {
477     void* payload;
478     int result = callbacks_->unpack_extension_callback(
479         nullptr, &payload, &current_frame_.hd, user_data_);
480     if (result == 0 && callbacks_->on_frame_recv_callback) {
481       current_frame_.ext.payload = payload;
482       result = callbacks_->on_frame_recv_callback(nullptr, &current_frame_,
483                                                   user_data_);
484     }
485     return (result == 0);
486   }
487   return true;
488 }
489 
OnErrorDebug(absl::string_view message)490 void CallbackVisitor::OnErrorDebug(absl::string_view message) {
491   QUICHE_VLOG(1) << "OnErrorDebug(message=[" << absl::CEscape(message) << "])";
492   if (callbacks_->error_callback2) {
493     callbacks_->error_callback2(nullptr, -1, message.data(), message.size(),
494                                 user_data_);
495   }
496 }
497 
GetStreamInfo(Http2StreamId stream_id)498 CallbackVisitor::StreamInfoMap::iterator CallbackVisitor::GetStreamInfo(
499     Http2StreamId stream_id) {
500   auto it = stream_map_.find(stream_id);
501   if (it == stream_map_.end()) {
502     auto p = stream_map_.insert({stream_id, {}});
503     it = p.first;
504   }
505   return it;
506 }
507 
508 }  // namespace adapter
509 }  // namespace http2
510