CCF
Loading...
Searching...
No Matches
http_parser.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/hex.h"
9#include "http_builder.h"
10#include "http_proc.h"
11
12#include <algorithm>
13#include <cctype>
14#include <llhttp/llhttp.h>
15#include <map>
16#include <queue>
17#include <regex>
18#include <string>
19#include <string_view>
20
21namespace http
22{
23 inline auto split_url_path(const std::string_view& url)
24 {
25 LOG_TRACE_FMT("Received url to parse: {}", std::string_view(url));
26
27 const auto path_end = url.find('?');
28 const auto query_start =
29 path_end == std::string::npos ? url.size() : path_end + 1;
30
31 const auto query_end = url.find('#', query_start);
32 const auto fragment_start =
33 query_end == std::string::npos ? url.size() : query_end + 1;
34
35 return std::make_tuple(
36 url.substr(0, path_end),
37 url.substr(query_start, query_end - query_start),
38 url.substr(fragment_start));
39 }
40
41 static std::string url_decode(const std::string_view& s_)
42 {
43 std::string s(s_);
44 char const* src = s.c_str();
45 char const* end = s.c_str() + s.size();
46 char* dst = s.data();
47
48 while (src < end)
49 {
50 char const c = *src++;
51 if (c == '%' && (src + 1) < end && isxdigit(src[0]) && isxdigit(src[1]))
52 {
53 const auto a = ccf::ds::hex_char_to_int(*src++);
54 const auto b = ccf::ds::hex_char_to_int(*src++);
55 *dst++ = (a << 4) | b;
56 }
57 else if (c == '+')
58 {
59 *dst++ = ' ';
60 }
61 else
62 {
63 *dst++ = c;
64 }
65 }
66
67 s.resize(dst - s.data());
68 return s;
69 }
70
71 inline bool status_success(ccf::http_status status)
72 {
73 return status >= 200 && status < 300;
74 }
75
77 {
78 public:
79 struct Request
80 {
81 llhttp_method method;
82 std::string url;
84 std::vector<uint8_t> body;
85 };
86
87 std::queue<Request> received;
88
89 virtual void handle_request(
90 llhttp_method method,
91 const std::string_view& url,
92 ccf::http::HeaderMap&& headers,
93 std::vector<uint8_t>&& body,
94 int32_t) override
95 {
96 received.emplace(Request{method, std::string(url), headers, body});
97 }
98 };
99
101 {
102 public:
109
110 std::queue<Response> received;
111
112 virtual void handle_response(
113 ccf::http_status status,
114 ccf::http::HeaderMap&& headers,
115 std::vector<uint8_t>&& body) override
116 {
117 received.emplace(Response{status, headers, body});
118 }
119 };
120
121 enum State
122 {
125 };
126
127 static int on_msg_begin(llhttp_t* parser);
128 static int on_url(llhttp_t* parser, const char* at, size_t length);
129 static int on_header_field(llhttp_t* parser, const char* at, size_t length);
130 static int on_header_value(llhttp_t* parser, const char* at, size_t length);
131 static int on_headers_complete(llhttp_t* parser);
132 static int on_body(llhttp_t* parser, const char* at, size_t length);
133 static int on_msg_end(llhttp_t* parser);
134
135 struct URL
136 {
137 std::string scheme;
138 std::string host;
139 std::string port;
140 std::string path;
141 std::string query;
142 std::string fragment;
143 };
144
145 inline URL parse_url_full(const std::string& url)
146 {
147 LOG_TRACE_FMT("Received url to parse: {}", url);
148
149 // From https://tools.ietf.org/html/rfc3986#appendix-B
150 std::regex url_regex(
151 "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?");
152
153 std::smatch match;
154 if (!std::regex_match(url, match, url_regex))
155 {
156 throw std::invalid_argument(fmt::format("Unable to parse url: {}", url));
157 }
158
159 const auto host_port = match[4].str();
160
161 // IPv6 hosts may contain colons, so only search for port after the closing
162 // square bracket
163 const auto closing_bracket = host_port.rfind(']');
164 const auto port_delim_start =
165 closing_bracket == std::string::npos ? 0 : closing_bracket;
166 const auto port_delim = host_port.find(':', port_delim_start);
167
168 URL u;
169 u.scheme = match[2].str();
170 u.host = host_port.substr(0, port_delim);
171 if (port_delim != std::string::npos)
172 {
173 u.port = host_port.substr(port_delim + 1);
174 }
175 u.path = match[5].str();
176 u.query = match[7].str();
177 u.fragment = match[9].str();
178 return u;
179 }
180
181 class Parser
182 {
183 protected:
184 llhttp_t parser;
185 llhttp_settings_t settings;
188
189 std::vector<uint8_t> body_buf;
191
192 std::pair<std::string, std::string> partial_parsed_header = {};
193
195 {
197 partial_parsed_header.first.clear();
198 partial_parsed_header.second.clear();
199 }
200
202 llhttp_type_t type,
203 const ccf::http::ParserConfiguration& config =
205 configuration(config)
206 {
207 llhttp_settings_init(&settings);
208
209 settings.on_message_begin = on_msg_begin;
210 settings.on_header_field = on_header_field;
211 settings.on_header_value = on_header_value;
212 settings.on_headers_complete = on_headers_complete;
213 settings.on_body = on_body;
214 settings.on_message_complete = on_msg_end;
215
216 llhttp_init(&parser, type, &settings);
217 parser.data = this;
218 }
219
220 public:
221 void execute(const uint8_t* data, size_t size)
222 {
223 const auto data_char = (const char*)data;
224 auto err_no = llhttp_execute(&parser, data_char, size);
225
226 if (err_no == HPE_PAUSED_UPGRADE)
227 {
228 // Assume Upgrade requests will be handled by caller inspecting headers,
229 // so we can instantly resume the parser.
230 llhttp_resume_after_upgrade(&parser);
231 }
232 else if (err_no != HPE_OK)
233 {
234 throw std::runtime_error(fmt::format(
235 "HTTP parsing failed ({}: {}) around byte {}",
236 llhttp_errno_name(err_no),
237 llhttp_get_error_reason(&parser),
238 llhttp_get_error_pos(&parser) - data_char));
239 }
240 }
241
242 void append_body(const char* at, size_t length)
243 {
244 if (state == IN_MESSAGE)
245 {
246 LOG_TRACE_FMT("Appending chunk [{} bytes]", length);
247 std::copy(at, at + length, std::back_inserter(body_buf));
248
249 auto const& max_body_size = configuration.max_body_size.value_or(
250 ccf::http::default_max_body_size);
251 if (body_buf.size() > max_body_size)
252 {
253 throw RequestPayloadTooLargeException(fmt::format(
254 "HTTP request body is too large (max size allowed: {})",
255 max_body_size));
256 }
257 }
258 else
259 {
260 throw std::runtime_error("Receiving content outside of message");
261 }
262 }
263
264 virtual void new_message()
265 {
266 if (state == DONE)
267 {
268 LOG_TRACE_FMT("Entering new message");
270 body_buf.clear();
271 headers.clear();
272 }
273 else
274 {
275 throw std::runtime_error(
276 "Entering new message when previous message isn't complete");
277 }
278 }
279
280 virtual void handle_completed_message() = 0;
281
283 {
284 if (state == IN_MESSAGE)
285 {
286 LOG_TRACE_FMT("Done with message");
288 state = DONE;
289 }
290 else
291 {
292 throw std::runtime_error("Ending message, but not in a message");
293 }
294 }
295
296 void header_field(const char* at, size_t length)
297 {
298 if (!partial_parsed_header.second.empty())
299 {
301 }
302
303 const auto max_headers_count = configuration.max_headers_count.value_or(
304 ccf::http::default_max_headers_count);
305 if (headers.size() >= max_headers_count)
306 {
307 throw RequestHeaderTooLargeException(fmt::format(
308 "Too many headers (max number allowed: {})", max_headers_count));
309 }
310
311 // HTTP headers are stored lowercase as it is easier to verify HTTP
312 // signatures later on
313 auto f = std::string(at, length);
314 ccf::nonstd::to_lower(f);
315 auto& partial_header_key = partial_parsed_header.first;
316 partial_header_key.append(f);
317
318 auto const& max_header_size = configuration.max_header_size.value_or(
319 ccf::http::default_max_header_size);
320 if (partial_header_key.size() > max_header_size)
321 {
322 throw RequestHeaderTooLargeException(fmt::format(
323 "Header key for '{}' is too large (max size allowed: {})",
325 max_header_size));
326 }
327 }
328
329 void header_value(const char* at, size_t length)
330 {
331 auto& partial_header_value = partial_parsed_header.second;
332 partial_header_value.append(at, length);
333 auto const& max_header_size = configuration.max_header_size.value_or(
334 ccf::http::default_max_header_size);
335 if (partial_header_value.size() > max_header_size)
336 {
337 throw RequestHeaderTooLargeException(fmt::format(
338 "Header value for '{}' is too large (max size allowed: {})",
340 max_header_size));
341 }
342 }
343
345 {
347 }
348 };
349
350 static int on_msg_begin(llhttp_t* parser)
351 {
352 Parser* p = reinterpret_cast<Parser*>(parser->data);
353 p->new_message();
354 return HPE_OK;
355 }
356
357 static int on_header_field(llhttp_t* parser, const char* at, size_t length)
358 {
359 Parser* p = reinterpret_cast<Parser*>(parser->data);
360 p->header_field(at, length);
361 return HPE_OK;
362 }
363
364 static int on_header_value(llhttp_t* parser, const char* at, size_t length)
365 {
366 Parser* p = reinterpret_cast<Parser*>(parser->data);
367 p->header_value(at, length);
368 return HPE_OK;
369 }
370
371 static int on_headers_complete(llhttp_t* parser)
372 {
373 Parser* p = reinterpret_cast<Parser*>(parser->data);
374 p->headers_complete();
375 return HPE_OK;
376 }
377
378 static int on_body(llhttp_t* parser, const char* at, size_t length)
379 {
380 Parser* p = reinterpret_cast<Parser*>(parser->data);
381 p->append_body(at, length);
382 return HPE_OK;
383 }
384
385 static int on_msg_end(llhttp_t* parser)
386 {
387 Parser* p = reinterpret_cast<Parser*>(parser->data);
388 p->end_message();
389 return HPE_OK;
390 }
391
392 // Request-specific
393 class RequestParser : public Parser
394 {
395 private:
396 RequestProcessor& proc;
397
398 std::string url = "";
399
400 public:
402 RequestProcessor& proc_,
403 const ccf::http::ParserConfiguration& config =
405 Parser(HTTP_REQUEST, config),
406 proc(proc_)
407 {
408 settings.on_url = on_url;
409 }
410
411 void append_url(const char* at, size_t length)
412 {
413 url.append(at, length);
414 }
415
416 void new_message() override
417 {
419 url.clear();
420 }
421
423 {
424 if (url.empty())
425 {
426 proc.handle_request(
427 llhttp_method(parser.method),
428 {},
429 std::move(headers),
430 std::move(body_buf));
431 }
432 else
433 {
434 proc.handle_request(
435 llhttp_method(parser.method),
436 url,
437 std::move(headers),
438 std::move(body_buf));
439 }
440 }
441 };
442
443 static int on_url(llhttp_t* parser, const char* at, size_t length)
444 {
445 RequestParser* p = reinterpret_cast<RequestParser*>(parser->data);
446 p->append_url(at, length);
447 return HPE_OK;
448 }
449
450 // Response-specific
451 class ResponseParser : public Parser
452 {
453 private:
454 ResponseProcessor& proc;
455
456 public:
458 Parser(HTTP_RESPONSE, ccf::http::ParserConfiguration{}),
459 proc(proc_)
460 {}
461
463 {
464 proc.handle_response(
465 ccf::http_status(parser.status_code),
466 std::move(headers),
467 std::move(body_buf));
468 }
469 };
470}
Definition http_parser.h:182
State state
Definition http_parser.h:187
virtual void handle_completed_message()=0
void append_body(const char *at, size_t length)
Definition http_parser.h:242
ccf::http::ParserConfiguration configuration
Definition http_parser.h:186
llhttp_t parser
Definition http_parser.h:184
void headers_complete()
Definition http_parser.h:344
virtual void new_message()
Definition http_parser.h:264
llhttp_settings_t settings
Definition http_parser.h:185
void execute(const uint8_t *data, size_t size)
Definition http_parser.h:221
std::vector< uint8_t > body_buf
Definition http_parser.h:189
void header_value(const char *at, size_t length)
Definition http_parser.h:329
Parser(llhttp_type_t type, const ccf::http::ParserConfiguration &config=ccf::http::ParserConfiguration{})
Definition http_parser.h:201
std::pair< std::string, std::string > partial_parsed_header
Definition http_parser.h:192
void complete_header()
Definition http_parser.h:194
void end_message()
Definition http_parser.h:282
void header_field(const char *at, size_t length)
Definition http_parser.h:296
ccf::http::HeaderMap headers
Definition http_parser.h:190
Definition http_exceptions.h:40
Definition http_parser.h:394
RequestParser(RequestProcessor &proc_, const ccf::http::ParserConfiguration &config=ccf::http::ParserConfiguration{})
Definition http_parser.h:401
void handle_completed_message() override
Definition http_parser.h:422
void new_message() override
Definition http_parser.h:416
void append_url(const char *at, size_t length)
Definition http_parser.h:411
Definition http_exceptions.h:31
Definition http_proc.h:20
virtual void handle_request(llhttp_method method, const std::string_view &url, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body, int32_t stream_id=http2::DEFAULT_STREAM_ID)=0
Definition http_parser.h:452
ResponseParser(ResponseProcessor &proc_)
Definition http_parser.h:457
void handle_completed_message() override
Definition http_parser.h:462
Definition http_proc.h:31
virtual void handle_response(ccf::http_status status, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body)=0
#define LOG_TRACE_FMT
Definition logger.h:356
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
Definition app_interface.h:14
llhttp_status http_status
Definition http_status.h:9
Definition error_reporter.h:6
auto split_url_path(const std::string_view &url)
Definition http_parser.h:23
bool status_success(ccf::http_status status)
Definition http_parser.h:71
State
Definition http_parser.h:122
@ DONE
Definition http_parser.h:123
@ IN_MESSAGE
Definition http_parser.h:124
URL parse_url_full(const std::string &url)
Definition http_parser.h:145
Definition http_configuration.h:24
std::optional< ccf::ds::SizeString > max_header_size
Definition http_configuration.h:26
std::optional< ccf::ds::SizeString > max_body_size
Definition http_configuration.h:25
std::optional< uint32_t > max_headers_count
Definition http_configuration.h:27
Definition http_parser.h:80
std::vector< uint8_t > body
Definition http_parser.h:84
std::string url
Definition http_parser.h:82
ccf::http::HeaderMap headers
Definition http_parser.h:83
llhttp_method method
Definition http_parser.h:81
Definition http_parser.h:77
virtual void handle_request(llhttp_method method, const std::string_view &url, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body, int32_t) override
Definition http_parser.h:89
std::queue< Request > received
Definition http_parser.h:87
Definition http_parser.h:104
ccf::http::HeaderMap headers
Definition http_parser.h:106
ccf::http_status status
Definition http_parser.h:105
std::vector< uint8_t > body
Definition http_parser.h:107
Definition http_parser.h:101
std::queue< Response > received
Definition http_parser.h:110
virtual void handle_response(ccf::http_status status, ccf::http::HeaderMap &&headers, std::vector< uint8_t > &&body) override
Definition http_parser.h:112
Definition http_parser.h:136
std::string host
Definition http_parser.h:138
std::string query
Definition http_parser.h:141
std::string port
Definition http_parser.h:139
std::string path
Definition http_parser.h:140
std::string fragment
Definition http_parser.h:142
std::string scheme
Definition http_parser.h:137