blob: c3e1dc83ebb1c947f14969925483be3f06b92025 [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"
9#include "http2/adapter/nghttp2_util.h"
10#include "third_party/nghttp2/nghttp2.h"
11#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
12#include "common/platform/api/quiche_logging.h"
13#include "common/quiche_endian.h"
14
15namespace http2 {
16namespace adapter {
QUICHE team6ecbb882021-04-13 13:04:27 -070017namespace callbacks {
QUICHE teame3eacca2021-04-06 10:24:33 -070018
QUICHE teamc82acf12021-05-21 17:54:00 -070019ssize_t OnReadyToSend(nghttp2_session* /* session */,
20 const uint8_t* data,
21 size_t length,
22 int flags,
23 void* user_data) {
24 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
25 const ssize_t result = visitor->OnReadyToSend(ToStringView(data, length));
26 if (result < 0) {
27 return NGHTTP2_ERR_CALLBACK_FAILURE;
28 } else if (result == 0) {
29 return NGHTTP2_ERR_WOULDBLOCK;
30 } else {
31 return result;
32 }
33}
34
QUICHE teame3eacca2021-04-06 10:24:33 -070035int OnBeginFrame(nghttp2_session* /* session */,
36 const nghttp2_frame_hd* header,
37 void* user_data) {
38 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
QUICHE team2121bae2021-04-30 13:05:38 -070039 visitor->OnFrameHeader(header->stream_id, header->length, header->type,
40 header->flags);
QUICHE teame3eacca2021-04-06 10:24:33 -070041 if (header->type == NGHTTP2_DATA) {
42 visitor->OnBeginDataForStream(header->stream_id, header->length);
43 }
44 return 0;
45}
46
47int OnFrameReceived(nghttp2_session* /* session */,
48 const nghttp2_frame* frame,
49 void* user_data) {
50 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
51 const Http2StreamId stream_id = frame->hd.stream_id;
QUICHE team62eeb202021-04-08 14:57:00 -070052 QUICHE_VLOG(2) << "Frame " << static_cast<int>(frame->hd.type)
53 << " for stream " << stream_id;
QUICHE teame3eacca2021-04-06 10:24:33 -070054 switch (frame->hd.type) {
55 // The beginning of the DATA frame is handled in OnBeginFrame(), and the
56 // beginning of the header block is handled in client/server-specific
57 // callbacks. This callback handles the point at which the entire logical
58 // frame has been received and processed.
59 case NGHTTP2_DATA:
60 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
61 visitor->OnEndStream(stream_id);
62 }
63 break;
64 case NGHTTP2_HEADERS: {
65 if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) {
66 visitor->OnEndHeadersForStream(stream_id);
67 }
68 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
69 visitor->OnEndStream(stream_id);
70 }
71 break;
72 }
73 case NGHTTP2_PRIORITY: {
74 nghttp2_priority_spec priority_spec = frame->priority.pri_spec;
75 visitor->OnPriorityForStream(stream_id, priority_spec.stream_id,
76 priority_spec.weight,
77 priority_spec.exclusive != 0);
78 break;
79 }
80 case NGHTTP2_RST_STREAM: {
81 visitor->OnRstStream(stream_id,
82 ToHttp2ErrorCode(frame->rst_stream.error_code));
83 break;
84 }
85 case NGHTTP2_SETTINGS:
86 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
87 visitor->OnSettingsAck();
88 } else {
89 visitor->OnSettingsStart();
90 for (int i = 0; i < frame->settings.niv; ++i) {
91 nghttp2_settings_entry entry = frame->settings.iv[i];
92 // The nghttp2_settings_entry uses int32_t for the ID; we must cast.
93 visitor->OnSetting(Http2Setting{
94 .id = static_cast<Http2SettingsId>(entry.settings_id),
95 .value = entry.value});
96 }
97 visitor->OnSettingsEnd();
98 }
99 break;
100 case NGHTTP2_PUSH_PROMISE:
101 // This case is handled by headers-related callbacks:
102 // 1. visitor->OnPushPromiseForStream() is invoked in the client-side
103 // OnHeadersStart() adapter callback, as nghttp2 only allows clients
104 // to receive PUSH_PROMISE frames.
105 // 2. visitor->OnHeaderForStream() is invoked for each server push
106 // request header in the PUSH_PROMISE header block.
107 // 3. This switch statement is reached once all server push request
108 // headers have been parsed.
109 break;
110 case NGHTTP2_PING: {
111 Http2PingId ping_id;
112 std::memcpy(&ping_id, frame->ping.opaque_data, sizeof(Http2PingId));
113 visitor->OnPing(quiche::QuicheEndian::NetToHost64(ping_id),
114 (frame->hd.flags & NGHTTP2_FLAG_ACK) != 0);
115 break;
116 }
117 case NGHTTP2_GOAWAY: {
118 absl::string_view opaque_data(
119 reinterpret_cast<const char*>(frame->goaway.opaque_data),
120 frame->goaway.opaque_data_len);
121 visitor->OnGoAway(frame->goaway.last_stream_id,
122 ToHttp2ErrorCode(frame->goaway.error_code),
123 opaque_data);
124 break;
125 }
126 case NGHTTP2_WINDOW_UPDATE: {
127 visitor->OnWindowUpdate(stream_id,
128 frame->window_update.window_size_increment);
129 break;
130 }
131 case NGHTTP2_CONTINUATION:
132 // This frame type should not be passed to any callbacks, according to
133 // https://nghttp2.org/documentation/enums.html#c.NGHTTP2_CONTINUATION.
134 QUICHE_LOG(ERROR) << "Unexpected receipt of NGHTTP2_CONTINUATION type!";
135 break;
136 case NGHTTP2_ALTSVC:
137 break;
138 case NGHTTP2_ORIGIN:
139 break;
140 }
141
142 return 0;
143}
144
145int OnBeginHeaders(nghttp2_session* /* session */,
146 const nghttp2_frame* frame,
147 void* user_data) {
148 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
149 visitor->OnBeginHeadersForStream(frame->hd.stream_id);
150 return 0;
151}
152
153int OnHeader(nghttp2_session* /* session */,
154 const nghttp2_frame* frame,
155 nghttp2_rcbuf* name,
156 nghttp2_rcbuf* value,
157 uint8_t flags,
158 void* user_data) {
159 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
160 visitor->OnHeaderForStream(frame->hd.stream_id, ToStringView(name),
161 ToStringView(value));
162 return 0;
163}
164
165int OnDataChunk(nghttp2_session* /* session */,
166 uint8_t flags,
167 Http2StreamId stream_id,
168 const uint8_t* data,
169 size_t len,
170 void* user_data) {
171 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
172 visitor->OnDataForStream(
173 stream_id, absl::string_view(reinterpret_cast<const char*>(data), len));
174 return 0;
175}
176
177int OnStreamClosed(nghttp2_session* /* session */,
178 Http2StreamId stream_id,
179 uint32_t error_code,
180 void* user_data) {
181 auto* visitor = static_cast<Http2VisitorInterface*>(user_data);
QUICHE teamd9050872021-04-27 09:12:49 -0700182 visitor->OnCloseStream(stream_id, ToHttp2ErrorCode(error_code));
QUICHE teame3eacca2021-04-06 10:24:33 -0700183 return 0;
184}
185
186ssize_t OnReadyToReadDataForStream(nghttp2_session* /* session */,
187 Http2StreamId stream_id,
188 uint8_t* dest_buffer,
189 size_t max_length,
190 uint32_t* data_flags,
191 nghttp2_data_source* source,
192 void* user_data) {
193 auto* visitor = static_cast<Http2VisitorInterface*>(source->ptr);
194 ssize_t bytes_to_send = 0;
195 bool end_stream = false;
196 visitor->OnReadyToSendDataForStream(stream_id,
197 reinterpret_cast<char*>(dest_buffer),
198 max_length, &bytes_to_send, &end_stream);
199 if (bytes_to_send >= 0 && end_stream) {
200 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
201 }
202 return bytes_to_send;
203}
204
QUICHE team30c9c732021-05-06 04:33:00 -0700205nghttp2_session_callbacks_unique_ptr Create() {
QUICHE team6ecbb882021-04-13 13:04:27 -0700206 nghttp2_session_callbacks* callbacks;
207 nghttp2_session_callbacks_new(&callbacks);
208
QUICHE teamc82acf12021-05-21 17:54:00 -0700209 nghttp2_session_callbacks_set_send_callback(callbacks, &OnReadyToSend);
QUICHE team6ecbb882021-04-13 13:04:27 -0700210 nghttp2_session_callbacks_set_on_begin_frame_callback(callbacks,
211 &OnBeginFrame);
212 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
213 &OnFrameReceived);
214 nghttp2_session_callbacks_set_on_begin_headers_callback(callbacks,
215 &OnBeginHeaders);
216 nghttp2_session_callbacks_set_on_header_callback2(callbacks, &OnHeader);
217 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks,
218 &OnDataChunk);
219 nghttp2_session_callbacks_set_on_stream_close_callback(callbacks,
220 &OnStreamClosed);
QUICHE team30c9c732021-05-06 04:33:00 -0700221 return MakeCallbacksPtr(callbacks);
QUICHE team6ecbb882021-04-13 13:04:27 -0700222}
223
224} // namespace callbacks
QUICHE teame3eacca2021-04-06 10:24:33 -0700225} // namespace adapter
226} // namespace http2