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