xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/http2/adapter/nghttp2_callbacks.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 #include "quiche/http2/adapter/nghttp2_callbacks.h"
2 
3 #include <cstdint>
4 #include <cstring>
5 
6 #include "absl/strings/string_view.h"
7 #include "quiche/http2/adapter/data_source.h"
8 #include "quiche/http2/adapter/http2_protocol.h"
9 #include "quiche/http2/adapter/http2_visitor_interface.h"
10 #include "quiche/http2/adapter/nghttp2_data_provider.h"
11 #include "quiche/http2/adapter/nghttp2_util.h"
12 #include "quiche/common/platform/api/quiche_bug_tracker.h"
13 #include "quiche/common/platform/api/quiche_logging.h"
14 #include "quiche/common/quiche_endian.h"
15 
16 namespace http2 {
17 namespace adapter {
18 namespace callbacks {
19 
OnReadyToSend(nghttp2_session *,const uint8_t * data,size_t length,int flags,void * user_data)20 ssize_t OnReadyToSend(nghttp2_session* /* session */, const uint8_t* data,
21                       size_t length, int flags, void* user_data) {
22   QUICHE_CHECK_NE(user_data, nullptr);
23   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
24   const int64_t result = visitor->OnReadyToSend(ToStringView(data, length));
25   QUICHE_VLOG(1) << "callbacks::OnReadyToSend(length=" << length
26                  << ", flags=" << flags << ") returning " << result;
27   if (result > 0) {
28     return result;
29   } else if (result == Http2VisitorInterface::kSendBlocked) {
30     return -504;  // NGHTTP2_ERR_WOULDBLOCK
31   } else {
32     return -902;  // NGHTTP2_ERR_CALLBACK_FAILURE
33   }
34 }
35 
OnBeginFrame(nghttp2_session *,const nghttp2_frame_hd * header,void * user_data)36 int OnBeginFrame(nghttp2_session* /* session */, const nghttp2_frame_hd* header,
37                  void* user_data) {
38   QUICHE_VLOG(1) << "callbacks::OnBeginFrame(stream_id=" << header->stream_id
39                  << ", type=" << int(header->type)
40                  << ", length=" << header->length
41                  << ", flags=" << int(header->flags) << ")";
42   QUICHE_CHECK_NE(user_data, nullptr);
43   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
44   bool result = visitor->OnFrameHeader(header->stream_id, header->length,
45                                        header->type, header->flags);
46   if (!result) {
47     return NGHTTP2_ERR_CALLBACK_FAILURE;
48   }
49   if (header->type == NGHTTP2_DATA) {
50     result = visitor->OnBeginDataForStream(header->stream_id, header->length);
51   } else if (header->type == kMetadataFrameType) {
52     visitor->OnBeginMetadataForStream(header->stream_id, header->length);
53   }
54   return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
55 }
56 
OnFrameReceived(nghttp2_session *,const nghttp2_frame * frame,void * user_data)57 int OnFrameReceived(nghttp2_session* /* session */, const nghttp2_frame* frame,
58                     void* user_data) {
59   QUICHE_VLOG(1) << "callbacks::OnFrameReceived(stream_id="
60                  << frame->hd.stream_id << ", type=" << int(frame->hd.type)
61                  << ", length=" << frame->hd.length
62                  << ", flags=" << int(frame->hd.flags) << ")";
63   QUICHE_CHECK_NE(user_data, nullptr);
64   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
65   const Http2StreamId stream_id = frame->hd.stream_id;
66   switch (frame->hd.type) {
67     // The beginning of the DATA frame is handled in OnBeginFrame(), and the
68     // beginning of the header block is handled in client/server-specific
69     // callbacks. This callback handles the point at which the entire logical
70     // frame has been received and processed.
71     case NGHTTP2_DATA:
72       if ((frame->hd.flags & NGHTTP2_FLAG_PADDED) != 0) {
73         visitor->OnDataPaddingLength(stream_id, frame->data.padlen);
74       }
75       if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
76         const bool result = visitor->OnEndStream(stream_id);
77         if (!result) {
78           return NGHTTP2_ERR_CALLBACK_FAILURE;
79         }
80       }
81       break;
82     case NGHTTP2_HEADERS: {
83       if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) {
84         const bool result = visitor->OnEndHeadersForStream(stream_id);
85         if (!result) {
86           return NGHTTP2_ERR_CALLBACK_FAILURE;
87         }
88       }
89       if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
90         const bool result = visitor->OnEndStream(stream_id);
91         if (!result) {
92           return NGHTTP2_ERR_CALLBACK_FAILURE;
93         }
94       }
95       break;
96     }
97     case NGHTTP2_PRIORITY: {
98       nghttp2_priority_spec priority_spec = frame->priority.pri_spec;
99       visitor->OnPriorityForStream(stream_id, priority_spec.stream_id,
100                                    priority_spec.weight,
101                                    priority_spec.exclusive != 0);
102       break;
103     }
104     case NGHTTP2_RST_STREAM: {
105       visitor->OnRstStream(stream_id,
106                            ToHttp2ErrorCode(frame->rst_stream.error_code));
107       break;
108     }
109     case NGHTTP2_SETTINGS:
110       if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
111         visitor->OnSettingsAck();
112       } else {
113         visitor->OnSettingsStart();
114         for (size_t i = 0; i < frame->settings.niv; ++i) {
115           nghttp2_settings_entry entry = frame->settings.iv[i];
116           // The nghttp2_settings_entry uses int32_t for the ID; we must cast.
117           visitor->OnSetting(Http2Setting{
118               static_cast<Http2SettingsId>(entry.settings_id), entry.value});
119         }
120         visitor->OnSettingsEnd();
121       }
122       break;
123     case NGHTTP2_PUSH_PROMISE:
124       // This case is handled by headers-related callbacks:
125       //   1. visitor->OnPushPromiseForStream() is invoked in the client-side
126       //      OnHeadersStart() adapter callback, as nghttp2 only allows clients
127       //      to receive PUSH_PROMISE frames.
128       //   2. visitor->OnHeaderForStream() is invoked for each server push
129       //      request header in the PUSH_PROMISE header block.
130       //   3. This switch statement is reached once all server push request
131       //      headers have been parsed.
132       break;
133     case NGHTTP2_PING: {
134       Http2PingId ping_id;
135       std::memcpy(&ping_id, frame->ping.opaque_data, sizeof(Http2PingId));
136       visitor->OnPing(quiche::QuicheEndian::NetToHost64(ping_id),
137                       (frame->hd.flags & NGHTTP2_FLAG_ACK) != 0);
138       break;
139     }
140     case NGHTTP2_GOAWAY: {
141       absl::string_view opaque_data(
142           reinterpret_cast<const char*>(frame->goaway.opaque_data),
143           frame->goaway.opaque_data_len);
144       const bool result = visitor->OnGoAway(
145           frame->goaway.last_stream_id,
146           ToHttp2ErrorCode(frame->goaway.error_code), opaque_data);
147       if (!result) {
148         return NGHTTP2_ERR_CALLBACK_FAILURE;
149       }
150       break;
151     }
152     case NGHTTP2_WINDOW_UPDATE: {
153       visitor->OnWindowUpdate(stream_id,
154                               frame->window_update.window_size_increment);
155       break;
156     }
157     case NGHTTP2_CONTINUATION:
158       // This frame type should not be passed to any callbacks, according to
159       // https://nghttp2.org/documentation/enums.html#c.NGHTTP2_CONTINUATION.
160       QUICHE_LOG(ERROR) << "Unexpected receipt of NGHTTP2_CONTINUATION type!";
161       break;
162     case NGHTTP2_ALTSVC:
163       break;
164     case NGHTTP2_ORIGIN:
165       break;
166   }
167 
168   return 0;
169 }
170 
OnBeginHeaders(nghttp2_session *,const nghttp2_frame * frame,void * user_data)171 int OnBeginHeaders(nghttp2_session* /* session */, const nghttp2_frame* frame,
172                    void* user_data) {
173   QUICHE_VLOG(1) << "callbacks::OnBeginHeaders(stream_id="
174                  << frame->hd.stream_id << ")";
175   QUICHE_CHECK_NE(user_data, nullptr);
176   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
177   const bool result = visitor->OnBeginHeadersForStream(frame->hd.stream_id);
178   return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
179 }
180 
OnHeader(nghttp2_session *,const nghttp2_frame * frame,nghttp2_rcbuf * name,nghttp2_rcbuf * value,uint8_t,void * user_data)181 int OnHeader(nghttp2_session* /* session */, const nghttp2_frame* frame,
182              nghttp2_rcbuf* name, nghttp2_rcbuf* value, uint8_t /*flags*/,
183              void* user_data) {
184   QUICHE_VLOG(2) << "callbacks::OnHeader(stream_id=" << frame->hd.stream_id
185                  << ", name=[" << absl::CEscape(ToStringView(name))
186                  << "], value=[" << absl::CEscape(ToStringView(value)) << "])";
187   QUICHE_CHECK_NE(user_data, nullptr);
188   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
189   const Http2VisitorInterface::OnHeaderResult result =
190       visitor->OnHeaderForStream(frame->hd.stream_id, ToStringView(name),
191                                  ToStringView(value));
192   switch (result) {
193     case Http2VisitorInterface::HEADER_OK:
194       return 0;
195     case Http2VisitorInterface::HEADER_CONNECTION_ERROR:
196     case Http2VisitorInterface::HEADER_COMPRESSION_ERROR:
197       return NGHTTP2_ERR_CALLBACK_FAILURE;
198     case Http2VisitorInterface::HEADER_RST_STREAM:
199     case Http2VisitorInterface::HEADER_FIELD_INVALID:
200     case Http2VisitorInterface::HEADER_HTTP_MESSAGING:
201       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
202   }
203   // Unexpected value.
204   return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
205 }
206 
OnBeforeFrameSent(nghttp2_session *,const nghttp2_frame * frame,void * user_data)207 int OnBeforeFrameSent(nghttp2_session* /* session */,
208                       const nghttp2_frame* frame, void* user_data) {
209   QUICHE_VLOG(1) << "callbacks::OnBeforeFrameSent(stream_id="
210                  << frame->hd.stream_id << ", type=" << int(frame->hd.type)
211                  << ", length=" << frame->hd.length
212                  << ", flags=" << int(frame->hd.flags) << ")";
213   QUICHE_CHECK_NE(user_data, nullptr);
214   LogBeforeSend(*frame);
215   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
216   return visitor->OnBeforeFrameSent(frame->hd.type, frame->hd.stream_id,
217                                     frame->hd.length, frame->hd.flags);
218 }
219 
OnFrameSent(nghttp2_session *,const nghttp2_frame * frame,void * user_data)220 int OnFrameSent(nghttp2_session* /* session */, const nghttp2_frame* frame,
221                 void* user_data) {
222   QUICHE_VLOG(1) << "callbacks::OnFrameSent(stream_id=" << frame->hd.stream_id
223                  << ", type=" << int(frame->hd.type)
224                  << ", length=" << frame->hd.length
225                  << ", flags=" << int(frame->hd.flags) << ")";
226   QUICHE_CHECK_NE(user_data, nullptr);
227   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
228   uint32_t error_code = 0;
229   if (frame->hd.type == NGHTTP2_RST_STREAM) {
230     error_code = frame->rst_stream.error_code;
231   } else if (frame->hd.type == NGHTTP2_GOAWAY) {
232     error_code = frame->goaway.error_code;
233   }
234   return visitor->OnFrameSent(frame->hd.type, frame->hd.stream_id,
235                               frame->hd.length, frame->hd.flags, error_code);
236 }
237 
OnFrameNotSent(nghttp2_session *,const nghttp2_frame * frame,int,void *)238 int OnFrameNotSent(nghttp2_session* /* session */, const nghttp2_frame* frame,
239                    int /* lib_error_code */, void* /* user_data */) {
240   QUICHE_VLOG(1) << "callbacks::OnFrameNotSent(stream_id="
241                  << frame->hd.stream_id << ", type=" << int(frame->hd.type)
242                  << ", length=" << frame->hd.length
243                  << ", flags=" << int(frame->hd.flags) << ")";
244   if (frame->hd.type == kMetadataFrameType) {
245     auto* source = static_cast<MetadataSource*>(frame->ext.payload);
246     if (source == nullptr) {
247       QUICHE_BUG(not_sent_payload_is_nullptr)
248           << "Extension frame payload for stream " << frame->hd.stream_id
249           << " is null!";
250     } else {
251       source->OnFailure();
252     }
253   }
254   return 0;
255 }
256 
OnInvalidFrameReceived(nghttp2_session *,const nghttp2_frame * frame,int lib_error_code,void * user_data)257 int OnInvalidFrameReceived(nghttp2_session* /* session */,
258                            const nghttp2_frame* frame, int lib_error_code,
259                            void* user_data) {
260   QUICHE_VLOG(1) << "callbacks::OnInvalidFrameReceived(stream_id="
261                  << frame->hd.stream_id << ", InvalidFrameError="
262                  << int(ToInvalidFrameError(lib_error_code)) << ")";
263   QUICHE_CHECK_NE(user_data, nullptr);
264   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
265   const bool result = visitor->OnInvalidFrame(
266       frame->hd.stream_id, ToInvalidFrameError(lib_error_code));
267   return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
268 }
269 
OnDataChunk(nghttp2_session *,uint8_t,Http2StreamId stream_id,const uint8_t * data,size_t len,void * user_data)270 int OnDataChunk(nghttp2_session* /* session */, uint8_t /*flags*/,
271                 Http2StreamId stream_id, const uint8_t* data, size_t len,
272                 void* user_data) {
273   QUICHE_VLOG(1) << "callbacks::OnDataChunk(stream_id=" << stream_id
274                  << ", length=" << len << ")";
275   QUICHE_CHECK_NE(user_data, nullptr);
276   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
277   const bool result = visitor->OnDataForStream(
278       stream_id, absl::string_view(reinterpret_cast<const char*>(data), len));
279   return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
280 }
281 
OnStreamClosed(nghttp2_session *,Http2StreamId stream_id,uint32_t error_code,void * user_data)282 int OnStreamClosed(nghttp2_session* /* session */, Http2StreamId stream_id,
283                    uint32_t error_code, void* user_data) {
284   QUICHE_VLOG(1) << "callbacks::OnStreamClosed(stream_id=" << stream_id
285                  << ", error_code=" << error_code << ")";
286   QUICHE_CHECK_NE(user_data, nullptr);
287   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
288   const bool result =
289       visitor->OnCloseStream(stream_id, ToHttp2ErrorCode(error_code));
290   return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
291 }
292 
OnExtensionChunkReceived(nghttp2_session *,const nghttp2_frame_hd * hd,const uint8_t * data,size_t len,void * user_data)293 int OnExtensionChunkReceived(nghttp2_session* /*session*/,
294                              const nghttp2_frame_hd* hd, const uint8_t* data,
295                              size_t len, void* user_data) {
296   QUICHE_CHECK_NE(user_data, nullptr);
297   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
298   if (hd->type != kMetadataFrameType) {
299     QUICHE_LOG(ERROR) << "Unexpected frame type: "
300                       << static_cast<int>(hd->type);
301     return NGHTTP2_ERR_CANCEL;
302   }
303   const bool result =
304       visitor->OnMetadataForStream(hd->stream_id, ToStringView(data, len));
305   return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
306 }
307 
OnUnpackExtensionCallback(nghttp2_session *,void **,const nghttp2_frame_hd * hd,void * user_data)308 int OnUnpackExtensionCallback(nghttp2_session* /*session*/, void** /*payload*/,
309                               const nghttp2_frame_hd* hd, void* user_data) {
310   QUICHE_CHECK_NE(user_data, nullptr);
311   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
312   if (hd->flags == kMetadataEndFlag) {
313     const bool result = visitor->OnMetadataEndForStream(hd->stream_id);
314     if (!result) {
315       return NGHTTP2_ERR_CALLBACK_FAILURE;
316     }
317   }
318   return 0;
319 }
320 
OnPackExtensionCallback(nghttp2_session *,uint8_t * buf,size_t len,const nghttp2_frame * frame,void * user_data)321 ssize_t OnPackExtensionCallback(nghttp2_session* /*session*/, uint8_t* buf,
322                                 size_t len, const nghttp2_frame* frame,
323                                 void* user_data) {
324   QUICHE_CHECK_NE(user_data, nullptr);
325   auto* source = static_cast<MetadataSource*>(frame->ext.payload);
326   if (source == nullptr) {
327     QUICHE_BUG(payload_is_nullptr) << "Extension frame payload for stream "
328                                    << frame->hd.stream_id << " is null!";
329     return NGHTTP2_ERR_CALLBACK_FAILURE;
330   }
331   const std::pair<int64_t, bool> result = source->Pack(buf, len);
332   if (result.first < 0) {
333     return NGHTTP2_ERR_CALLBACK_FAILURE;
334   }
335   const bool end_metadata_flag = (frame->hd.flags & kMetadataEndFlag);
336   QUICHE_LOG_IF(DFATAL, result.second != end_metadata_flag)
337       << "Metadata ends: " << result.second
338       << " has kMetadataEndFlag: " << end_metadata_flag;
339   return result.first;
340 }
341 
OnError(nghttp2_session *,int,const char * msg,size_t len,void * user_data)342 int OnError(nghttp2_session* /*session*/, int /*lib_error_code*/,
343             const char* msg, size_t len, void* user_data) {
344   QUICHE_VLOG(1) << "callbacks::OnError(" << absl::string_view(msg, len) << ")";
345   QUICHE_CHECK_NE(user_data, nullptr);
346   auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
347   visitor->OnErrorDebug(absl::string_view(msg, len));
348   return 0;
349 }
350 
Create()351 nghttp2_session_callbacks_unique_ptr Create() {
352   nghttp2_session_callbacks* callbacks;
353   nghttp2_session_callbacks_new(&callbacks);
354 
355   nghttp2_session_callbacks_set_send_callback(callbacks, &OnReadyToSend);
356   nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks,
357                                                         &OnBeginFrame);
358   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
359                                                        &OnFrameReceived);
360   nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks,
361                                                           &OnBeginHeaders);
362   nghttp2_session_callbacks_set_on_header_callback2(callbacks, &OnHeader);
363   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks,
364                                                             &OnDataChunk);
365   nghttp2_session_callbacks_set_on_stream_close_callback(callbacks,
366                                                          &OnStreamClosed);
367   nghttp2_session_callbacks_set_before_frame_send_callback(callbacks,
368                                                            &OnBeforeFrameSent);
369   nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, &OnFrameSent);
370   nghttp2_session_callbacks_set_on_frame_not_send_callback(callbacks,
371                                                            &OnFrameNotSent);
372   nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
373       callbacks, &OnInvalidFrameReceived);
374   nghttp2_session_callbacks_set_error_callback2(callbacks, &OnError);
375   nghttp2_session_callbacks_set_send_data_callback(
376       callbacks, &DataFrameSourceSendCallback);
377   nghttp2_session_callbacks_set_pack_extension_callback(
378       callbacks, &OnPackExtensionCallback);
379   nghttp2_session_callbacks_set_unpack_extension_callback(
380       callbacks, &OnUnpackExtensionCallback);
381   nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
382       callbacks, &OnExtensionChunkReceived);
383   return MakeCallbacksPtr(callbacks);
384 }
385 
386 }  // namespace callbacks
387 }  // namespace adapter
388 }  // namespace http2
389