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