CCF
Loading...
Searching...
No Matches
http2_callbacks.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
8#include "http2_types.h"
9#include "http2_utils.h"
10
11namespace http2
12{
13 static ssize_t read_outgoing_callback(
14 nghttp2_session* session,
15 StreamId stream_id,
16 uint8_t* buf,
17 size_t length,
18 uint32_t* data_flags,
19 nghttp2_data_source* /*source*/,
20 void* /*user_data*/)
21 {
22 auto* stream_data = get_stream_data(session, stream_id);
23 if (stream_data->outgoing.state == StreamResponseState::Uninitialised)
24 {
26 "http2::read_outgoing_callback error: unexpected state {}",
27 stream_data->outgoing.state);
28 return NGHTTP2_ERR_CALLBACK_FAILURE;
29 }
30
31 DataSource& body = stream_data->outgoing.body;
32 const auto body_remaining = body.data.size() - body.consumed;
33 size_t to_read = std::min(body_remaining, length);
34
35 if (
36 to_read == 0 &&
37 stream_data->outgoing.state == StreamResponseState::Streaming)
38 {
39 // Early out: when streaming, avoid calling this callback
40 // repeatedly when there no data to read
41 return NGHTTP2_ERR_DEFERRED;
42 }
43
44 if (to_read > 0)
45 {
46 memcpy(buf, body.data.data() + body.consumed, to_read);
47 body.consumed += to_read;
48 }
49
50 if (stream_data->outgoing.state == StreamResponseState::Closing)
51 {
52 if (body.consumed == body.data.size())
53 {
54 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
55 }
56
57 if (stream_data->outgoing.has_trailers)
58 {
59 *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
60 }
61 }
62
63 return to_read;
64 }
65
66 static int on_begin_frame_recv_callback(
67 nghttp2_session* /*session*/, const nghttp2_frame_hd* hd, void* user_data)
68 {
69 const auto& stream_id = hd->stream_id;
71 "http2::on_begin_frame_recv_callback, type: {}, stream_id: {}",
72 hd->type,
73 stream_id);
74
75 // nghttp2 does not handle
76 // https://www.rfc-editor.org/rfc/rfc7540#section-5.1.1 (see
77 // https://github.com/nghttp2/nghttp2/issues/1300)
78 // > An endpoint that receives an unexpected stream identifier MUST
79 // > respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
80 //
81 // So can catch this case early in this callback by making sure that _new_
82 // stream ids are not less than the most recent stream id on this session.
83 auto* p = get_parser(user_data);
84 if (
85 stream_id != DEFAULT_STREAM_ID && p->get_stream(stream_id) == nullptr &&
86 hd->type == NGHTTP2_HEADERS)
87 {
88 if (stream_id < p->get_last_stream_id())
89 {
91 "http2::on_begin_frame_recv_callback: cannot process stream id {} "
92 "< last stream id {}",
93 stream_id,
94 p->get_last_stream_id());
95 return NGHTTP2_ERR_PROTO;
96 }
97
98 p->create_stream(stream_id);
99 }
100
101 return 0;
102 }
103
104 static int on_frame_recv_callback(
105 nghttp2_session* session, const nghttp2_frame* frame, void* user_data)
106 {
107 LOG_TRACE_FMT("http2::on_frame_recv_callback, type: {}", frame->hd.type);
108
109 const auto stream_id = frame->hd.stream_id;
110 auto* stream_data = get_stream_data(session, stream_id);
111
112 switch (frame->hd.type)
113 {
114 case NGHTTP2_DATA:
115 case NGHTTP2_HEADERS:
116 {
117 LOG_TRACE_FMT("Headers/Data frame");
118 // For DATA and HEADERS frame, this callback may be called after
119 // on_stream_close_callback so check that stream_data still alive.
120 if (stream_data == nullptr)
121 {
122 LOG_FAIL_FMT("No stream_data");
123 return 0;
124 }
125
126 // If the request is complete, process it
127 if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) != 0)
128 {
129 auto* p = get_parser(user_data);
130 p->handle_completed(stream_id, stream_data);
131 }
132 break;
133 }
134 default:
135 {
136 break;
137 }
138 }
139
140 return 0;
141 }
142
143 static int on_begin_headers_callback(
144 nghttp2_session* session, const nghttp2_frame* frame, void* user_data)
145 {
146 LOG_TRACE_FMT("http2::on_begin_headers_callback");
147 const auto& stream_id = frame->hd.stream_id;
148
149 auto* p = get_parser(user_data);
150
151 auto stream_data = p->get_stream(stream_id);
152 if (stream_data == nullptr)
153 {
154 // Streams are created in on_begin_frame_recv_callback
155 throw std::logic_error(
156 fmt::format("Stream {} should already exist", stream_id));
157 }
158
159 auto rc = nghttp2_session_set_stream_user_data(
160 session, stream_id, stream_data.get());
161 if (rc != 0)
162 {
163 throw std::logic_error(fmt::format(
164 "HTTP/2: Could not set user data for stream {}: {}",
165 stream_id,
166 nghttp2_strerror(rc)));
167 }
168
169 return 0;
170 }
171
172 static int on_header_callback(
173 nghttp2_session* session,
174 const nghttp2_frame* frame,
175 const uint8_t* name,
176 size_t namelen,
177 const uint8_t* value,
178 size_t valuelen,
179 uint8_t /*flags*/,
180 void* user_data)
181 {
182 const auto& stream_id = frame->hd.stream_id;
183 auto k = std::string(name, name + namelen);
184 auto v = std::string(value, value + valuelen);
185 LOG_TRACE_FMT("http2::on_header_callback: {}, {}:{}", stream_id, k, v);
186
187 auto* p = get_parser(user_data);
188 const auto& configuration = p->get_configuration();
189
190 auto const& max_header_size = configuration.max_header_size.value_or(
191 ccf::http::default_max_header_size);
192 if (namelen > max_header_size)
193 {
195 fmt::format(
196 "Header key for '{}' is too large (max size allowed: {})",
197 k,
198 max_header_size),
199 stream_id);
200 }
201
202 if (valuelen > max_header_size)
203 {
205 fmt::format(
206 "Header value for '{}' is too large (max size allowed: {})",
207 v,
208 max_header_size),
209 stream_id);
210 }
211
212 auto* stream_data = get_stream_data(session, stream_id);
213 const auto max_headers_count = configuration.max_headers_count.value_or(
214 ccf::http::default_max_headers_count);
215 if (stream_data->incoming.headers.size() >= max_headers_count)
216 {
218 fmt::format(
219 "Too many headers (max number allowed: {})", max_headers_count),
220 stream_id);
221 }
222
223 stream_data->incoming.headers.emplace(k, v);
224
225 return 0;
226 }
227
228 static int on_data_callback(
229 nghttp2_session* session,
230 uint8_t /*flags*/,
231 StreamId stream_id,
232 const uint8_t* data,
233 size_t len,
234 void* user_data)
235 {
236 LOG_TRACE_FMT("http2::on_data_callback: {}, {} bytes", stream_id, len);
237
238 auto* stream_data = get_stream_data(session, stream_id);
239 auto* p = get_parser(user_data);
240 const auto& configuration = p->get_configuration();
241
242 stream_data->incoming.body.insert(
243 stream_data->incoming.body.end(), data, data + len);
244
245 auto const& max_body_size =
246 configuration.max_body_size.value_or(ccf::http::default_max_body_size);
247 if (stream_data->incoming.body.size() > max_body_size)
248 {
250 fmt::format(
251 "HTTP request body is too large (max size allowed: {})",
252 max_body_size),
253 stream_id);
254 }
255
256 return 0;
257 }
258
259 static int on_stream_close_callback(
260 nghttp2_session* /*session*/,
261 StreamId stream_id,
262 uint32_t error_code,
263 void* user_data)
264 {
266 "http2::on_stream_close_callback: {}, {}", stream_id, error_code);
267
268 auto* p = get_parser(user_data);
269 p->destroy_stream(stream_id);
270
271 return 0;
272 }
273
274 static ssize_t on_data_source_read_length_callback(
275 nghttp2_session* /*session*/,
276 uint8_t /*frame_type*/,
277 int32_t stream_id,
278 int32_t session_remote_window_size,
279 int32_t stream_remote_window_size,
280 uint32_t remote_max_frame_size,
281 void* user_data)
282 {
283 auto* p = get_parser(user_data);
284 const auto& configuration = p->get_configuration();
285 const auto& max_frame_size =
286 configuration.max_frame_size.value_or(ccf::http::default_max_frame_size);
287
289 "http2::on_data_source_read_length_callback: {}, {}, allowed [1, "
290 "min({},{},{})]",
291 stream_id,
292 max_frame_size,
293 session_remote_window_size,
294 stream_remote_window_size,
295 remote_max_frame_size);
296
297 return max_frame_size;
298 }
299
300 static int on_error_callback(
301 nghttp2_session* /*session*/,
302 int /*lib_error_code*/,
303 const char* msg,
304 size_t len,
305 void* /*user_data*/)
306 {
307 LOG_DEBUG_FMT("HTTP/2 error: {}", std::string(msg, msg + len));
308 return 0;
309 }
310}
Definition http_exceptions.h:40
Definition http_exceptions.h:31
#define LOG_TRACE_FMT
Definition internal_logger.h:13
#define LOG_DEBUG_FMT
Definition internal_logger.h:14
#define LOG_FAIL_FMT
Definition internal_logger.h:16
Definition http2_callbacks.h:12
int32_t StreamId
Definition http2_types.h:20
Definition http2_types.h:35
size_t consumed
Definition http2_types.h:39
std::vector< uint8_t > data
Definition http2_types.h:38