Crow  1.1
A C++ microframework for the web
 
Loading...
Searching...
No Matches
http_parser_merged.h
1/* merged revision: 5b951d74bd66ec9d38448e0a85b1cf8b85d97db3 */
2/* updated to : e13b274770da9b82a1085dec29182acfea72e7a7 (beyond v2.9.5) */
3/* commits not included:
4 * 091ebb87783a58b249062540bbea07de2a11e9cf
5 * 6132d1fefa03f769a3979355d1f5da0b8889cad2
6 * 7ba312397c2a6c851a4b5efe6c1603b1e1bda6ff
7 * d7675453a6c03180572f084e95eea0d02df39164
8 * dff604db203986e532e5a679bafd0e7382c6bdd9 (Might be useful to actually add [upgrade requests with a body])
9 * e01811e7f4894d7f0f7f4bd8492cccec6f6b4038 (related to above)
10 * 05525c5fde1fc562481f6ae08fa7056185325daf (also related to above)
11 * 350258965909f249f9c59823aac240313e0d0120 (cannot be implemented due to upgrade)
12 */
13
14// clang-format off
15#pragma once
16extern "C" {
17#include <stddef.h>
18#if defined(_WIN32) && !defined(__MINGW32__) && \
19 (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
20#include <BaseTsd.h>
21typedef __int8 int8_t;
22typedef unsigned __int8 uint8_t;
23typedef __int16 int16_t;
24typedef unsigned __int16 uint16_t;
25typedef __int32 int32_t;
26typedef unsigned __int32 uint32_t;
27typedef __int64 int64_t;
28typedef unsigned __int64 uint64_t;
29#elif (defined(__sun) || defined(__sun__)) && defined(__SunOS_5_9)
30#include <sys/inttypes.h>
31#else
32#include <stdint.h>
33#endif
34#include <assert.h>
35#include <ctype.h>
36#include <string.h>
37#include <limits.h>
38}
39
40#include "crow/common.h"
41namespace crow
42{
43/* Maximium header size allowed. If the macro is not defined
44 * before including this header then the default is used. To
45 * change the maximum header size, define the macro in the build
46 * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
47 * the effective limit on the size of the header, define the macro
48 * to a very large number (e.g. -DCROW_HTTP_MAX_HEADER_SIZE=0x7fffffff)
49 */
50#ifndef CROW_HTTP_MAX_HEADER_SIZE
51# define CROW_HTTP_MAX_HEADER_SIZE (80*1024)
52#endif
53
54typedef struct http_parser http_parser;
55typedef struct http_parser_settings http_parser_settings;
56
57/* Callbacks should return non-zero to indicate an error. The parser will
58 * then halt execution.
59 *
60 * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
61 * returning '1' from on_headers_complete will tell the parser that it
62 * should not expect a body. This is used when receiving a response to a
63 * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
64 * chunked' headers that indicate the presence of a body.
65 *
66 * Returning `2` from on_headers_complete will tell parser that it should not
67 * expect neither a body nor any futher responses on this connection. This is
68 * useful for handling responses to a CONNECT request which may not contain
69 * `Upgrade` or `Connection: upgrade` headers.
70 *
71 * http_data_cb does not return data chunks. It will be called arbitrarally
72 * many times for each string. E.G. you might get 10 callbacks for "on_url"
73 * each providing just a few characters more data.
74 */
75typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
76typedef int (*http_cb) (http_parser*);
77
78
79/* Flag values for http_parser.flags field */
80enum http_connection_flags // This is basically 7 booleans placed into 1 integer. Uses 4 bytes instead of n bytes (7 currently).
81 { F_CHUNKED = 1 << 0 // 00000000 00000000 00000000 00000001
82 , F_CONNECTION_KEEP_ALIVE = 1 << 1 // 00000000 00000000 00000000 00000010
83 , F_CONNECTION_CLOSE = 1 << 2 // 00000000 00000000 00000000 00000100
84 , F_TRAILING = 1 << 3 // 00000000 00000000 00000000 00001000
85 , F_UPGRADE = 1 << 4 // 00000000 00000000 00000000 00010000
86 , F_SKIPBODY = 1 << 5 // 00000000 00000000 00000000 00100000
87 , F_CONTENTLENGTH = 1 << 6 // 00000000 00000000 00000000 01000000
88 };
89
90
91/* Map for errno-related constants
92 *
93 * The provided argument should be a macro that takes 2 arguments.
94 */
95#define CROW_HTTP_ERRNO_MAP(CROW_XX) \
96 /* No error */ \
97 CROW_XX(OK, "success") \
98 \
99 /* Callback-related errors */ \
100 CROW_XX(CB_message_begin, "the on_message_begin callback failed") \
101 CROW_XX(CB_method, "the on_method callback failed") \
102 CROW_XX(CB_url, "the \"on_url\" callback failed") \
103 CROW_XX(CB_header_field, "the \"on_header_field\" callback failed") \
104 CROW_XX(CB_header_value, "the \"on_header_value\" callback failed") \
105 CROW_XX(CB_headers_complete, "the \"on_headers_complete\" callback failed") \
106 CROW_XX(CB_body, "the \"on_body\" callback failed") \
107 CROW_XX(CB_message_complete, "the \"on_message_complete\" callback failed") \
108 CROW_XX(CB_status, "the \"on_status\" callback failed") \
109 \
110 /* Parsing-related errors */ \
111 CROW_XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
112 CROW_XX(HEADER_OVERFLOW, "too many header bytes seen; overflow detected") \
113 CROW_XX(CLOSED_CONNECTION, "data received after completed connection: close message") \
114 CROW_XX(INVALID_VERSION, "invalid HTTP version") \
115 CROW_XX(INVALID_STATUS, "invalid HTTP status code") \
116 CROW_XX(INVALID_METHOD, "invalid HTTP method") \
117 CROW_XX(INVALID_URL, "invalid URL") \
118 CROW_XX(INVALID_HOST, "invalid host") \
119 CROW_XX(INVALID_PORT, "invalid port") \
120 CROW_XX(INVALID_PATH, "invalid path") \
121 CROW_XX(INVALID_QUERY_STRING, "invalid query string") \
122 CROW_XX(INVALID_FRAGMENT, "invalid fragment") \
123 CROW_XX(LF_EXPECTED, "LF character expected") \
124 CROW_XX(INVALID_HEADER_TOKEN, "invalid character in header") \
125 CROW_XX(INVALID_CONTENT_LENGTH, "invalid character in content-length header") \
126 CROW_XX(UNEXPECTED_CONTENT_LENGTH, "unexpected content-length header") \
127 CROW_XX(INVALID_CHUNK_SIZE, "invalid character in chunk size header") \
128 CROW_XX(INVALID_CONSTANT, "invalid constant string") \
129 CROW_XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state") \
130 CROW_XX(STRICT, "strict mode assertion failed") \
131 CROW_XX(UNKNOWN, "an unknown error occurred") \
132 CROW_XX(INVALID_TRANSFER_ENCODING, "request has invalid transfer-encoding") \
133
134
135/* Define CHPE_* values for each errno value above */
136#define CROW_HTTP_ERRNO_GEN(n, s) CHPE_##n,
137enum http_errno {
138 CROW_HTTP_ERRNO_MAP(CROW_HTTP_ERRNO_GEN)
139};
140#undef CROW_HTTP_ERRNO_GEN
141
142
143/* Get an http_errno value from an http_parser */
144#define CROW_HTTP_PARSER_ERRNO(p) ((enum http_errno)(p)->http_errno)
145
146
148 {
149 /** PRIVATE **/
150 unsigned int flags : 7; /* F_* values from 'flags' enum; semi-public */
151 unsigned int state : 8; /* enum state from http_parser.c */
152 unsigned int header_state : 7; /* enum header_state from http_parser.c */
153 unsigned int index : 5; /* index into current matcher */
154 unsigned int uses_transfer_encoding : 1; /* Transfer-Encoding header is present */
155 unsigned int allow_chunked_length : 1; /* Allow headers with both `Content-Length` and `Transfer-Encoding: chunked` set */
156 unsigned int lenient_http_headers : 1;
157
158 uint32_t nread; /* # bytes read in various scenarios */
159 uint64_t content_length; /* # bytes in body. `(uint64_t) -1` (all bits one) if no Content-Length header. */
160 unsigned long qs_point;
161
162 /** READ-ONLY **/
163 unsigned char http_major;
164 unsigned char http_minor;
165 unsigned int method : 8; /* requests only */
166 unsigned int http_errno : 7;
167
168 /* 1 = Upgrade header was present and the parser has exited because of that.
169 * 0 = No upgrade header present.
170 * Should be checked when http_parser_execute() returns in addition to
171 * error checking.
172 */
173 unsigned int upgrade : 1;
174
175 /** PUBLIC **/
176 void* data; /* A pointer to get hook to the "connection" or "socket" object */
177 };
178
179
181 {
182 http_cb on_message_begin;
183 http_cb on_method;
184 http_data_cb on_url;
185 http_data_cb on_header_field;
186 http_data_cb on_header_value;
187 http_cb on_headers_complete;
188 http_data_cb on_body;
189 http_cb on_message_complete;
190 };
191
192
193
194// SOURCE (.c) CODE
195static uint32_t max_header_size = CROW_HTTP_MAX_HEADER_SIZE;
196
197#ifndef CROW_ULLONG_MAX
198# define CROW_ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
199#endif
200
201#ifndef CROW_MIN
202# define CROW_MIN(a,b) ((a) < (b) ? (a) : (b))
203#endif
204
205#ifndef CROW_ARRAY_SIZE
206# define CROW_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
207#endif
208
209#ifndef CROW_BIT_AT
210# define CROW_BIT_AT(a, i) \
211 (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
212 (1 << ((unsigned int) (i) & 7))))
213#endif
214
215#define CROW_SET_ERRNO(e) \
216do { \
217 parser->nread = nread; \
218 parser->http_errno = (e); \
219} while(0)
220
221/* Run the notify callback FOR, returning ER if it fails */
222#define CROW_CALLBACK_NOTIFY_(FOR, ER) \
223do { \
224 assert(CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK); \
225 \
226 if (CROW_LIKELY(settings->on_##FOR)) { \
227 if (CROW_UNLIKELY(0 != settings->on_##FOR(parser))) { \
228 CROW_SET_ERRNO(CHPE_CB_##FOR); \
229 } \
230 \
231 /* We either errored above or got paused; get out */ \
232 if (CROW_UNLIKELY(CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK)) { \
233 return (ER); \
234 } \
235 } \
236} while (0)
237
238/* Run the notify callback FOR and consume the current byte */
239#define CROW_CALLBACK_NOTIFY(FOR) CROW_CALLBACK_NOTIFY_(FOR, p - data + 1)
240
241/* Run the notify callback FOR and don't consume the current byte */
242#define CROW_CALLBACK_NOTIFY_NOADVANCE(FOR) CROW_CALLBACK_NOTIFY_(FOR, p - data)
243
244/* Run data callback FOR with LEN bytes, returning ER if it fails */
245#define CROW_CALLBACK_DATA_(FOR, LEN, ER) \
246do { \
247 assert(CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK); \
248 \
249 if (FOR##_mark) { \
250 if (CROW_LIKELY(settings->on_##FOR)) { \
251 if (CROW_UNLIKELY(0 != \
252 settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
253 CROW_SET_ERRNO(CHPE_CB_##FOR); \
254 } \
255 \
256 /* We either errored above or got paused; get out */ \
257 if (CROW_UNLIKELY(CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK)) {\
258 return (ER); \
259 } \
260 } \
261 FOR##_mark = NULL; \
262 } \
263} while (0)
264
265/* Run the data callback FOR and consume the current byte */
266#define CROW_CALLBACK_DATA(FOR) \
267 CROW_CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
268
269/* Run the data callback FOR and don't consume the current byte */
270#define CROW_CALLBACK_DATA_NOADVANCE(FOR) \
271 CROW_CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
272
273/* Set the mark FOR; non-destructive if mark is already set */
274#define CROW_MARK(FOR) \
275do { \
276 if (!FOR##_mark) { \
277 FOR##_mark = p; \
278 } \
279} while (0)
280
281/* Don't allow the total size of the HTTP headers (including the status
282 * line) to exceed max_header_size. This check is here to protect
283 * embedders against denial-of-service attacks where the attacker feeds
284 * us a never-ending header that the embedder keeps buffering.
285 *
286 * This check is arguably the responsibility of embedders but we're doing
287 * it on the embedder's behalf because most won't bother and this way we
288 * make the web a little safer. max_header_size is still far bigger
289 * than any reasonable request or response so this should never affect
290 * day-to-day operation.
291 */
292#define CROW_COUNT_HEADER_SIZE(V) \
293do { \
294 nread += (uint32_t)(V); \
295 if (CROW_UNLIKELY(nread > max_header_size)) { \
296 CROW_SET_ERRNO(CHPE_HEADER_OVERFLOW); \
297 goto error; \
298 } \
299} while (0)
300#define CROW_REEXECUTE() \
301 goto reexecute; \
302
303#define CROW_PROXY_CONNECTION "proxy-connection"
304#define CROW_CONNECTION "connection"
305#define CROW_CONTENT_LENGTH "content-length"
306#define CROW_TRANSFER_ENCODING "transfer-encoding"
307#define CROW_UPGRADE "upgrade"
308#define CROW_CHUNKED "chunked"
309#define CROW_KEEP_ALIVE "keep-alive"
310#define CROW_CLOSE "close"
311
312
313
314 enum state
315 {
316 s_dead = 1 /* important that this is > 0 */
317
318 ,
319 s_start_req
320
321 ,
322 s_req_method,
323 s_req_spaces_before_url,
324 s_req_schema,
325 s_req_schema_slash,
326 s_req_schema_slash_slash,
327 s_req_server_start,
328 s_req_server, // }
329 s_req_server_with_at, // |
330 s_req_path, // | The parser recognizes how to switch between these states,
331 s_req_query_string_start, // | however it doesn't process them any differently.
332 s_req_query_string, // }
333 s_req_http_start,
334 s_req_http_H,
335 s_req_http_HT,
336 s_req_http_HTT,
337 s_req_http_HTTP,
338 s_req_http_I,
339 s_req_http_IC,
340 s_req_http_major,
341 s_req_http_dot,
342 s_req_http_minor,
343 s_req_http_end,
344 s_req_line_almost_done
345
346 ,
347 s_header_field_start,
348 s_header_field,
349 s_header_value_discard_ws,
350 s_header_value_discard_ws_almost_done,
351 s_header_value_discard_lws,
352 s_header_value_start,
353 s_header_value,
354 s_header_value_lws
355
356 ,
357 s_header_almost_done
358
359 ,
360 s_chunk_size_start,
361 s_chunk_size,
362 s_chunk_parameters,
363 s_chunk_size_almost_done
364
365 ,
366 s_headers_almost_done,
367 s_headers_done
368
369 /* Important: 's_headers_done' must be the last 'header' state. All
370 * states beyond this must be 'body' states. It is used for overflow
371 * checking. See the CROW_PARSING_HEADER() macro.
372 */
373
374 ,
375 s_chunk_data,
376 s_chunk_data_almost_done,
377 s_chunk_data_done
378
379 ,
380 s_body_identity,
381 s_body_identity_eof
382
383 ,
384 s_message_done
385 };
386
387
388#define CROW_PARSING_HEADER(state) (state <= s_headers_done)
389
390
391enum header_states
392 { h_general = 0
393 , h_C
394 , h_CO
395 , h_CON
396
397 , h_matching_connection
398 , h_matching_proxy_connection
399 , h_matching_content_length
400 , h_matching_transfer_encoding
401 , h_matching_upgrade
402
403 , h_connection
404 , h_content_length
405 , h_content_length_num
406 , h_content_length_ws
407 , h_transfer_encoding
408 , h_upgrade
409
410 , h_matching_transfer_encoding_token_start
411 , h_matching_transfer_encoding_chunked
412 , h_matching_transfer_encoding_token
413
414 , h_matching_connection_keep_alive
415 , h_matching_connection_close
416
417 , h_transfer_encoding_chunked
418 , h_connection_keep_alive
419 , h_connection_close
420 };
421
422enum http_host_state
423 {
424 s_http_host_dead = 1
425 , s_http_userinfo_start
426 , s_http_userinfo
427 , s_http_host_start
428 , s_http_host_v6_start
429 , s_http_host
430 , s_http_host_v6
431 , s_http_host_v6_end
432 , s_http_host_v6_zone_start
433 , s_http_host_v6_zone
434 , s_http_host_port_start
435 , s_http_host_port
436};
437
438/* Macros for character classes; depends on strict-mode */
439#define CROW_LOWER(c) (unsigned char)(c | 0x20)
440#define CROW_IS_ALPHA(c) (CROW_LOWER(c) >= 'a' && CROW_LOWER(c) <= 'z')
441#define CROW_IS_NUM(c) ((c) >= '0' && (c) <= '9')
442#define CROW_IS_ALPHANUM(c) (CROW_IS_ALPHA(c) || CROW_IS_NUM(c))
443//#define CROW_IS_HEX(c) (CROW_IS_NUM(c) || (CROW_LOWER(c) >= 'a' && CROW_LOWER(c) <= 'f'))
444#define CROW_IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
445 (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
446 (c) == ')')
447#define CROW_IS_USERINFO_CHAR(c) (CROW_IS_ALPHANUM(c) || CROW_IS_MARK(c) || (c) == '%' || \
448 (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
449 (c) == '$' || (c) == ',')
450
451#define CROW_TOKEN(c) (tokens[(unsigned char)c])
452#define CROW_IS_URL_CHAR(c) (CROW_BIT_AT(normal_url_char, (unsigned char)c))
453//#define CROW_IS_HOST_CHAR(c) (CROW_IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
454
455 /**
456 * Verify that a char is a valid visible (printable) US-ASCII
457 * character or %x80-FF
458 **/
459#define CROW_IS_HEADER_CHAR(ch) \
460 (ch == cr || ch == lf || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
461
462#define CROW_start_state s_start_req
463
464# define CROW_STRICT_CHECK(cond) \
465do { \
466 if (cond) { \
467 CROW_SET_ERRNO(CHPE_STRICT); \
468 goto error; \
469 } \
470} while (0)
471#define CROW_NEW_MESSAGE() (CROW_start_state)
472
473/* Our URL parser.
474 *
475 * This is designed to be shared by http_parser_execute() for URL validation,
476 * hence it has a state transition + byte-for-byte interface. In addition, it
477 * is meant to be embedded in http_parser_parse_url(), which does the dirty
478 * work of turning state transitions URL components for its API.
479 *
480 * This function should only be invoked with non-space characters. It is
481 * assumed that the caller cares about (and can detect) the transition between
482 * URL and non-URL states by looking for these.
483 */
484inline enum state
485parse_url_char(enum state s, const char ch, http_parser *parser, const char* url_mark, const char* p)
486{
487# define CROW_T(v) 0
488
489
490static const uint8_t normal_url_char[32] = {
491/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
492 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
493/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
494 0 |CROW_T(2)| 0 | 0 |CROW_T(16)| 0 | 0 | 0,
495/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
496 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
497/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
498 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
499/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
500 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
501/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
502 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
503/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
504 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
505/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
506 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
507/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
508 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
509/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
510 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
511/* 80 P 81 Q 82 R 83 S 84 CROW_T 85 U 86 V 87 W */
512 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
513/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
514 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
515/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
516 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
517/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
518 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
519/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
520 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
521/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
522 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
523
524#undef CROW_T
525
526 if (ch == ' ' || ch == '\r' || ch == '\n') {
527 return s_dead;
528 }
529 if (ch == '\t' || ch == '\f') {
530 return s_dead;
531 }
532
533 switch (s) {
534 case s_req_spaces_before_url:
535 /* Proxied requests are followed by scheme of an absolute URI (alpha).
536 * All methods except CONNECT are followed by '/' or '*'.
537 */
538
539 if (ch == '/' || ch == '*') {
540 return s_req_path;
541 }
542
543 if (CROW_IS_ALPHA(ch)) {
544 return s_req_schema;
545 }
546
547 break;
548
549 case s_req_schema:
550 if (CROW_IS_ALPHA(ch)) {
551 return s;
552 }
553
554 if (ch == ':') {
555 return s_req_schema_slash;
556 }
557
558 break;
559
560 case s_req_schema_slash:
561 if (ch == '/') {
562 return s_req_schema_slash_slash;
563 }
564
565 break;
566
567 case s_req_schema_slash_slash:
568 if (ch == '/') {
569 return s_req_server_start;
570 }
571
572 break;
573
574 case s_req_server_with_at:
575 if (ch == '@') {
576 return s_dead;
577 }
578
579 /* fall through */
580 case s_req_server_start:
581 case s_req_server:
582 if (ch == '/') {
583 return s_req_path;
584 }
585
586 if (ch == '?') {
587 parser->qs_point = p - url_mark;
588 return s_req_query_string_start;
589 }
590
591 if (ch == '@') {
592 return s_req_server_with_at;
593 }
594
595 if (CROW_IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
596 return s_req_server;
597 }
598
599 break;
600
601 case s_req_path:
602 if (CROW_IS_URL_CHAR(ch)) {
603 return s;
604 }
605 else if (ch == '?')
606 {
607 parser->qs_point = p - url_mark;
608 return s_req_query_string_start;
609 }
610
611 break;
612
613 case s_req_query_string_start:
614 case s_req_query_string:
615 if (CROW_IS_URL_CHAR(ch)) {
616 return s_req_query_string;
617 }
618 else if (ch == '?')
619 {
620 return s_req_query_string;
621 }
622
623 break;
624
625 default:
626 break;
627 }
628
629 /* We should never fall out of the switch above unless there's an error */
630 return s_dead;
631}
632
633inline size_t http_parser_execute (http_parser *parser,
634 const http_parser_settings *settings,
635 const char *data,
636 size_t len)
637{
638
639/* Tokens as defined by rfc 2616. Also lowercases them.
640 * token = 1*<any CHAR except CTLs or separators>
641 * separators = "(" | ")" | "<" | ">" | "@"
642 * | "," | ";" | ":" | "\" | <">
643 * | "/" | "[" | "]" | "?" | "="
644 * | "{" | "}" | SP | HT
645 */
646static const char tokens[256] = {
647/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
648 0, 0, 0, 0, 0, 0, 0, 0,
649/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
650 0, 0, 0, 0, 0, 0, 0, 0,
651/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
652 0, 0, 0, 0, 0, 0, 0, 0,
653/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
654 0, 0, 0, 0, 0, 0, 0, 0,
655/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
656 0, '!', 0, '#', '$', '%', '&', '\'',
657/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
658 0, 0, '*', '+', 0, '-', '.', 0,
659/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
660 '0', '1', '2', '3', '4', '5', '6', '7',
661/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
662 '8', '9', 0, 0, 0, 0, 0, 0,
663/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
664 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
665/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
666 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
667/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
668 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
669/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
670 'x', 'y', 'z', 0, 0, 0, '^', '_',
671/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
672 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
673/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
674 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
675/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
676 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
677/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
678 'x', 'y', 'z', 0, '|', 0, '~', 0 };
679
680
681static const int8_t unhex[256] =
682 {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
683 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
684 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
685 , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
686 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
687 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
688 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
689 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
690 };
691
692
693
694 char c, ch;
695 int8_t unhex_val;
696 const char *p = data;
697 const char *header_field_mark = 0;
698 const char *header_value_mark = 0;
699 const char *url_mark = 0;
700 const char *url_start_mark = 0;
701 const char *body_mark = 0;
702 const unsigned int lenient = parser->lenient_http_headers;
703 const unsigned int allow_chunked_length = parser->allow_chunked_length;
704
705 uint32_t nread = parser->nread;
706
707 /* We're in an error state. Don't bother doing anything. */
708 if (CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK) {
709 return 0;
710 }
711
712 if (len == 0) {
713 switch (parser->state) {
714 case s_body_identity_eof:
715 /* Use of CROW_CALLBACK_NOTIFY() here would erroneously return 1 byte read if we got paused. */
716 CROW_CALLBACK_NOTIFY_NOADVANCE(message_complete);
717 return 0;
718
719 case s_dead:
720 case s_start_req:
721 return 0;
722
723 default:
724 CROW_SET_ERRNO(CHPE_INVALID_EOF_STATE);
725 return 1;
726 }
727 }
728
729
730 if (parser->state == s_header_field)
731 header_field_mark = data;
732 if (parser->state == s_header_value)
733 header_value_mark = data;
734 switch (parser->state) {
735 case s_req_path:
736 case s_req_schema:
737 case s_req_schema_slash:
738 case s_req_schema_slash_slash:
739 case s_req_server_start:
740 case s_req_server:
741 case s_req_server_with_at:
742 case s_req_query_string_start:
743 case s_req_query_string:
744 url_mark = data;
745 break;
746 default:
747 break;
748 }
749
750 for (p=data; p != data + len; p++) {
751 ch = *p;
752
753 if (CROW_PARSING_HEADER(parser->state))
754 CROW_COUNT_HEADER_SIZE(1);
755
756reexecute:
757 switch (parser->state) {
758
759 case s_dead:
760 /* this state is used after a 'Connection: close' message
761 * the parser will error out if it reads another message
762 */
763 if (CROW_LIKELY(ch == cr || ch == lf))
764 break;
765
766 CROW_SET_ERRNO(CHPE_CLOSED_CONNECTION);
767 goto error;
768
769 case s_start_req:
770 {
771 if (ch == cr || ch == lf)
772 break;
773 parser->flags = 0;
774 parser->uses_transfer_encoding = 0;
775 parser->content_length = CROW_ULLONG_MAX;
776
777 if (CROW_UNLIKELY(!CROW_IS_ALPHA(ch))) {
778 CROW_SET_ERRNO(CHPE_INVALID_METHOD);
779 goto error;
780 }
781
782 parser->method = 0;
783 parser->index = 1;
784 switch (ch) {
785 case 'A': parser->method = (unsigned)HTTPMethod::Acl; break;
786 case 'B': parser->method = (unsigned)HTTPMethod::Bind; break;
787 case 'C': parser->method = (unsigned)HTTPMethod::Connect; /* or COPY, CHECKOUT */ break;
788 case 'D': parser->method = (unsigned)HTTPMethod::Delete; break;
789 case 'G': parser->method = (unsigned)HTTPMethod::Get; break;
790 case 'H': parser->method = (unsigned)HTTPMethod::Head; break;
791 case 'L': parser->method = (unsigned)HTTPMethod::Lock; /* or LINK */ break;
792 case 'M': parser->method = (unsigned)HTTPMethod::MkCol; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
793 case 'N': parser->method = (unsigned)HTTPMethod::Notify; break;
794 case 'O': parser->method = (unsigned)HTTPMethod::Options; break;
795 case 'P': parser->method = (unsigned)HTTPMethod::Post; /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ break;
796 case 'R': parser->method = (unsigned)HTTPMethod::Report; /* or REBIND */ break;
797 case 'S': parser->method = (unsigned)HTTPMethod::Subscribe; /* or SEARCH, SOURCE */ break;
798 case 'T': parser->method = (unsigned)HTTPMethod::Trace; break;
799 case 'U': parser->method = (unsigned)HTTPMethod::Unlock; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
800 default:
801 CROW_SET_ERRNO(CHPE_INVALID_METHOD);
802 goto error;
803 }
804 parser->state = s_req_method;
805
806 CROW_CALLBACK_NOTIFY(message_begin);
807
808 break;
809 }
810
811 case s_req_method:
812 {
813 const char *matcher;
814 if (CROW_UNLIKELY(ch == '\0')) {
815 CROW_SET_ERRNO(CHPE_INVALID_METHOD);
816 goto error;
817 }
818
819 matcher = method_strings[parser->method];
820 if (ch == ' ' && matcher[parser->index] == '\0') {
821 parser->state = s_req_spaces_before_url;
822 } else if (ch == matcher[parser->index]) {
823 ; /* nada */
824 } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
825
826 switch (parser->method << 16 | parser->index << 8 | ch) {
827#define CROW_XX(meth, pos, ch, new_meth) \
828 case ((unsigned)HTTPMethod::meth << 16 | pos << 8 | ch): \
829 parser->method = (unsigned)HTTPMethod::new_meth; break;
830
831 CROW_XX(Post, 1, 'U', Put)
832 CROW_XX(Post, 1, 'A', Patch)
833 CROW_XX(Post, 1, 'R', Propfind)
834 CROW_XX(Put, 2, 'R', Purge)
835 CROW_XX(Connect, 1, 'H', Checkout)
836 CROW_XX(Connect, 2, 'P', Copy)
837 CROW_XX(MkCol, 1, 'O', Move)
838 CROW_XX(MkCol, 1, 'E', Merge)
839 CROW_XX(MkCol, 1, '-', MSearch)
840 CROW_XX(MkCol, 2, 'A', MkActivity)
841 CROW_XX(MkCol, 3, 'A', MkCalendar)
842 CROW_XX(Subscribe, 1, 'E', Search)
843 CROW_XX(Subscribe, 1, 'O', Source)
844 CROW_XX(Report, 2, 'B', Rebind)
845 CROW_XX(Propfind, 4, 'P', Proppatch)
846 CROW_XX(Lock, 1, 'I', Link)
847 CROW_XX(Unlock, 2, 'S', Unsubscribe)
848 CROW_XX(Unlock, 2, 'B', Unbind)
849 CROW_XX(Unlock, 3, 'I', Unlink)
850#undef CROW_XX
851 default:
852 CROW_SET_ERRNO(CHPE_INVALID_METHOD);
853 goto error;
854 }
855 } else {
856 CROW_SET_ERRNO(CHPE_INVALID_METHOD);
857 goto error;
858 }
859
860 CROW_CALLBACK_NOTIFY_NOADVANCE(method);
861
862 ++parser->index;
863 break;
864 }
865
866 case s_req_spaces_before_url:
867 {
868 if (ch == ' ') break;
869
870 CROW_MARK(url);
871 CROW_MARK(url_start);
872 if (parser->method == (unsigned)HTTPMethod::Connect) {
873 parser->state = s_req_server_start;
874 }
875
876 parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p);
877 if (CROW_UNLIKELY(parser->state == s_dead)) {
878 CROW_SET_ERRNO(CHPE_INVALID_URL);
879 goto error;
880 }
881
882 break;
883 }
884
885 case s_req_schema:
886 case s_req_schema_slash:
887 case s_req_schema_slash_slash:
888 case s_req_server_start:
889 {
890 switch (ch) {
891 /* No whitespace allowed here */
892 case ' ':
893 case cr:
894 case lf:
895 CROW_SET_ERRNO(CHPE_INVALID_URL);
896 goto error;
897 default:
898 parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p);
899 if (CROW_UNLIKELY(parser->state == s_dead)) {
900 CROW_SET_ERRNO(CHPE_INVALID_URL);
901 goto error;
902 }
903 }
904
905 break;
906 }
907
908 case s_req_server:
909 case s_req_server_with_at:
910 case s_req_path:
911 case s_req_query_string_start:
912 case s_req_query_string:
913 {
914 switch (ch) {
915 case ' ':
916 parser->state = s_req_http_start;
917 CROW_CALLBACK_DATA(url);
918 break;
919 case cr: // No space after URL means no HTTP version. Which means the request is using HTTP/0.9
920 case lf:
921 if (CROW_UNLIKELY(parser->method != (unsigned)HTTPMethod::Get)) // HTTP/0.9 doesn't define any method other than GET
922 {
923 parser->state = s_dead;
924 CROW_SET_ERRNO(CHPE_INVALID_VERSION);
925 goto error;
926 }
927 parser->http_major = 0;
928 parser->http_minor = 9;
929 parser->state = (ch == cr) ?
930 s_req_line_almost_done :
931 s_header_field_start;
932 CROW_CALLBACK_DATA(url);
933 break;
934 default:
935 parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p);
936 if (CROW_UNLIKELY(parser->state == s_dead)) {
937 CROW_SET_ERRNO(CHPE_INVALID_URL);
938 goto error;
939 }
940 }
941 break;
942 }
943
944 case s_req_http_start:
945 switch (ch) {
946 case ' ':
947 break;
948 case 'H':
949 parser->state = s_req_http_H;
950 break;
951 case 'I':
952 if (parser->method == (unsigned)HTTPMethod::Source) {
953 parser->state = s_req_http_I;
954 break;
955 }
956 /* fall through */
957 default:
958 CROW_SET_ERRNO(CHPE_INVALID_CONSTANT);
959 goto error;
960 }
961 break;
962
963 case s_req_http_H:
964 CROW_STRICT_CHECK(ch != 'T');
965 parser->state = s_req_http_HT;
966 break;
967
968 case s_req_http_HT:
969 CROW_STRICT_CHECK(ch != 'T');
970 parser->state = s_req_http_HTT;
971 break;
972
973 case s_req_http_HTT:
974 CROW_STRICT_CHECK(ch != 'P');
975 parser->state = s_req_http_HTTP;
976 break;
977
978 case s_req_http_I:
979 CROW_STRICT_CHECK(ch != 'C');
980 parser->state = s_req_http_IC;
981 break;
982
983 case s_req_http_IC:
984 CROW_STRICT_CHECK(ch != 'E');
985 parser->state = s_req_http_HTTP; /* Treat "ICE" as "HTTP". */
986 break;
987
988 case s_req_http_HTTP:
989 CROW_STRICT_CHECK(ch != '/');
990 parser->state = s_req_http_major;
991 break;
992
993 /* dot */
994 case s_req_http_major:
995 if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) {
996 CROW_SET_ERRNO(CHPE_INVALID_VERSION);
997 goto error;
998 }
999
1000 parser->http_major = ch - '0';
1001 parser->state = s_req_http_dot;
1002 break;
1003
1004 case s_req_http_dot:
1005 {
1006 if (CROW_UNLIKELY(ch != '.')) {
1007 CROW_SET_ERRNO(CHPE_INVALID_VERSION);
1008 goto error;
1009 }
1010
1011 parser->state = s_req_http_minor;
1012 break;
1013 }
1014
1015 /* minor HTTP version */
1016 case s_req_http_minor:
1017 if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) {
1018 CROW_SET_ERRNO(CHPE_INVALID_VERSION);
1019 goto error;
1020 }
1021
1022 parser->http_minor = ch - '0';
1023 parser->state = s_req_http_end;
1024 break;
1025
1026 /* end of request line */
1027 case s_req_http_end:
1028 {
1029 if (ch == cr) {
1030 parser->state = s_req_line_almost_done;
1031 break;
1032 }
1033
1034 if (ch == lf) {
1035 parser->state = s_header_field_start;
1036 break;
1037 }
1038
1039 CROW_SET_ERRNO(CHPE_INVALID_VERSION);
1040 goto error;
1041 break;
1042 }
1043
1044 /* end of request line */
1045 case s_req_line_almost_done:
1046 {
1047 if (CROW_UNLIKELY(ch != lf)) {
1048 CROW_SET_ERRNO(CHPE_LF_EXPECTED);
1049 goto error;
1050 }
1051
1052 parser->state = s_header_field_start;
1053 break;
1054 }
1055
1056 case s_header_field_start:
1057 {
1058 if (ch == cr) {
1059 parser->state = s_headers_almost_done;
1060 break;
1061 }
1062
1063 if (ch == lf) {
1064 /* they might be just sending \n instead of \r\n so this would be
1065 * the second \n to denote the end of headers*/
1066 parser->state = s_headers_almost_done;
1067 CROW_REEXECUTE();
1068 }
1069
1070 c = CROW_TOKEN(ch);
1071
1072 if (CROW_UNLIKELY(!c)) {
1073 CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN);
1074 goto error;
1075 }
1076
1077 CROW_MARK(header_field);
1078
1079 parser->index = 0;
1080 parser->state = s_header_field;
1081
1082 switch (c) {
1083 case 'c':
1084 parser->header_state = h_C;
1085 break;
1086
1087 case 'p':
1088 parser->header_state = h_matching_proxy_connection;
1089 break;
1090
1091 case 't':
1092 parser->header_state = h_matching_transfer_encoding;
1093 break;
1094
1095 case 'u':
1096 parser->header_state = h_matching_upgrade;
1097 break;
1098
1099 default:
1100 parser->header_state = h_general;
1101 break;
1102 }
1103 break;
1104 }
1105
1106 case s_header_field:
1107 {
1108 const char* start = p;
1109 for (; p != data + len; p++) {
1110 ch = *p;
1111 c = CROW_TOKEN(ch);
1112
1113 if (!c)
1114 break;
1115
1116 switch (parser->header_state) {
1117 case h_general: {
1118 size_t left = data + len - p;
1119 const char* pe = p + CROW_MIN(left, max_header_size);
1120 while (p+1 < pe && CROW_TOKEN(p[1])) {
1121 p++;
1122 }
1123 break;
1124 }
1125
1126 case h_C:
1127 parser->index++;
1128 parser->header_state = (c == 'o' ? h_CO : h_general);
1129 break;
1130
1131 case h_CO:
1132 parser->index++;
1133 parser->header_state = (c == 'n' ? h_CON : h_general);
1134 break;
1135
1136 case h_CON:
1137 parser->index++;
1138 switch (c) {
1139 case 'n':
1140 parser->header_state = h_matching_connection;
1141 break;
1142 case 't':
1143 parser->header_state = h_matching_content_length;
1144 break;
1145 default:
1146 parser->header_state = h_general;
1147 break;
1148 }
1149 break;
1150
1151 /* connection */
1152
1153 case h_matching_connection:
1154 parser->index++;
1155 if (parser->index > sizeof(CROW_CONNECTION)-1 || c != CROW_CONNECTION[parser->index]) {
1156 parser->header_state = h_general;
1157 } else if (parser->index == sizeof(CROW_CONNECTION)-2) {
1158 parser->header_state = h_connection;
1159 }
1160 break;
1161
1162 /* proxy-connection */
1163
1164 case h_matching_proxy_connection:
1165 parser->index++;
1166 if (parser->index > sizeof(CROW_PROXY_CONNECTION)-1 || c != CROW_PROXY_CONNECTION[parser->index]) {
1167 parser->header_state = h_general;
1168 } else if (parser->index == sizeof(CROW_PROXY_CONNECTION)-2) {
1169 parser->header_state = h_connection;
1170 }
1171 break;
1172
1173 /* content-length */
1174
1175 case h_matching_content_length:
1176 parser->index++;
1177 if (parser->index > sizeof(CROW_CONTENT_LENGTH)-1 || c != CROW_CONTENT_LENGTH[parser->index]) {
1178 parser->header_state = h_general;
1179 } else if (parser->index == sizeof(CROW_CONTENT_LENGTH)-2) {
1180 parser->header_state = h_content_length;
1181 }
1182 break;
1183
1184 /* transfer-encoding */
1185
1186 case h_matching_transfer_encoding:
1187 parser->index++;
1188 if (parser->index > sizeof(CROW_TRANSFER_ENCODING)-1 || c != CROW_TRANSFER_ENCODING[parser->index]) {
1189 parser->header_state = h_general;
1190 } else if (parser->index == sizeof(CROW_TRANSFER_ENCODING)-2) {
1191 parser->header_state = h_transfer_encoding;
1192 parser->uses_transfer_encoding = 1;
1193 }
1194 break;
1195
1196 /* upgrade */
1197
1198 case h_matching_upgrade:
1199 parser->index++;
1200 if (parser->index > sizeof(CROW_UPGRADE)-1 || c != CROW_UPGRADE[parser->index]) {
1201 parser->header_state = h_general;
1202 } else if (parser->index == sizeof(CROW_UPGRADE)-2) {
1203 parser->header_state = h_upgrade;
1204 }
1205 break;
1206
1207 case h_connection:
1208 case h_content_length:
1209 case h_transfer_encoding:
1210 case h_upgrade:
1211 if (ch != ' ') parser->header_state = h_general;
1212 break;
1213
1214 default:
1215 assert(0 && "Unknown header_state");
1216 break;
1217 }
1218 }
1219
1220 if (p == data + len) {
1221 --p;
1222 CROW_COUNT_HEADER_SIZE(p - start);
1223 break;
1224 }
1225
1226 CROW_COUNT_HEADER_SIZE(p - start);
1227
1228 if (ch == ':') {
1229 parser->state = s_header_value_discard_ws;
1230 CROW_CALLBACK_DATA(header_field);
1231 break;
1232 }
1233/* RFC-7230 Sec 3.2.4 expressly forbids line-folding in header field-names.
1234 if (ch == cr) {
1235 parser->state = s_header_almost_done;
1236 CROW_CALLBACK_DATA(header_field);
1237 break;
1238 }
1239
1240 if (ch == lf) {
1241 parser->state = s_header_field_start;
1242 CROW_CALLBACK_DATA(header_field);
1243 break;
1244 }
1245*/
1246 CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN);
1247 goto error;
1248 }
1249
1250 case s_header_value_discard_ws:
1251 if (ch == ' ' || ch == '\t') break;
1252
1253 if (ch == cr) {
1254 parser->state = s_header_value_discard_ws_almost_done;
1255 break;
1256 }
1257
1258 if (ch == lf) {
1259 parser->state = s_header_value_discard_lws;
1260 break;
1261 }
1262
1263 /* fall through */
1264
1265 case s_header_value_start:
1266 {
1267 CROW_MARK(header_value);
1268
1269 parser->state = s_header_value;
1270 parser->index = 0;
1271
1272 c = CROW_LOWER(ch);
1273
1274 switch (parser->header_state) {
1275 case h_upgrade:
1276 // Crow does not support HTTP/2 at the moment.
1277 // According to the RFC https://datatracker.ietf.org/doc/html/rfc7540#section-3.2
1278 // "A server that does not support HTTP/2 can respond to the request as though the Upgrade header field were absent"
1279 // => `F_UPGRADE` is not set if the header starts by "h2".
1280 // This prevents the parser from skipping the request body.
1281 if (ch != 'h' || p+1 == (data + len) || *(p+1) != '2') {
1282 parser->flags |= F_UPGRADE;
1283 }
1284 parser->header_state = h_general;
1285 break;
1286
1287 case h_transfer_encoding:
1288 /* looking for 'Transfer-Encoding: chunked' */
1289 if ('c' == c) {
1290 parser->header_state = h_matching_transfer_encoding_chunked;
1291 } else {
1292 parser->header_state = h_matching_transfer_encoding_token;
1293 }
1294 break;
1295
1296 /* Multi-value `Transfer-Encoding` header */
1297 case h_matching_transfer_encoding_token_start:
1298 break;
1299
1300 case h_content_length:
1301 if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) {
1302 CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1303 goto error;
1304 }
1305
1306 if (parser->flags & F_CONTENTLENGTH) {
1307 CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH);
1308 goto error;
1309 }
1310 parser->flags |= F_CONTENTLENGTH;
1311 parser->content_length = ch - '0';
1312 parser->header_state = h_content_length_num;
1313 break;
1314
1315 /* when obsolete line folding is encountered for content length
1316 * continue to the s_header_value state */
1317 case h_content_length_ws:
1318 break;
1319
1320 case h_connection:
1321 /* looking for 'Connection: keep-alive' */
1322 if (c == 'k') {
1323 parser->header_state = h_matching_connection_keep_alive;
1324 /* looking for 'Connection: close' */
1325 } else if (c == 'c') {
1326 parser->header_state = h_matching_connection_close;
1327 } else if (c == ' ' || c == '\t') {
1328 /* Skip lws */
1329 } else {
1330 parser->header_state = h_general;
1331 }
1332 break;
1333
1334 default:
1335 parser->header_state = h_general;
1336 break;
1337 }
1338 break;
1339 }
1340
1341 case s_header_value:
1342 {
1343 const char* start = p;
1344 enum header_states h_state = static_cast<header_states>(parser->header_state);
1345 for (; p != data + len; p++) {
1346 ch = *p;
1347
1348 if (ch == cr) {
1349 parser->state = s_header_almost_done;
1350 parser->header_state = h_state;
1351 CROW_CALLBACK_DATA(header_value);
1352 break;
1353 }
1354
1355 if (ch == lf) {
1356 parser->state = s_header_almost_done;
1357 CROW_COUNT_HEADER_SIZE(p - start);
1358 parser->header_state = h_state;
1359 CROW_CALLBACK_DATA_NOADVANCE(header_value);
1360 CROW_REEXECUTE();
1361 }
1362
1363 if (!lenient && !CROW_IS_HEADER_CHAR(ch)) {
1364 CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN);
1365 goto error;
1366 }
1367
1368 c = CROW_LOWER(ch);
1369
1370 switch (h_state) {
1371 case h_general:
1372 {
1373 size_t left = data + len - p;
1374 const char* pe = p + CROW_MIN(left, max_header_size);
1375
1376 for (; p != pe; p++) {
1377 ch = *p;
1378 if (ch == cr || ch == lf) {
1379 --p;
1380 break;
1381 }
1382 if (!lenient && !CROW_IS_HEADER_CHAR(ch)) {
1383 CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN);
1384 goto error;
1385 }
1386 }
1387 if (p == data + len)
1388 --p;
1389 break;
1390 }
1391
1392 case h_connection:
1393 case h_transfer_encoding:
1394 assert(0 && "Shouldn't get here.");
1395 break;
1396
1397 case h_content_length:
1398 if (ch == ' ') break;
1399 h_state = h_content_length_num;
1400 /* fall through */
1401
1402 case h_content_length_num:
1403 {
1404 uint64_t t;
1405
1406 if (ch == ' ') {
1407 h_state = h_content_length_ws;
1408 break;
1409 }
1410
1411 if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) {
1412 CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1413 parser->header_state = h_state;
1414 goto error;
1415 }
1416
1417 t = parser->content_length;
1418 t *= 10;
1419 t += ch - '0';
1420
1421 /* Overflow? Test against a conservative limit for simplicity. */
1422 if (CROW_UNLIKELY((CROW_ULLONG_MAX - 10) / 10 < parser->content_length)) {
1423 CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1424 parser->header_state = h_state;
1425 goto error;
1426 }
1427
1428 parser->content_length = t;
1429 break;
1430 }
1431
1432 case h_content_length_ws:
1433 if (ch == ' ') break;
1434 CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1435 parser->header_state = h_state;
1436 goto error;
1437
1438 /* Transfer-Encoding: chunked */
1439 case h_matching_transfer_encoding_token_start:
1440 /* looking for 'Transfer-Encoding: chunked' */
1441 if ('c' == c) {
1442 h_state = h_matching_transfer_encoding_chunked;
1443 } else if (CROW_TOKEN(c)) {
1444 /* TODO(indutny): similar code below does this, but why?
1445 * At the very least it seems to be inconsistent given that
1446 * h_matching_transfer_encoding_token does not check for
1447 * `STRICT_TOKEN`
1448 */
1449 h_state = h_matching_transfer_encoding_token;
1450 } else if (c == ' ' || c == '\t') {
1451 /* Skip lws */
1452 } else {
1453 h_state = h_general;
1454 }
1455 break;
1456
1457 case h_matching_transfer_encoding_chunked:
1458 parser->index++;
1459 if (parser->index > sizeof(CROW_CHUNKED)-1 || c != CROW_CHUNKED[parser->index]) {
1460 h_state = h_matching_transfer_encoding_token;
1461 } else if (parser->index == sizeof(CROW_CHUNKED)-2) {
1462 h_state = h_transfer_encoding_chunked;
1463 }
1464 break;
1465
1466 case h_matching_transfer_encoding_token:
1467 if (ch == ',') {
1468 h_state = h_matching_transfer_encoding_token_start;
1469 parser->index = 0;
1470 }
1471 break;
1472
1473 /* looking for 'Connection: keep-alive' */
1474 case h_matching_connection_keep_alive:
1475 parser->index++;
1476 if (parser->index > sizeof(CROW_KEEP_ALIVE)-1 || c != CROW_KEEP_ALIVE[parser->index]) {
1477 h_state = h_general;
1478 } else if (parser->index == sizeof(CROW_KEEP_ALIVE)-2) {
1479 h_state = h_connection_keep_alive;
1480 }
1481 break;
1482
1483 /* looking for 'Connection: close' */
1484 case h_matching_connection_close:
1485 parser->index++;
1486 if (parser->index > sizeof(CROW_CLOSE)-1 || c != CROW_CLOSE[parser->index]) {
1487 h_state = h_general;
1488 } else if (parser->index == sizeof(CROW_CLOSE)-2) {
1489 h_state = h_connection_close;
1490 }
1491 break;
1492
1493 // Edited from original (because of commits that werent included)
1494 case h_transfer_encoding_chunked:
1495 if (ch != ' ') h_state = h_matching_transfer_encoding_token;
1496 break;
1497 case h_connection_keep_alive:
1498 case h_connection_close:
1499 if (ch != ' ') h_state = h_general;
1500 break;
1501
1502 default:
1503 parser->state = s_header_value;
1504 h_state = h_general;
1505 break;
1506 }
1507 }
1508 parser->header_state = h_state;
1509
1510
1511 if (p == data + len)
1512 --p;
1513
1514 CROW_COUNT_HEADER_SIZE(p - start);
1515 break;
1516 }
1517
1518 case s_header_almost_done:
1519 {
1520 if (CROW_UNLIKELY(ch != lf)) {
1521 CROW_SET_ERRNO(CHPE_LF_EXPECTED);
1522 goto error;
1523 }
1524
1525 parser->state = s_header_value_lws;
1526 break;
1527 }
1528
1529 case s_header_value_lws:
1530 {
1531 if (ch == ' ' || ch == '\t') {
1532 if (parser->header_state == h_content_length_num) {
1533 /* treat obsolete line folding as space */
1534 parser->header_state = h_content_length_ws;
1535 }
1536 parser->state = s_header_value_start;
1537 CROW_REEXECUTE();
1538 }
1539
1540 /* finished the header */
1541 switch (parser->header_state) {
1542 case h_connection_keep_alive:
1543 parser->flags |= F_CONNECTION_KEEP_ALIVE;
1544 break;
1545 case h_connection_close:
1546 parser->flags |= F_CONNECTION_CLOSE;
1547 break;
1548 case h_transfer_encoding_chunked:
1549 parser->flags |= F_CHUNKED;
1550 break;
1551 default:
1552 break;
1553 }
1554
1555 parser->state = s_header_field_start;
1556 CROW_REEXECUTE();
1557 }
1558
1559 case s_header_value_discard_ws_almost_done:
1560 {
1561 CROW_STRICT_CHECK(ch != lf);
1562 parser->state = s_header_value_discard_lws;
1563 break;
1564 }
1565
1566 case s_header_value_discard_lws:
1567 {
1568 if (ch == ' ' || ch == '\t') {
1569 parser->state = s_header_value_discard_ws;
1570 break;
1571 } else {
1572 /* header value was empty */
1573 CROW_MARK(header_value);
1574 parser->state = s_header_field_start;
1575 CROW_CALLBACK_DATA_NOADVANCE(header_value);
1576 CROW_REEXECUTE();
1577 }
1578 }
1579
1580 case s_headers_almost_done:
1581 {
1582 CROW_STRICT_CHECK(ch != lf);
1583
1584 if (parser->flags & F_TRAILING) {
1585 /* End of a chunked request */
1586 CROW_CALLBACK_NOTIFY(message_complete);
1587 break;
1588 }
1589
1590 /* Cannot use transfer-encoding and a content-length header together
1591 per the HTTP specification. (RFC 7230 Section 3.3.3) */
1592 if ((parser->uses_transfer_encoding == 1) &&
1593 (parser->flags & F_CONTENTLENGTH)) {
1594 /* Allow it for lenient parsing as long as `Transfer-Encoding` is
1595 * not `chunked` or allow_length_with_encoding is set
1596 */
1597 if (parser->flags & F_CHUNKED) {
1598 if (!allow_chunked_length) {
1599 CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH);
1600 goto error;
1601 }
1602 } else if (!lenient) {
1603 CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH);
1604 goto error;
1605 }
1606 }
1607
1608 parser->state = s_headers_done;
1609
1610 /* Set this here so that on_headers_complete() callbacks can see it */
1611 parser->upgrade =
1612 (parser->flags & F_UPGRADE || parser->method == (unsigned)HTTPMethod::Connect);
1613
1614 /* Here we call the headers_complete callback. This is somewhat
1615 * different than other callbacks because if the user returns 1, we
1616 * will interpret that as saying that this message has no body. This
1617 * is needed for the annoying case of recieving a response to a HEAD
1618 * request.
1619 *
1620 * We'd like to use CROW_CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
1621 * we have to simulate it by handling a change in errno below.
1622 */
1623 if (settings->on_headers_complete) {
1624 switch (settings->on_headers_complete(parser)) {
1625 case 0:
1626 break;
1627
1628 case 2:
1629 parser->upgrade = 1;
1630 //break;
1631
1632 /* fall through */
1633 case 1:
1634 parser->flags |= F_SKIPBODY;
1635 break;
1636
1637 default:
1638 CROW_SET_ERRNO(CHPE_CB_headers_complete);
1639 parser->nread = nread;
1640 return p - data; /* Error */
1641 }
1642 }
1643
1644 if (CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK) {
1645 parser->nread = nread;
1646 return p - data;
1647 }
1648
1649 CROW_REEXECUTE();
1650 }
1651
1652 case s_headers_done:
1653 {
1654 CROW_STRICT_CHECK(ch != lf);
1655
1656 parser->nread = 0;
1657 nread = 0;
1658
1659 /* Exit, the rest of the connect is in a different protocol. */
1660 if (parser->upgrade) {
1661 CROW_CALLBACK_NOTIFY(message_complete);
1662 parser->nread = nread;
1663 return (p - data) + 1;
1664 }
1665
1666 if (parser->flags & F_SKIPBODY) {
1667 CROW_CALLBACK_NOTIFY(message_complete);
1668 } else if (parser->flags & F_CHUNKED) {
1669 /* chunked encoding - ignore Content-Length header,
1670 * prepare for a chunk */
1671 parser->state = s_chunk_size_start;
1672 }
1673 else if (parser->uses_transfer_encoding == 1)
1674 {
1675 if (!lenient)
1676 {
1677 /* RFC 7230 3.3.3 */
1678
1679 /* If a Transfer-Encoding header field
1680 * is present in a request and the chunked transfer coding is not
1681 * the final encoding, the message body length cannot be determined
1682 * reliably; the server MUST respond with the 400 (Bad Request)
1683 * status code and then close the connection.
1684 */
1685 CROW_SET_ERRNO(CHPE_INVALID_TRANSFER_ENCODING);
1686 parser->nread = nread;
1687 return (p - data); /* Error */
1688 }
1689 else
1690 {
1691 /* RFC 7230 3.3.3 */
1692
1693 /* If a Transfer-Encoding header field is present in a response and
1694 * the chunked transfer coding is not the final encoding, the
1695 * message body length is determined by reading the connection until
1696 * it is closed by the server.
1697 */
1698 parser->state = s_body_identity_eof;
1699 }
1700 }
1701 else
1702 {
1703 if (parser->content_length == 0)
1704 {
1705 /* Content-Length header given but zero: Content-Length: 0\r\n */
1706 CROW_CALLBACK_NOTIFY(message_complete);
1707 }
1708 else if (parser->content_length != CROW_ULLONG_MAX)
1709 {
1710 /* Content-Length header given and non-zero */
1711 parser->state = s_body_identity;
1712 }
1713 else
1714 {
1715 /* Assume content-length 0 - read the next */
1716 CROW_CALLBACK_NOTIFY(message_complete);
1717 }
1718 }
1719
1720 break;
1721 }
1722
1723 case s_body_identity:
1724 {
1725 uint64_t to_read = CROW_MIN(parser->content_length,
1726 (uint64_t) ((data + len) - p));
1727
1728 assert(parser->content_length != 0
1729 && parser->content_length != CROW_ULLONG_MAX);
1730
1731 /* The difference between advancing content_length and p is because
1732 * the latter will automaticaly advance on the next loop iteration.
1733 * Further, if content_length ends up at 0, we want to see the last
1734 * byte again for our message complete callback.
1735 */
1736 CROW_MARK(body);
1737 parser->content_length -= to_read;
1738 p += to_read - 1;
1739
1740 if (parser->content_length == 0) {
1741 parser->state = s_message_done;
1742
1743 /* Mimic CROW_CALLBACK_DATA_NOADVANCE() but with one extra byte.
1744 *
1745 * The alternative to doing this is to wait for the next byte to
1746 * trigger the data callback, just as in every other case. The
1747 * problem with this is that this makes it difficult for the test
1748 * harness to distinguish between complete-on-EOF and
1749 * complete-on-length. It's not clear that this distinction is
1750 * important for applications, but let's keep it for now.
1751 */
1752 CROW_CALLBACK_DATA_(body, p - body_mark + 1, p - data);
1753 CROW_REEXECUTE();
1754 }
1755
1756 break;
1757 }
1758
1759 /* read until EOF */
1760 case s_body_identity_eof:
1761 CROW_MARK(body);
1762 p = data + len - 1;
1763
1764 break;
1765
1766 case s_message_done:
1767 CROW_CALLBACK_NOTIFY(message_complete);
1768 break;
1769
1770 case s_chunk_size_start:
1771 {
1772 assert(nread == 1);
1773 assert(parser->flags & F_CHUNKED);
1774
1775 unhex_val = unhex[static_cast<unsigned char>(ch)];
1776 if (CROW_UNLIKELY(unhex_val == -1)) {
1777 CROW_SET_ERRNO(CHPE_INVALID_CHUNK_SIZE);
1778 goto error;
1779 }
1780
1781 parser->content_length = unhex_val;
1782 parser->state = s_chunk_size;
1783 break;
1784 }
1785
1786 case s_chunk_size:
1787 {
1788 uint64_t t;
1789
1790 assert(parser->flags & F_CHUNKED);
1791
1792 if (ch == cr) {
1793 parser->state = s_chunk_size_almost_done;
1794 break;
1795 }
1796
1797 unhex_val = unhex[static_cast<unsigned char>(ch)];
1798
1799 if (unhex_val == -1) {
1800 if (ch == ';' || ch == ' ') {
1801 parser->state = s_chunk_parameters;
1802 break;
1803 }
1804
1805 CROW_SET_ERRNO(CHPE_INVALID_CHUNK_SIZE);
1806 goto error;
1807 }
1808
1809 t = parser->content_length;
1810 t *= 16;
1811 t += unhex_val;
1812
1813 /* Overflow? Test against a conservative limit for simplicity. */
1814 if (CROW_UNLIKELY((CROW_ULLONG_MAX - 16) / 16 < parser->content_length)) {
1815 CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1816 goto error;
1817 }
1818
1819 parser->content_length = t;
1820 break;
1821 }
1822
1823 case s_chunk_parameters:
1824 {
1825 assert(parser->flags & F_CHUNKED);
1826 /* just ignore this shit. TODO check for overflow */
1827 if (ch == cr) {
1828 parser->state = s_chunk_size_almost_done;
1829 break;
1830 }
1831 break;
1832 }
1833
1834 case s_chunk_size_almost_done:
1835 {
1836 assert(parser->flags & F_CHUNKED);
1837 CROW_STRICT_CHECK(ch != lf);
1838
1839 parser->nread = 0;
1840 nread = 0;
1841
1842 if (parser->content_length == 0) {
1843 parser->flags |= F_TRAILING;
1844 parser->state = s_header_field_start;
1845 } else {
1846 parser->state = s_chunk_data;
1847 }
1848 break;
1849 }
1850
1851 case s_chunk_data:
1852 {
1853 uint64_t to_read = CROW_MIN(parser->content_length,
1854 (uint64_t) ((data + len) - p));
1855
1856 assert(parser->flags & F_CHUNKED);
1857 assert(parser->content_length != 0
1858 && parser->content_length != CROW_ULLONG_MAX);
1859
1860 /* See the explanation in s_body_identity for why the content
1861 * length and data pointers are managed this way.
1862 */
1863 CROW_MARK(body);
1864 parser->content_length -= to_read;
1865 p += to_read - 1;
1866
1867 if (parser->content_length == 0) {
1868 parser->state = s_chunk_data_almost_done;
1869 }
1870
1871 break;
1872 }
1873
1874 case s_chunk_data_almost_done:
1875 assert(parser->flags & F_CHUNKED);
1876 assert(parser->content_length == 0);
1877 CROW_STRICT_CHECK(ch != cr);
1878 parser->state = s_chunk_data_done;
1879 CROW_CALLBACK_DATA(body);
1880 break;
1881
1882 case s_chunk_data_done:
1883 assert(parser->flags & F_CHUNKED);
1884 CROW_STRICT_CHECK(ch != lf);
1885 parser->nread = 0;
1886 nread = 0;
1887 parser->state = s_chunk_size_start;
1888 break;
1889
1890 default:
1891 assert(0 && "unhandled state");
1892 CROW_SET_ERRNO(CHPE_INVALID_INTERNAL_STATE);
1893 goto error;
1894 }
1895 }
1896
1897 /* Run callbacks for any marks that we have leftover after we ran out of
1898 * bytes. There should be at most one of these set, so it's OK to invoke
1899 * them in series (unset marks will not result in callbacks).
1900 *
1901 * We use the NOADVANCE() variety of callbacks here because 'p' has already
1902 * overflowed 'data' and this allows us to correct for the off-by-one that
1903 * we'd otherwise have (since CROW_CALLBACK_DATA() is meant to be run with a 'p'
1904 * value that's in-bounds).
1905 */
1906
1907 assert(((header_field_mark ? 1 : 0) +
1908 (header_value_mark ? 1 : 0) +
1909 (url_mark ? 1 : 0) +
1910 (body_mark ? 1 : 0)) <= 1);
1911
1912 CROW_CALLBACK_DATA_NOADVANCE(header_field);
1913 CROW_CALLBACK_DATA_NOADVANCE(header_value);
1914 CROW_CALLBACK_DATA_NOADVANCE(url);
1915 CROW_CALLBACK_DATA_NOADVANCE(body);
1916
1917 parser->nread = nread;
1918 return len;
1919
1920error:
1921 if (CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK) {
1922 CROW_SET_ERRNO(CHPE_UNKNOWN);
1923 }
1924
1925 parser->nread = nread;
1926 return (p - data);
1927}
1928
1929inline void
1930 http_parser_init(http_parser* parser)
1931{
1932 void *data = parser->data; /* preserve application data */
1933 memset(parser, 0, sizeof(*parser));
1934 parser->data = data;
1935 parser->state = s_start_req;
1936 parser->http_errno = CHPE_OK;
1937}
1938
1939/* Return a string name of the given error */
1940inline const char *
1941http_errno_name(enum http_errno err) {
1942/* Map errno values to strings for human-readable output */
1943#define CROW_HTTP_STRERROR_GEN(n, s) { "CHPE_" #n, s },
1944static struct {
1945 const char *name;
1946 const char *description;
1947} http_strerror_tab[] = {
1948 CROW_HTTP_ERRNO_MAP(CROW_HTTP_STRERROR_GEN)
1949};
1950#undef CROW_HTTP_STRERROR_GEN
1951 assert(((size_t) err) < CROW_ARRAY_SIZE(http_strerror_tab));
1952 return http_strerror_tab[err].name;
1953}
1954
1955/* Return a string description of the given error */
1956inline const char *
1957http_errno_description(enum http_errno err) {
1958/* Map errno values to strings for human-readable output */
1959#define CROW_HTTP_STRERROR_GEN(n, s) { "CHPE_" #n, s },
1960static struct {
1961 const char *name;
1962 const char *description;
1963} http_strerror_tab[] = {
1964 CROW_HTTP_ERRNO_MAP(CROW_HTTP_STRERROR_GEN)
1965};
1966#undef CROW_HTTP_STRERROR_GEN
1967 assert(((size_t) err) < CROW_ARRAY_SIZE(http_strerror_tab));
1968 return http_strerror_tab[err].description;
1969}
1970
1971/* Checks if this is the final chunk of the body. */
1972inline int
1973http_body_is_final(const struct http_parser *parser) {
1974 return parser->state == s_message_done;
1975}
1976
1977/* Change the maximum header size provided at compile time. */
1978inline void
1979http_parser_set_max_header_size(uint32_t size) {
1980 max_header_size = size;
1981}
1982
1983#undef CROW_HTTP_ERRNO_MAP
1984#undef CROW_SET_ERRNO
1985#undef CROW_CALLBACK_NOTIFY_
1986#undef CROW_CALLBACK_NOTIFY
1987#undef CROW_CALLBACK_NOTIFY_NOADVANCE
1988#undef CROW_CALLBACK_DATA_
1989#undef CROW_CALLBACK_DATA
1990#undef CROW_CALLBACK_DATA_NOADVANCE
1991#undef CROW_MARK
1992#undef CROW_PROXY_CONNECTION
1993#undef CROW_CONNECTION
1994#undef CROW_CONTENT_LENGTH
1995#undef CROW_TRANSFER_ENCODING
1996#undef CROW_UPGRADE
1997#undef CROW_CHUNKED
1998#undef CROW_KEEP_ALIVE
1999#undef CROW_CLOSE
2000#undef CROW_PARSING_HEADER
2001#undef CROW_LOWER
2002#undef CROW_IS_ALPHA
2003#undef CROW_IS_NUM
2004#undef CROW_IS_ALPHANUM
2005//#undef CROW_IS_HEX
2006#undef CROW_IS_MARK
2007#undef CROW_IS_USERINFO_CHAR
2008#undef CROW_TOKEN
2009#undef CROW_IS_URL_CHAR
2010//#undef CROW_IS_HOST_CHAR
2011#undef CROW_STRICT_CHECK
2012
2013}
2014
2015// clang-format on
The main namespace of the library. In this namespace is defined the most important classes and functi...
Definition http_parser_merged.h:181
Definition http_parser_merged.h:148
unsigned char http_major
Definition http_parser_merged.h:163
unsigned int flags
Definition http_parser_merged.h:150
void * data
Definition http_parser_merged.h:176