QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 1 | #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 team | 1cc835e | 2021-05-24 07:55:18 -0700 | [diff] [blame] | 9 | #include "http2/adapter/nghttp2_data_provider.h" |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 10 | #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 | |
| 16 | namespace http2 { |
| 17 | namespace adapter { |
QUICHE team | 6ecbb88 | 2021-04-13 13:04:27 -0700 | [diff] [blame] | 18 | namespace callbacks { |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 19 | |
QUICHE team | c82acf1 | 2021-05-21 17:54:00 -0700 | [diff] [blame] | 20 | ssize_t OnReadyToSend(nghttp2_session* /* session */, |
| 21 | const uint8_t* data, |
| 22 | size_t length, |
| 23 | int flags, |
| 24 | void* user_data) { |
QUICHE team | b23b83c | 2021-06-18 15:43:50 -0700 | [diff] [blame] | 25 | QUICHE_CHECK_NE(user_data, nullptr); |
QUICHE team | c82acf1 | 2021-05-21 17:54:00 -0700 | [diff] [blame] | 26 | 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 team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 37 | int OnBeginFrame(nghttp2_session* /* session */, |
| 38 | const nghttp2_frame_hd* header, |
| 39 | void* user_data) { |
QUICHE team | b23b83c | 2021-06-18 15:43:50 -0700 | [diff] [blame] | 40 | QUICHE_CHECK_NE(user_data, nullptr); |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 41 | auto* visitor = static_cast<Http2VisitorInterface*>(user_data); |
QUICHE team | 2121bae | 2021-04-30 13:05:38 -0700 | [diff] [blame] | 42 | visitor->OnFrameHeader(header->stream_id, header->length, header->type, |
| 43 | header->flags); |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 44 | if (header->type == NGHTTP2_DATA) { |
| 45 | visitor->OnBeginDataForStream(header->stream_id, header->length); |
| 46 | } |
| 47 | return 0; |
| 48 | } |
| 49 | |
| 50 | int OnFrameReceived(nghttp2_session* /* session */, |
| 51 | const nghttp2_frame* frame, |
| 52 | void* user_data) { |
QUICHE team | b23b83c | 2021-06-18 15:43:50 -0700 | [diff] [blame] | 53 | QUICHE_CHECK_NE(user_data, nullptr); |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 54 | auto* visitor = static_cast<Http2VisitorInterface*>(user_data); |
| 55 | const Http2StreamId stream_id = frame->hd.stream_id; |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 56 | 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 | |
| 147 | int OnBeginHeaders(nghttp2_session* /* session */, |
| 148 | const nghttp2_frame* frame, |
| 149 | void* user_data) { |
QUICHE team | b23b83c | 2021-06-18 15:43:50 -0700 | [diff] [blame] | 150 | QUICHE_CHECK_NE(user_data, nullptr); |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 151 | auto* visitor = static_cast<Http2VisitorInterface*>(user_data); |
| 152 | visitor->OnBeginHeadersForStream(frame->hd.stream_id); |
| 153 | return 0; |
| 154 | } |
| 155 | |
| 156 | int 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 team | b23b83c | 2021-06-18 15:43:50 -0700 | [diff] [blame] | 162 | QUICHE_CHECK_NE(user_data, nullptr); |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 163 | auto* visitor = static_cast<Http2VisitorInterface*>(user_data); |
QUICHE team | 29e35dd | 2021-06-28 15:25:35 -0700 | [diff] [blame] | 164 | 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 team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 175 | } |
| 176 | |
QUICHE team | 06606d0 | 2021-06-23 10:58:40 -0700 | [diff] [blame] | 177 | int OnBeforeFrameSent(nghttp2_session* /* session */, |
| 178 | const nghttp2_frame* frame, void* user_data) { |
| 179 | QUICHE_CHECK_NE(user_data, nullptr); |
QUICHE team | 43d93a0 | 2021-06-25 12:32:25 -0700 | [diff] [blame] | 180 | LogBeforeSend(*frame); |
QUICHE team | 06606d0 | 2021-06-23 10:58:40 -0700 | [diff] [blame] | 181 | 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 | |
| 186 | int 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 team | 7062486 | 2021-06-24 10:37:15 -0700 | [diff] [blame] | 200 | int 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 team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 210 | int 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 team | b23b83c | 2021-06-18 15:43:50 -0700 | [diff] [blame] | 216 | QUICHE_CHECK_NE(user_data, nullptr); |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 217 | 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 | |
| 223 | int OnStreamClosed(nghttp2_session* /* session */, |
| 224 | Http2StreamId stream_id, |
| 225 | uint32_t error_code, |
| 226 | void* user_data) { |
QUICHE team | b23b83c | 2021-06-18 15:43:50 -0700 | [diff] [blame] | 227 | QUICHE_CHECK_NE(user_data, nullptr); |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 228 | auto* visitor = static_cast<Http2VisitorInterface*>(user_data); |
QUICHE team | d905087 | 2021-04-27 09:12:49 -0700 | [diff] [blame] | 229 | visitor->OnCloseStream(stream_id, ToHttp2ErrorCode(error_code)); |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 230 | return 0; |
| 231 | } |
| 232 | |
QUICHE team | f422b22 | 2021-06-25 08:55:17 -0700 | [diff] [blame] | 233 | int 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 | |
| 247 | int 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 | |
| 260 | ssize_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 team | 0b33cbb | 2021-06-23 12:48:46 -0700 | [diff] [blame] | 271 | int 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 team | 30c9c73 | 2021-05-06 04:33:00 -0700 | [diff] [blame] | 279 | nghttp2_session_callbacks_unique_ptr Create() { |
QUICHE team | 6ecbb88 | 2021-04-13 13:04:27 -0700 | [diff] [blame] | 280 | nghttp2_session_callbacks* callbacks; |
| 281 | nghttp2_session_callbacks_new(&callbacks); |
| 282 | |
QUICHE team | c82acf1 | 2021-05-21 17:54:00 -0700 | [diff] [blame] | 283 | nghttp2_session_callbacks_set_send_callback(callbacks, &OnReadyToSend); |
QUICHE team | 6ecbb88 | 2021-04-13 13:04:27 -0700 | [diff] [blame] | 284 | 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 team | 06606d0 | 2021-06-23 10:58:40 -0700 | [diff] [blame] | 295 | nghttp2_session_callbacks_set_before_frame_send_callback(callbacks, |
| 296 | &OnBeforeFrameSent); |
| 297 | nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, &OnFrameSent); |
QUICHE team | 7062486 | 2021-06-24 10:37:15 -0700 | [diff] [blame] | 298 | nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( |
| 299 | callbacks, &OnInvalidFrameReceived); |
QUICHE team | 0b33cbb | 2021-06-23 12:48:46 -0700 | [diff] [blame] | 300 | nghttp2_session_callbacks_set_error_callback2(callbacks, &OnError); |
QUICHE team | f422b22 | 2021-06-25 08:55:17 -0700 | [diff] [blame] | 301 | // on_frame_not_send_callback <- just ignored |
QUICHE team | 1cc835e | 2021-05-24 07:55:18 -0700 | [diff] [blame] | 302 | nghttp2_session_callbacks_set_send_data_callback( |
| 303 | callbacks, &DataFrameSourceSendCallback); |
QUICHE team | f422b22 | 2021-06-25 08:55:17 -0700 | [diff] [blame] | 304 | 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 team | 30c9c73 | 2021-05-06 04:33:00 -0700 | [diff] [blame] | 310 | return MakeCallbacksPtr(callbacks); |
QUICHE team | 6ecbb88 | 2021-04-13 13:04:27 -0700 | [diff] [blame] | 311 | } |
| 312 | |
| 313 | } // namespace callbacks |
QUICHE team | e3eacca | 2021-04-06 10:24:33 -0700 | [diff] [blame] | 314 | } // namespace adapter |
| 315 | } // namespace http2 |