blob: c2b64f4cf0dea4b5b84410ea5fcc825280d8b60b [file] [log] [blame]
QUICHE teame3eacca2021-04-06 10:24:33 -07001#include "http2/adapter/nghttp2_callbacks.h"
2
3#include <cstdint>
4#include <cstring>
5
6#include "absl/strings/string_view.h"
7#include "http2/adapter/http2_protocol.h"
8#include "http2/adapter/http2_visitor_interface.h"
QUICHE team1cc835e2021-05-24 07:55:18 -07009#include "http2/adapter/nghttp2_data_provider.h"
QUICHE teame3eacca2021-04-06 10:24:33 -070010#include "http2/adapter/nghttp2_util.h"
11#include "third_party/nghttp2/nghttp2.h"
12#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
13#include "common/platform/api/quiche_logging.h"
14#include "common/quiche_endian.h"
15
16namespace http2 {
17namespace adapter {
QUICHE team6ecbb882021-04-13 13:04:27 -070018namespace callbacks {
QUICHE teame3eacca2021-04-06 10:24:33 -070019
QUICHE teamc82acf12021-05-21 17:54:00 -070020ssize_t OnReadyToSend(nghttp2_session* /* session */,
21 const uint8_t* data,
22 size_t length,
23 int flags,
24 void* user_data) {
QUICHE teamb23b83c2021-06-18 15:43:50 -070025 QUICHE_CHECK_NE(user_data, nullptr);
QUICHE teamc82acf12021-05-21 17:54:00 -070026 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
27 const ssize_t result = visitor->OnReadyToSend(ToStringView(data, length));
28 if (result < 0) {
29 return NGHTTP2_ERR_CALLBACK_FAILURE;
30 } else if (result == 0) {
31 return NGHTTP2_ERR_WOULDBLOCK;
32 } else {
33 return result;
34 }
35}
36
QUICHE teame3eacca2021-04-06 10:24:33 -070037int OnBeginFrame(nghttp2_session* /* session */,
38 const nghttp2_frame_hd* header,
39 void* user_data) {
QUICHE teamb23b83c2021-06-18 15:43:50 -070040 QUICHE_CHECK_NE(user_data, nullptr);
QUICHE teame3eacca2021-04-06 10:24:33 -070041 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
QUICHE team2121bae2021-04-30 13:05:38 -070042 visitor->OnFrameHeader(header->stream_id, header->length, header->type,
43 header->flags);
QUICHE teame3eacca2021-04-06 10:24:33 -070044 if (header->type == NGHTTP2_DATA) {
45 visitor->OnBeginDataForStream(header->stream_id, header->length);
46 }
47 return 0;
48}
49
50int OnFrameReceived(nghttp2_session* /* session */,
51 const nghttp2_frame* frame,
52 void* user_data) {
QUICHE teamb23b83c2021-06-18 15:43:50 -070053 QUICHE_CHECK_NE(user_data, nullptr);
QUICHE teame3eacca2021-04-06 10:24:33 -070054 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
55 const Http2StreamId stream_id = frame->hd.stream_id;
QUICHE teame3eacca2021-04-06 10:24:33 -070056 switch (frame->hd.type) {
57 // The beginning of the DATA frame is handled in OnBeginFrame(), and the
58 // beginning of the header block is handled in client/server-specific
59 // callbacks. This callback handles the point at which the entire logical
60 // frame has been received and processed.
61 case NGHTTP2_DATA:
62 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
63 visitor->OnEndStream(stream_id);
64 }
65 break;
66 case NGHTTP2_HEADERS: {
67 if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) {
68 visitor->OnEndHeadersForStream(stream_id);
69 }
70 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
71 visitor->OnEndStream(stream_id);
72 }
73 break;
74 }
75 case NGHTTP2_PRIORITY: {
76 nghttp2_priority_spec priority_spec = frame->priority.pri_spec;
77 visitor->OnPriorityForStream(stream_id, priority_spec.stream_id,
78 priority_spec.weight,
79 priority_spec.exclusive != 0);
80 break;
81 }
82 case NGHTTP2_RST_STREAM: {
83 visitor->OnRstStream(stream_id,
84 ToHttp2ErrorCode(frame->rst_stream.error_code));
85 break;
86 }
87 case NGHTTP2_SETTINGS:
88 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
89 visitor->OnSettingsAck();
90 } else {
91 visitor->OnSettingsStart();
92 for (int i = 0; i < frame->settings.niv; ++i) {
93 nghttp2_settings_entry entry = frame->settings.iv[i];
94 // The nghttp2_settings_entry uses int32_t for the ID; we must cast.
95 visitor->OnSetting(Http2Setting{
96 .id = static_cast<Http2SettingsId>(entry.settings_id),
97 .value = entry.value});
98 }
99 visitor->OnSettingsEnd();
100 }
101 break;
102 case NGHTTP2_PUSH_PROMISE:
103 // This case is handled by headers-related callbacks:
104 // 1. visitor->OnPushPromiseForStream() is invoked in the client-side
105 // OnHeadersStart() adapter callback, as nghttp2 only allows clients
106 // to receive PUSH_PROMISE frames.
107 // 2. visitor->OnHeaderForStream() is invoked for each server push
108 // request header in the PUSH_PROMISE header block.
109 // 3. This switch statement is reached once all server push request
110 // headers have been parsed.
111 break;
112 case NGHTTP2_PING: {
113 Http2PingId ping_id;
114 std::memcpy(&ping_id, frame->ping.opaque_data, sizeof(Http2PingId));
115 visitor->OnPing(quiche::QuicheEndian::NetToHost64(ping_id),
116 (frame->hd.flags & NGHTTP2_FLAG_ACK) != 0);
117 break;
118 }
119 case NGHTTP2_GOAWAY: {
120 absl::string_view opaque_data(
121 reinterpret_cast<const char*>(frame->goaway.opaque_data),
122 frame->goaway.opaque_data_len);
123 visitor->OnGoAway(frame->goaway.last_stream_id,
124 ToHttp2ErrorCode(frame->goaway.error_code),
125 opaque_data);
126 break;
127 }
128 case NGHTTP2_WINDOW_UPDATE: {
129 visitor->OnWindowUpdate(stream_id,
130 frame->window_update.window_size_increment);
131 break;
132 }
133 case NGHTTP2_CONTINUATION:
134 // This frame type should not be passed to any callbacks, according to
135 // https://nghttp2.org/documentation/enums.html#c.NGHTTP2_CONTINUATION.
136 QUICHE_LOG(ERROR) << "Unexpected receipt of NGHTTP2_CONTINUATION type!";
137 break;
138 case NGHTTP2_ALTSVC:
139 break;
140 case NGHTTP2_ORIGIN:
141 break;
142 }
143
144 return 0;
145}
146
147int OnBeginHeaders(nghttp2_session* /* session */,
148 const nghttp2_frame* frame,
149 void* user_data) {
QUICHE teamb23b83c2021-06-18 15:43:50 -0700150 QUICHE_CHECK_NE(user_data, nullptr);
QUICHE teame3eacca2021-04-06 10:24:33 -0700151 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
152 visitor->OnBeginHeadersForStream(frame->hd.stream_id);
153 return 0;
154}
155
156int OnHeader(nghttp2_session* /* session */,
157 const nghttp2_frame* frame,
158 nghttp2_rcbuf* name,
159 nghttp2_rcbuf* value,
160 uint8_t flags,
161 void* user_data) {
QUICHE teamb23b83c2021-06-18 15:43:50 -0700162 QUICHE_CHECK_NE(user_data, nullptr);
QUICHE teame3eacca2021-04-06 10:24:33 -0700163 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
QUICHE team29e35dd2021-06-28 15:25:35 -0700164 const Http2VisitorInterface::OnHeaderResult result =
165 visitor->OnHeaderForStream(frame->hd.stream_id, ToStringView(name),
166 ToStringView(value));
167 switch (result) {
168 case Http2VisitorInterface::HEADER_OK:
169 return 0;
170 case Http2VisitorInterface::HEADER_CONNECTION_ERROR:
171 return NGHTTP2_ERR_CALLBACK_FAILURE;
172 case Http2VisitorInterface::HEADER_RST_STREAM:
173 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
174 }
QUICHE teame3eacca2021-04-06 10:24:33 -0700175}
176
QUICHE team06606d02021-06-23 10:58:40 -0700177int OnBeforeFrameSent(nghttp2_session* /* session */,
178 const nghttp2_frame* frame, void* user_data) {
179 QUICHE_CHECK_NE(user_data, nullptr);
QUICHE team43d93a02021-06-25 12:32:25 -0700180 LogBeforeSend(*frame);
QUICHE team06606d02021-06-23 10:58:40 -0700181 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
182 return visitor->OnBeforeFrameSent(frame->hd.type, frame->hd.stream_id,
183 frame->hd.length, frame->hd.flags);
184}
185
186int OnFrameSent(nghttp2_session* /* session */, const nghttp2_frame* frame,
187 void* user_data) {
188 QUICHE_CHECK_NE(user_data, nullptr);
189 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
190 uint32_t error_code = 0;
191 if (frame->hd.type == NGHTTP2_RST_STREAM) {
192 error_code = frame->rst_stream.error_code;
193 } else if (frame->hd.type == NGHTTP2_GOAWAY) {
194 error_code = frame->goaway.error_code;
195 }
196 return visitor->OnFrameSent(frame->hd.type, frame->hd.stream_id,
197 frame->hd.length, frame->hd.flags, error_code);
198}
199
QUICHE team70624862021-06-24 10:37:15 -0700200int OnInvalidFrameReceived(nghttp2_session* /* session */,
201 const nghttp2_frame* frame, int lib_error_code,
202 void* user_data) {
203 QUICHE_CHECK_NE(user_data, nullptr);
204 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
205 const bool result =
206 visitor->OnInvalidFrame(frame->hd.stream_id, lib_error_code);
207 return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
208}
209
QUICHE teame3eacca2021-04-06 10:24:33 -0700210int OnDataChunk(nghttp2_session* /* session */,
211 uint8_t flags,
212 Http2StreamId stream_id,
213 const uint8_t* data,
214 size_t len,
215 void* user_data) {
QUICHE teamb23b83c2021-06-18 15:43:50 -0700216 QUICHE_CHECK_NE(user_data, nullptr);
QUICHE teame3eacca2021-04-06 10:24:33 -0700217 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
218 visitor->OnDataForStream(
219 stream_id, absl::string_view(reinterpret_cast<const char*>(data), len));
220 return 0;
221}
222
223int OnStreamClosed(nghttp2_session* /* session */,
224 Http2StreamId stream_id,
225 uint32_t error_code,
226 void* user_data) {
QUICHE teamb23b83c2021-06-18 15:43:50 -0700227 QUICHE_CHECK_NE(user_data, nullptr);
QUICHE teame3eacca2021-04-06 10:24:33 -0700228 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
QUICHE teamd9050872021-04-27 09:12:49 -0700229 visitor->OnCloseStream(stream_id, ToHttp2ErrorCode(error_code));
QUICHE teame3eacca2021-04-06 10:24:33 -0700230 return 0;
231}
232
QUICHE teamf422b222021-06-25 08:55:17 -0700233int OnExtensionChunkReceived(nghttp2_session* session,
234 const nghttp2_frame_hd* hd, const uint8_t* data,
235 size_t len, void* user_data) {
236 QUICHE_CHECK_NE(user_data, nullptr);
237 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
238 if (hd->type != kMetadataFrameType) {
239 QUICHE_LOG(ERROR) << "Unexpected frame type: "
240 << static_cast<int>(hd->type);
241 return NGHTTP2_ERR_CANCEL;
242 }
243 visitor->OnMetadataForStream(hd->stream_id, ToStringView(data, len));
244 return 0;
245}
246
247int OnUnpackExtensionCallback(nghttp2_session* session, void** payload,
248 const nghttp2_frame_hd* hd, void* user_data) {
249 QUICHE_CHECK_NE(user_data, nullptr);
250 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
251 if (hd->flags == kMetadataEndFlag) {
252 const bool result = visitor->OnMetadataEndForStream(hd->stream_id);
253 if (!result) {
254 return NGHTTP2_ERR_CALLBACK_FAILURE;
255 }
256 }
257 return 0;
258}
259
260ssize_t OnPackExtensionCallback(nghttp2_session* session, uint8_t* buf,
261 size_t len, const nghttp2_frame* frame,
262 void* user_data) {
263 QUICHE_CHECK_NE(user_data, nullptr);
264 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
265 ssize_t written = 0;
266 visitor->OnReadyToSendMetadataForStream(
267 frame->hd.stream_id, reinterpret_cast<char*>(buf), len, &written);
268 return written;
269}
270
QUICHE team0b33cbb2021-06-23 12:48:46 -0700271int OnError(nghttp2_session* session, int lib_error_code, const char* msg,
272 size_t len, void* user_data) {
273 QUICHE_CHECK_NE(user_data, nullptr);
274 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
275 visitor->OnErrorDebug(absl::string_view(msg, len));
276 return 0;
277}
278
QUICHE team30c9c732021-05-06 04:33:00 -0700279nghttp2_session_callbacks_unique_ptr Create() {
QUICHE team6ecbb882021-04-13 13:04:27 -0700280 nghttp2_session_callbacks* callbacks;
281 nghttp2_session_callbacks_new(&callbacks);
282
QUICHE teamc82acf12021-05-21 17:54:00 -0700283 nghttp2_session_callbacks_set_send_callback(callbacks, &OnReadyToSend);
QUICHE team6ecbb882021-04-13 13:04:27 -0700284 nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks,
285 &OnBeginFrame);
286 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
287 &OnFrameReceived);
288 nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks,
289 &OnBeginHeaders);
290 nghttp2_session_callbacks_set_on_header_callback2(callbacks, &OnHeader);
291 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks,
292 &OnDataChunk);
293 nghttp2_session_callbacks_set_on_stream_close_callback(callbacks,
294 &OnStreamClosed);
QUICHE team06606d02021-06-23 10:58:40 -0700295 nghttp2_session_callbacks_set_before_frame_send_callback(callbacks,
296 &OnBeforeFrameSent);
297 nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, &OnFrameSent);
QUICHE team70624862021-06-24 10:37:15 -0700298 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
299 callbacks, &OnInvalidFrameReceived);
QUICHE team0b33cbb2021-06-23 12:48:46 -0700300 nghttp2_session_callbacks_set_error_callback2(callbacks, &OnError);
QUICHE teamf422b222021-06-25 08:55:17 -0700301 // on_frame_not_send_callback <- just ignored
QUICHE team1cc835e2021-05-24 07:55:18 -0700302 nghttp2_session_callbacks_set_send_data_callback(
303 callbacks, &DataFrameSourceSendCallback);
QUICHE teamf422b222021-06-25 08:55:17 -0700304 nghttp2_session_callbacks_set_pack_extension_callback(
305 callbacks, &OnPackExtensionCallback);
306 nghttp2_session_callbacks_set_unpack_extension_callback(
307 callbacks, &OnUnpackExtensionCallback);
308 nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
309 callbacks, &OnExtensionChunkReceived);
QUICHE team30c9c732021-05-06 04:33:00 -0700310 return MakeCallbacksPtr(callbacks);
QUICHE team6ecbb882021-04-13 13:04:27 -0700311}
312
313} // namespace callbacks
QUICHE teame3eacca2021-04-06 10:24:33 -0700314} // namespace adapter
315} // namespace http2