/src/crow/include/crow/http_parser_merged.h
Line | Count | Source |
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 |
16 | | extern "C" { |
17 | | #include <stddef.h> |
18 | | #if defined(_WIN32) && !defined(__MINGW32__) && \ |
19 | | (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) |
20 | | #include <BaseTsd.h> |
21 | | typedef __int8 int8_t; |
22 | | typedef unsigned __int8 uint8_t; |
23 | | typedef __int16 int16_t; |
24 | | typedef unsigned __int16 uint16_t; |
25 | | typedef __int32 int32_t; |
26 | | typedef unsigned __int32 uint32_t; |
27 | | typedef __int64 int64_t; |
28 | | typedef 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" |
41 | | namespace 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 | | |
54 | | typedef struct http_parser http_parser; |
55 | | typedef 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 | | */ |
75 | | typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); |
76 | | typedef int (*http_cb) (http_parser*); |
77 | | |
78 | | |
79 | | /* Flag values for http_parser.flags field */ |
80 | | enum 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 | 0 | CROW_XX(OK, "success") \ |
98 | 0 | \ |
99 | 0 | /* Callback-related errors */ \ |
100 | 0 | CROW_XX(CB_message_begin, "the on_message_begin callback failed") \ |
101 | 0 | CROW_XX(CB_method, "the on_method callback failed") \ |
102 | 0 | CROW_XX(CB_url, "the \"on_url\" callback failed") \ |
103 | 0 | CROW_XX(CB_header_field, "the \"on_header_field\" callback failed") \ |
104 | 0 | CROW_XX(CB_header_value, "the \"on_header_value\" callback failed") \ |
105 | 0 | CROW_XX(CB_headers_complete, "the \"on_headers_complete\" callback failed") \ |
106 | 0 | CROW_XX(CB_body, "the \"on_body\" callback failed") \ |
107 | 0 | CROW_XX(CB_message_complete, "the \"on_message_complete\" callback failed") \ |
108 | 0 | CROW_XX(CB_status, "the \"on_status\" callback failed") \ |
109 | 0 | \ |
110 | 0 | /* Parsing-related errors */ \ |
111 | 0 | CROW_XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ |
112 | 0 | CROW_XX(HEADER_OVERFLOW, "too many header bytes seen; overflow detected") \ |
113 | 0 | CROW_XX(CLOSED_CONNECTION, "data received after completed connection: close message") \ |
114 | 0 | CROW_XX(INVALID_VERSION, "invalid HTTP version") \ |
115 | 0 | CROW_XX(INVALID_STATUS, "invalid HTTP status code") \ |
116 | 0 | CROW_XX(INVALID_METHOD, "invalid HTTP method") \ |
117 | 0 | CROW_XX(INVALID_URL, "invalid URL") \ |
118 | 0 | CROW_XX(INVALID_HOST, "invalid host") \ |
119 | 0 | CROW_XX(INVALID_PORT, "invalid port") \ |
120 | 0 | CROW_XX(INVALID_PATH, "invalid path") \ |
121 | 0 | CROW_XX(INVALID_QUERY_STRING, "invalid query string") \ |
122 | 0 | CROW_XX(INVALID_FRAGMENT, "invalid fragment") \ |
123 | 0 | CROW_XX(LF_EXPECTED, "LF character expected") \ |
124 | 0 | CROW_XX(INVALID_HEADER_TOKEN, "invalid character in header") \ |
125 | 0 | CROW_XX(INVALID_CONTENT_LENGTH, "invalid character in content-length header") \ |
126 | 0 | CROW_XX(UNEXPECTED_CONTENT_LENGTH, "unexpected content-length header") \ |
127 | 0 | CROW_XX(INVALID_CHUNK_SIZE, "invalid character in chunk size header") \ |
128 | 0 | CROW_XX(INVALID_CONSTANT, "invalid constant string") \ |
129 | 0 | CROW_XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state") \ |
130 | 0 | CROW_XX(STRICT, "strict mode assertion failed") \ |
131 | 0 | CROW_XX(UNKNOWN, "an unknown error occurred") \ |
132 | 0 | 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, |
137 | | enum 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 | 0 | #define CROW_HTTP_PARSER_ERRNO(p) ((enum http_errno)(p)->http_errno) |
145 | | |
146 | | |
147 | | struct http_parser |
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 | | |
180 | | struct http_parser_settings |
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 |
195 | | static uint32_t max_header_size = CROW_HTTP_MAX_HEADER_SIZE; |
196 | | |
197 | | #ifndef CROW_ULLONG_MAX |
198 | 0 | # define CROW_ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ |
199 | | #endif |
200 | | |
201 | | #ifndef CROW_MIN |
202 | 0 | # 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 | 0 | (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ |
212 | 0 | (1 << ((unsigned int) (i) & 7)))) |
213 | | #endif |
214 | | |
215 | 0 | #define CROW_SET_ERRNO(e) \ |
216 | 0 | do { \ |
217 | 0 | parser->nread = nread; \ |
218 | 0 | parser->http_errno = (e); \ |
219 | 0 | } while(0) |
220 | | |
221 | | /* Run the notify callback FOR, returning ER if it fails */ |
222 | 0 | #define CROW_CALLBACK_NOTIFY_(FOR, ER) \ |
223 | 0 | do { \ |
224 | 0 | assert(CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK); \ |
225 | 0 | \ |
226 | 0 | if (CROW_LIKELY(settings->on_##FOR)) { \ |
227 | 0 | if (CROW_UNLIKELY(0 != settings->on_##FOR(parser))) { \ |
228 | 0 | CROW_SET_ERRNO(CHPE_CB_##FOR); \ |
229 | 0 | } \ |
230 | 0 | \ |
231 | 0 | /* We either errored above or got paused; get out */ \ |
232 | 0 | if (CROW_UNLIKELY(CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK)) { \ |
233 | 0 | return (ER); \ |
234 | 0 | } \ |
235 | 0 | } \ |
236 | 0 | } while (0) |
237 | | |
238 | | /* Run the notify callback FOR and consume the current byte */ |
239 | 0 | #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 | 0 | #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 | 0 | #define CROW_CALLBACK_DATA_(FOR, LEN, ER) \ |
246 | 0 | do { \ |
247 | 0 | assert(CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK); \ |
248 | 0 | \ |
249 | 0 | if (FOR##_mark) { \ |
250 | 0 | if (CROW_LIKELY(settings->on_##FOR)) { \ |
251 | 0 | if (CROW_UNLIKELY(0 != \ |
252 | 0 | settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ |
253 | 0 | CROW_SET_ERRNO(CHPE_CB_##FOR); \ |
254 | 0 | } \ |
255 | 0 | \ |
256 | 0 | /* We either errored above or got paused; get out */ \ |
257 | 0 | if (CROW_UNLIKELY(CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK)) {\ |
258 | 0 | return (ER); \ |
259 | 0 | } \ |
260 | 0 | } \ |
261 | 0 | FOR##_mark = NULL; \ |
262 | 0 | } \ |
263 | 0 | } while (0) |
264 | | |
265 | | /* Run the data callback FOR and consume the current byte */ |
266 | | #define CROW_CALLBACK_DATA(FOR) \ |
267 | 0 | 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 | 0 | CROW_CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) |
272 | | |
273 | | /* Set the mark FOR; non-destructive if mark is already set */ |
274 | 0 | #define CROW_MARK(FOR) \ |
275 | 0 | do { \ |
276 | 0 | if (!FOR##_mark) { \ |
277 | 0 | FOR##_mark = p; \ |
278 | 0 | } \ |
279 | 0 | } 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 | 0 | #define CROW_COUNT_HEADER_SIZE(V) \ |
293 | 0 | do { \ |
294 | 0 | nread += (uint32_t)(V); \ |
295 | 0 | if (CROW_UNLIKELY(nread > max_header_size)) { \ |
296 | 0 | CROW_SET_ERRNO(CHPE_HEADER_OVERFLOW); \ |
297 | 0 | goto error; \ |
298 | 0 | } \ |
299 | 0 | } while (0) |
300 | | #define CROW_REEXECUTE() \ |
301 | 0 | goto reexecute; \ |
302 | | |
303 | 0 | #define CROW_PROXY_CONNECTION "proxy-connection" |
304 | 0 | #define CROW_CONNECTION "connection" |
305 | 0 | #define CROW_CONTENT_LENGTH "content-length" |
306 | 0 | #define CROW_TRANSFER_ENCODING "transfer-encoding" |
307 | 0 | #define CROW_UPGRADE "upgrade" |
308 | 0 | #define CROW_CHUNKED "chunked" |
309 | 0 | #define CROW_KEEP_ALIVE "keep-alive" |
310 | 0 | #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 | 0 | #define CROW_PARSING_HEADER(state) (state <= s_headers_done) |
389 | | |
390 | | |
391 | | enum 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 | | |
422 | | enum 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 | 0 | #define CROW_LOWER(c) (unsigned char)(c | 0x20) |
440 | 0 | #define CROW_IS_ALPHA(c) (CROW_LOWER(c) >= 'a' && CROW_LOWER(c) <= 'z') |
441 | 0 | #define CROW_IS_NUM(c) ((c) >= '0' && (c) <= '9') |
442 | 0 | #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 | 0 | #define CROW_IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ |
445 | 0 | (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ |
446 | 0 | (c) == ')') |
447 | 0 | #define CROW_IS_USERINFO_CHAR(c) (CROW_IS_ALPHANUM(c) || CROW_IS_MARK(c) || (c) == '%' || \ |
448 | 0 | (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ |
449 | 0 | (c) == '$' || (c) == ',') |
450 | | |
451 | 0 | #define CROW_TOKEN(c) (tokens[(unsigned char)c]) |
452 | 0 | #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 | 0 | (ch == cr || ch == lf || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) |
461 | | |
462 | 0 | #define CROW_start_state s_start_req |
463 | | |
464 | 0 | # define CROW_STRICT_CHECK(cond) \ |
465 | 0 | do { \ |
466 | 0 | if (cond) { \ |
467 | 0 | CROW_SET_ERRNO(CHPE_STRICT); \ |
468 | 0 | goto error; \ |
469 | 0 | } \ |
470 | 0 | } while (0) |
471 | 0 | #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 | | */ |
484 | | inline enum state |
485 | | parse_url_char(enum state s, const char ch, http_parser *parser, const char* url_mark, const char* p) |
486 | 0 | { |
487 | 0 | # define CROW_T(v) 0 |
488 | | |
489 | |
|
490 | 0 | static 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 | 0, |
493 | | /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ |
494 | 0 | 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 | 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 | 0, |
499 | | /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ |
500 | 0 | 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, |
501 | | /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ |
502 | 0 | 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 | 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, |
505 | | /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ |
506 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, |
513 | | /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ |
514 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, |
521 | | /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ |
522 | 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; |
523 | |
|
524 | 0 | #undef CROW_T |
525 | |
|
526 | 0 | if (ch == ' ' || ch == '\r' || ch == '\n') { |
527 | 0 | return s_dead; |
528 | 0 | } |
529 | 0 | if (ch == '\t' || ch == '\f') { |
530 | 0 | return s_dead; |
531 | 0 | } |
532 | | |
533 | 0 | switch (s) { |
534 | 0 | 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 | 0 | if (ch == '/' || ch == '*') { |
540 | 0 | return s_req_path; |
541 | 0 | } |
542 | | |
543 | 0 | if (CROW_IS_ALPHA(ch)) { |
544 | 0 | return s_req_schema; |
545 | 0 | } |
546 | | |
547 | 0 | break; |
548 | | |
549 | 0 | case s_req_schema: |
550 | 0 | if (CROW_IS_ALPHA(ch)) { |
551 | 0 | return s; |
552 | 0 | } |
553 | | |
554 | 0 | if (ch == ':') { |
555 | 0 | return s_req_schema_slash; |
556 | 0 | } |
557 | | |
558 | 0 | break; |
559 | | |
560 | 0 | case s_req_schema_slash: |
561 | 0 | if (ch == '/') { |
562 | 0 | return s_req_schema_slash_slash; |
563 | 0 | } |
564 | | |
565 | 0 | break; |
566 | | |
567 | 0 | case s_req_schema_slash_slash: |
568 | 0 | if (ch == '/') { |
569 | 0 | return s_req_server_start; |
570 | 0 | } |
571 | | |
572 | 0 | break; |
573 | | |
574 | 0 | case s_req_server_with_at: |
575 | 0 | if (ch == '@') { |
576 | 0 | return s_dead; |
577 | 0 | } |
578 | | |
579 | | /* fall through */ |
580 | 0 | case s_req_server_start: |
581 | 0 | case s_req_server: |
582 | 0 | if (ch == '/') { |
583 | 0 | return s_req_path; |
584 | 0 | } |
585 | | |
586 | 0 | if (ch == '?') { |
587 | 0 | parser->qs_point = p - url_mark; |
588 | 0 | return s_req_query_string_start; |
589 | 0 | } |
590 | | |
591 | 0 | if (ch == '@') { |
592 | 0 | return s_req_server_with_at; |
593 | 0 | } |
594 | | |
595 | 0 | if (CROW_IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { |
596 | 0 | return s_req_server; |
597 | 0 | } |
598 | | |
599 | 0 | break; |
600 | | |
601 | 0 | case s_req_path: |
602 | 0 | if (CROW_IS_URL_CHAR(ch)) { |
603 | 0 | return s; |
604 | 0 | } |
605 | 0 | else if (ch == '?') |
606 | 0 | { |
607 | 0 | parser->qs_point = p - url_mark; |
608 | 0 | return s_req_query_string_start; |
609 | 0 | } |
610 | | |
611 | 0 | break; |
612 | | |
613 | 0 | case s_req_query_string_start: |
614 | 0 | case s_req_query_string: |
615 | 0 | if (CROW_IS_URL_CHAR(ch)) { |
616 | 0 | return s_req_query_string; |
617 | 0 | } |
618 | 0 | else if (ch == '?') |
619 | 0 | { |
620 | 0 | return s_req_query_string; |
621 | 0 | } |
622 | | |
623 | 0 | break; |
624 | | |
625 | 0 | default: |
626 | 0 | break; |
627 | 0 | } |
628 | | |
629 | | /* We should never fall out of the switch above unless there's an error */ |
630 | 0 | return s_dead; |
631 | 0 | } Unexecuted instantiation: crow::parse_url_char(crow::state, char, crow::http_parser*, char const*, char const*) Unexecuted instantiation: crow::parse_url_char(crow::state, char, crow::http_parser*, char const*, char const*) |
632 | | |
633 | | inline size_t http_parser_execute (http_parser *parser, |
634 | | const http_parser_settings *settings, |
635 | | const char *data, |
636 | | size_t len) |
637 | 0 | { |
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 | | */ |
646 | 0 | static 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, 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, 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, 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, 0, |
655 | | /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ |
656 | 0 | 0, '!', 0, '#', '$', '%', '&', '\'', |
657 | | /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ |
658 | 0 | 0, 0, '*', '+', 0, '-', '.', 0, |
659 | | /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ |
660 | 0 | '0', '1', '2', '3', '4', '5', '6', '7', |
661 | | /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ |
662 | 0 | '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 | 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 | 0 | '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 | 0 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', |
669 | | /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ |
670 | 0 | 'x', 'y', 'z', 0, 0, 0, '^', '_', |
671 | | /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ |
672 | 0 | '`', '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 | 0 | '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 | 0 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', |
677 | | /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ |
678 | 0 | 'x', 'y', 'z', 0, '|', 0, '~', 0 }; |
679 | | |
680 | |
|
681 | 0 | static const int8_t unhex[256] = |
682 | 0 | {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
683 | 0 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
684 | 0 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
685 | 0 | , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 |
686 | 0 | ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
687 | 0 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
688 | 0 | ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
689 | 0 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
690 | 0 | }; |
691 | | |
692 | | |
693 | |
|
694 | 0 | char c, ch; |
695 | 0 | int8_t unhex_val; |
696 | 0 | const char *p = data; |
697 | 0 | const char *header_field_mark = 0; |
698 | 0 | const char *header_value_mark = 0; |
699 | 0 | const char *url_mark = 0; |
700 | 0 | const char *url_start_mark = 0; |
701 | 0 | const char *body_mark = 0; |
702 | 0 | const unsigned int lenient = parser->lenient_http_headers; |
703 | 0 | const unsigned int allow_chunked_length = parser->allow_chunked_length; |
704 | |
|
705 | 0 | uint32_t nread = parser->nread; |
706 | | |
707 | | /* We're in an error state. Don't bother doing anything. */ |
708 | 0 | if (CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK) { |
709 | 0 | return 0; |
710 | 0 | } |
711 | | |
712 | 0 | if (len == 0) { |
713 | 0 | switch (parser->state) { |
714 | 0 | case s_body_identity_eof: |
715 | | /* Use of CROW_CALLBACK_NOTIFY() here would erroneously return 1 byte read if we got paused. */ |
716 | 0 | CROW_CALLBACK_NOTIFY_NOADVANCE(message_complete); |
717 | 0 | return 0; |
718 | | |
719 | 0 | case s_dead: |
720 | 0 | case s_start_req: |
721 | 0 | return 0; |
722 | | |
723 | 0 | default: |
724 | 0 | CROW_SET_ERRNO(CHPE_INVALID_EOF_STATE); |
725 | 0 | return 1; |
726 | 0 | } |
727 | 0 | } |
728 | | |
729 | | |
730 | 0 | if (parser->state == s_header_field) |
731 | 0 | header_field_mark = data; |
732 | 0 | if (parser->state == s_header_value) |
733 | 0 | header_value_mark = data; |
734 | 0 | switch (parser->state) { |
735 | 0 | case s_req_path: |
736 | 0 | case s_req_schema: |
737 | 0 | case s_req_schema_slash: |
738 | 0 | case s_req_schema_slash_slash: |
739 | 0 | case s_req_server_start: |
740 | 0 | case s_req_server: |
741 | 0 | case s_req_server_with_at: |
742 | 0 | case s_req_query_string_start: |
743 | 0 | case s_req_query_string: |
744 | 0 | url_mark = data; |
745 | 0 | break; |
746 | 0 | default: |
747 | 0 | break; |
748 | 0 | } |
749 | | |
750 | 0 | for (p=data; p != data + len; p++) { |
751 | 0 | ch = *p; |
752 | |
|
753 | 0 | if (CROW_PARSING_HEADER(parser->state)) |
754 | 0 | CROW_COUNT_HEADER_SIZE(1); |
755 | | |
756 | 0 | reexecute: |
757 | 0 | switch (parser->state) { |
758 | | |
759 | 0 | 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 | 0 | if (CROW_LIKELY(ch == cr || ch == lf)) |
764 | 0 | break; |
765 | | |
766 | 0 | CROW_SET_ERRNO(CHPE_CLOSED_CONNECTION); |
767 | 0 | goto error; |
768 | | |
769 | 0 | case s_start_req: |
770 | 0 | { |
771 | 0 | if (ch == cr || ch == lf) |
772 | 0 | break; |
773 | 0 | parser->flags = 0; |
774 | 0 | parser->uses_transfer_encoding = 0; |
775 | 0 | parser->content_length = CROW_ULLONG_MAX; |
776 | |
|
777 | 0 | if (CROW_UNLIKELY(!CROW_IS_ALPHA(ch))) { |
778 | 0 | CROW_SET_ERRNO(CHPE_INVALID_METHOD); |
779 | 0 | goto error; |
780 | 0 | } |
781 | | |
782 | 0 | parser->method = 0; |
783 | 0 | parser->index = 1; |
784 | 0 | switch (ch) { |
785 | 0 | case 'A': parser->method = (unsigned)HTTPMethod::Acl; break; |
786 | 0 | case 'B': parser->method = (unsigned)HTTPMethod::Bind; break; |
787 | 0 | case 'C': parser->method = (unsigned)HTTPMethod::Connect; /* or COPY, CHECKOUT */ break; |
788 | 0 | case 'D': parser->method = (unsigned)HTTPMethod::Delete; break; |
789 | 0 | case 'G': parser->method = (unsigned)HTTPMethod::Get; break; |
790 | 0 | case 'H': parser->method = (unsigned)HTTPMethod::Head; break; |
791 | 0 | case 'L': parser->method = (unsigned)HTTPMethod::Lock; /* or LINK */ break; |
792 | 0 | case 'M': parser->method = (unsigned)HTTPMethod::MkCol; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; |
793 | 0 | case 'N': parser->method = (unsigned)HTTPMethod::Notify; break; |
794 | 0 | case 'O': parser->method = (unsigned)HTTPMethod::Options; break; |
795 | 0 | case 'P': parser->method = (unsigned)HTTPMethod::Post; /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ break; |
796 | 0 | case 'R': parser->method = (unsigned)HTTPMethod::Report; /* or REBIND */ break; |
797 | 0 | case 'S': parser->method = (unsigned)HTTPMethod::Subscribe; /* or SEARCH, SOURCE */ break; |
798 | 0 | case 'T': parser->method = (unsigned)HTTPMethod::Trace; break; |
799 | 0 | case 'U': parser->method = (unsigned)HTTPMethod::Unlock; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; |
800 | 0 | default: |
801 | 0 | CROW_SET_ERRNO(CHPE_INVALID_METHOD); |
802 | 0 | goto error; |
803 | 0 | } |
804 | 0 | parser->state = s_req_method; |
805 | |
|
806 | 0 | CROW_CALLBACK_NOTIFY(message_begin); |
807 | | |
808 | 0 | break; |
809 | 0 | } |
810 | | |
811 | 0 | case s_req_method: |
812 | 0 | { |
813 | 0 | const char *matcher; |
814 | 0 | if (CROW_UNLIKELY(ch == '\0')) { |
815 | 0 | CROW_SET_ERRNO(CHPE_INVALID_METHOD); |
816 | 0 | goto error; |
817 | 0 | } |
818 | | |
819 | 0 | matcher = method_strings[parser->method]; |
820 | 0 | if (ch == ' ' && matcher[parser->index] == '\0') { |
821 | 0 | parser->state = s_req_spaces_before_url; |
822 | 0 | } else if (ch == matcher[parser->index]) { |
823 | 0 | ; /* nada */ |
824 | 0 | } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') { |
825 | |
|
826 | 0 | switch (parser->method << 16 | parser->index << 8 | ch) { |
827 | 0 | #define CROW_XX(meth, pos, ch, new_meth) \ |
828 | 0 | case ((unsigned)HTTPMethod::meth << 16 | pos << 8 | ch): \ |
829 | 0 | parser->method = (unsigned)HTTPMethod::new_meth; break; |
830 | | |
831 | 0 | CROW_XX(Post, 1, 'U', Put) |
832 | 0 | CROW_XX(Post, 1, 'A', Patch) |
833 | 0 | CROW_XX(Post, 1, 'R', Propfind) |
834 | 0 | CROW_XX(Put, 2, 'R', Purge) |
835 | 0 | CROW_XX(Connect, 1, 'H', Checkout) |
836 | 0 | CROW_XX(Connect, 2, 'P', Copy) |
837 | 0 | CROW_XX(MkCol, 1, 'O', Move) |
838 | 0 | CROW_XX(MkCol, 1, 'E', Merge) |
839 | 0 | CROW_XX(MkCol, 1, '-', MSearch) |
840 | 0 | CROW_XX(MkCol, 2, 'A', MkActivity) |
841 | 0 | CROW_XX(MkCol, 3, 'A', MkCalendar) |
842 | 0 | CROW_XX(Subscribe, 1, 'E', Search) |
843 | 0 | CROW_XX(Subscribe, 1, 'O', Source) |
844 | 0 | CROW_XX(Report, 2, 'B', Rebind) |
845 | 0 | CROW_XX(Propfind, 4, 'P', Proppatch) |
846 | 0 | CROW_XX(Lock, 1, 'I', Link) |
847 | 0 | CROW_XX(Unlock, 2, 'S', Unsubscribe) |
848 | 0 | CROW_XX(Unlock, 2, 'B', Unbind) |
849 | 0 | CROW_XX(Unlock, 3, 'I', Unlink) |
850 | 0 | #undef CROW_XX |
851 | 0 | default: |
852 | 0 | CROW_SET_ERRNO(CHPE_INVALID_METHOD); |
853 | 0 | goto error; |
854 | 0 | } |
855 | 0 | } else { |
856 | 0 | CROW_SET_ERRNO(CHPE_INVALID_METHOD); |
857 | 0 | goto error; |
858 | 0 | } |
859 | | |
860 | 0 | CROW_CALLBACK_NOTIFY_NOADVANCE(method); |
861 | | |
862 | 0 | ++parser->index; |
863 | 0 | break; |
864 | 0 | } |
865 | | |
866 | 0 | case s_req_spaces_before_url: |
867 | 0 | { |
868 | 0 | if (ch == ' ') break; |
869 | | |
870 | 0 | CROW_MARK(url); |
871 | 0 | CROW_MARK(url_start); |
872 | 0 | if (parser->method == (unsigned)HTTPMethod::Connect) { |
873 | 0 | parser->state = s_req_server_start; |
874 | 0 | } |
875 | |
|
876 | 0 | parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p); |
877 | 0 | if (CROW_UNLIKELY(parser->state == s_dead)) { |
878 | 0 | CROW_SET_ERRNO(CHPE_INVALID_URL); |
879 | 0 | goto error; |
880 | 0 | } |
881 | | |
882 | 0 | break; |
883 | 0 | } |
884 | | |
885 | 0 | case s_req_schema: |
886 | 0 | case s_req_schema_slash: |
887 | 0 | case s_req_schema_slash_slash: |
888 | 0 | case s_req_server_start: |
889 | 0 | { |
890 | 0 | switch (ch) { |
891 | | /* No whitespace allowed here */ |
892 | 0 | case ' ': |
893 | 0 | case cr: |
894 | 0 | case lf: |
895 | 0 | CROW_SET_ERRNO(CHPE_INVALID_URL); |
896 | 0 | goto error; |
897 | 0 | default: |
898 | 0 | parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p); |
899 | 0 | if (CROW_UNLIKELY(parser->state == s_dead)) { |
900 | 0 | CROW_SET_ERRNO(CHPE_INVALID_URL); |
901 | 0 | goto error; |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | 0 | break; |
906 | 0 | } |
907 | | |
908 | 0 | case s_req_server: |
909 | 0 | case s_req_server_with_at: |
910 | 0 | case s_req_path: |
911 | 0 | case s_req_query_string_start: |
912 | 0 | case s_req_query_string: |
913 | 0 | { |
914 | 0 | switch (ch) { |
915 | 0 | case ' ': |
916 | 0 | parser->state = s_req_http_start; |
917 | 0 | CROW_CALLBACK_DATA(url); |
918 | 0 | break; |
919 | 0 | case cr: // No space after URL means no HTTP version. Which means the request is using HTTP/0.9 |
920 | 0 | case lf: |
921 | 0 | if (CROW_UNLIKELY(parser->method != (unsigned)HTTPMethod::Get)) // HTTP/0.9 doesn't define any method other than GET |
922 | 0 | { |
923 | 0 | parser->state = s_dead; |
924 | 0 | CROW_SET_ERRNO(CHPE_INVALID_VERSION); |
925 | 0 | goto error; |
926 | 0 | } |
927 | 0 | parser->http_major = 0; |
928 | 0 | parser->http_minor = 9; |
929 | 0 | parser->state = (ch == cr) ? |
930 | 0 | s_req_line_almost_done : |
931 | 0 | s_header_field_start; |
932 | 0 | CROW_CALLBACK_DATA(url); |
933 | 0 | break; |
934 | 0 | default: |
935 | 0 | parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p); |
936 | 0 | if (CROW_UNLIKELY(parser->state == s_dead)) { |
937 | 0 | CROW_SET_ERRNO(CHPE_INVALID_URL); |
938 | 0 | goto error; |
939 | 0 | } |
940 | 0 | } |
941 | 0 | break; |
942 | 0 | } |
943 | | |
944 | 0 | case s_req_http_start: |
945 | 0 | switch (ch) { |
946 | 0 | case ' ': |
947 | 0 | break; |
948 | 0 | case 'H': |
949 | 0 | parser->state = s_req_http_H; |
950 | 0 | break; |
951 | 0 | case 'I': |
952 | 0 | if (parser->method == (unsigned)HTTPMethod::Source) { |
953 | 0 | parser->state = s_req_http_I; |
954 | 0 | break; |
955 | 0 | } |
956 | | /* fall through */ |
957 | 0 | default: |
958 | 0 | CROW_SET_ERRNO(CHPE_INVALID_CONSTANT); |
959 | 0 | goto error; |
960 | 0 | } |
961 | 0 | break; |
962 | | |
963 | 0 | case s_req_http_H: |
964 | 0 | CROW_STRICT_CHECK(ch != 'T'); |
965 | 0 | parser->state = s_req_http_HT; |
966 | 0 | break; |
967 | | |
968 | 0 | case s_req_http_HT: |
969 | 0 | CROW_STRICT_CHECK(ch != 'T'); |
970 | 0 | parser->state = s_req_http_HTT; |
971 | 0 | break; |
972 | | |
973 | 0 | case s_req_http_HTT: |
974 | 0 | CROW_STRICT_CHECK(ch != 'P'); |
975 | 0 | parser->state = s_req_http_HTTP; |
976 | 0 | break; |
977 | | |
978 | 0 | case s_req_http_I: |
979 | 0 | CROW_STRICT_CHECK(ch != 'C'); |
980 | 0 | parser->state = s_req_http_IC; |
981 | 0 | break; |
982 | | |
983 | 0 | case s_req_http_IC: |
984 | 0 | CROW_STRICT_CHECK(ch != 'E'); |
985 | 0 | parser->state = s_req_http_HTTP; /* Treat "ICE" as "HTTP". */ |
986 | 0 | break; |
987 | | |
988 | 0 | case s_req_http_HTTP: |
989 | 0 | CROW_STRICT_CHECK(ch != '/'); |
990 | 0 | parser->state = s_req_http_major; |
991 | 0 | break; |
992 | | |
993 | | /* dot */ |
994 | 0 | case s_req_http_major: |
995 | 0 | if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) { |
996 | 0 | CROW_SET_ERRNO(CHPE_INVALID_VERSION); |
997 | 0 | goto error; |
998 | 0 | } |
999 | | |
1000 | 0 | parser->http_major = ch - '0'; |
1001 | 0 | parser->state = s_req_http_dot; |
1002 | 0 | break; |
1003 | | |
1004 | 0 | case s_req_http_dot: |
1005 | 0 | { |
1006 | 0 | if (CROW_UNLIKELY(ch != '.')) { |
1007 | 0 | CROW_SET_ERRNO(CHPE_INVALID_VERSION); |
1008 | 0 | goto error; |
1009 | 0 | } |
1010 | | |
1011 | 0 | parser->state = s_req_http_minor; |
1012 | 0 | break; |
1013 | 0 | } |
1014 | | |
1015 | | /* minor HTTP version */ |
1016 | 0 | case s_req_http_minor: |
1017 | 0 | if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) { |
1018 | 0 | CROW_SET_ERRNO(CHPE_INVALID_VERSION); |
1019 | 0 | goto error; |
1020 | 0 | } |
1021 | | |
1022 | 0 | parser->http_minor = ch - '0'; |
1023 | 0 | parser->state = s_req_http_end; |
1024 | 0 | break; |
1025 | | |
1026 | | /* end of request line */ |
1027 | 0 | case s_req_http_end: |
1028 | 0 | { |
1029 | 0 | if (ch == cr) { |
1030 | 0 | parser->state = s_req_line_almost_done; |
1031 | 0 | break; |
1032 | 0 | } |
1033 | | |
1034 | 0 | if (ch == lf) { |
1035 | 0 | parser->state = s_header_field_start; |
1036 | 0 | break; |
1037 | 0 | } |
1038 | | |
1039 | 0 | CROW_SET_ERRNO(CHPE_INVALID_VERSION); |
1040 | 0 | goto error; |
1041 | 0 | break; |
1042 | 0 | } |
1043 | | |
1044 | | /* end of request line */ |
1045 | 0 | case s_req_line_almost_done: |
1046 | 0 | { |
1047 | 0 | if (CROW_UNLIKELY(ch != lf)) { |
1048 | 0 | CROW_SET_ERRNO(CHPE_LF_EXPECTED); |
1049 | 0 | goto error; |
1050 | 0 | } |
1051 | | |
1052 | 0 | parser->state = s_header_field_start; |
1053 | 0 | break; |
1054 | 0 | } |
1055 | | |
1056 | 0 | case s_header_field_start: |
1057 | 0 | { |
1058 | 0 | if (ch == cr) { |
1059 | 0 | parser->state = s_headers_almost_done; |
1060 | 0 | break; |
1061 | 0 | } |
1062 | | |
1063 | 0 | 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 | 0 | parser->state = s_headers_almost_done; |
1067 | 0 | CROW_REEXECUTE(); |
1068 | 0 | } |
1069 | | |
1070 | 0 | c = CROW_TOKEN(ch); |
1071 | |
|
1072 | 0 | if (CROW_UNLIKELY(!c)) { |
1073 | 0 | CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN); |
1074 | 0 | goto error; |
1075 | 0 | } |
1076 | | |
1077 | 0 | CROW_MARK(header_field); |
1078 | |
|
1079 | 0 | parser->index = 0; |
1080 | 0 | parser->state = s_header_field; |
1081 | |
|
1082 | 0 | switch (c) { |
1083 | 0 | case 'c': |
1084 | 0 | parser->header_state = h_C; |
1085 | 0 | break; |
1086 | | |
1087 | 0 | case 'p': |
1088 | 0 | parser->header_state = h_matching_proxy_connection; |
1089 | 0 | break; |
1090 | | |
1091 | 0 | case 't': |
1092 | 0 | parser->header_state = h_matching_transfer_encoding; |
1093 | 0 | break; |
1094 | | |
1095 | 0 | case 'u': |
1096 | 0 | parser->header_state = h_matching_upgrade; |
1097 | 0 | break; |
1098 | | |
1099 | 0 | default: |
1100 | 0 | parser->header_state = h_general; |
1101 | 0 | break; |
1102 | 0 | } |
1103 | 0 | break; |
1104 | 0 | } |
1105 | | |
1106 | 0 | case s_header_field: |
1107 | 0 | { |
1108 | 0 | const char* start = p; |
1109 | 0 | for (; p != data + len; p++) { |
1110 | 0 | ch = *p; |
1111 | 0 | c = CROW_TOKEN(ch); |
1112 | |
|
1113 | 0 | if (!c) |
1114 | 0 | break; |
1115 | | |
1116 | 0 | switch (parser->header_state) { |
1117 | 0 | case h_general: { |
1118 | 0 | size_t left = data + len - p; |
1119 | 0 | const char* pe = p + CROW_MIN(left, max_header_size); |
1120 | 0 | while (p+1 < pe && CROW_TOKEN(p[1])) { |
1121 | 0 | p++; |
1122 | 0 | } |
1123 | 0 | break; |
1124 | 0 | } |
1125 | | |
1126 | 0 | case h_C: |
1127 | 0 | parser->index++; |
1128 | 0 | parser->header_state = (c == 'o' ? h_CO : h_general); |
1129 | 0 | break; |
1130 | | |
1131 | 0 | case h_CO: |
1132 | 0 | parser->index++; |
1133 | 0 | parser->header_state = (c == 'n' ? h_CON : h_general); |
1134 | 0 | break; |
1135 | | |
1136 | 0 | case h_CON: |
1137 | 0 | parser->index++; |
1138 | 0 | switch (c) { |
1139 | 0 | case 'n': |
1140 | 0 | parser->header_state = h_matching_connection; |
1141 | 0 | break; |
1142 | 0 | case 't': |
1143 | 0 | parser->header_state = h_matching_content_length; |
1144 | 0 | break; |
1145 | 0 | default: |
1146 | 0 | parser->header_state = h_general; |
1147 | 0 | break; |
1148 | 0 | } |
1149 | 0 | break; |
1150 | | |
1151 | | /* connection */ |
1152 | | |
1153 | 0 | case h_matching_connection: |
1154 | 0 | parser->index++; |
1155 | 0 | if (parser->index > sizeof(CROW_CONNECTION)-1 || c != CROW_CONNECTION[parser->index]) { |
1156 | 0 | parser->header_state = h_general; |
1157 | 0 | } else if (parser->index == sizeof(CROW_CONNECTION)-2) { |
1158 | 0 | parser->header_state = h_connection; |
1159 | 0 | } |
1160 | 0 | break; |
1161 | | |
1162 | | /* proxy-connection */ |
1163 | | |
1164 | 0 | case h_matching_proxy_connection: |
1165 | 0 | parser->index++; |
1166 | 0 | if (parser->index > sizeof(CROW_PROXY_CONNECTION)-1 || c != CROW_PROXY_CONNECTION[parser->index]) { |
1167 | 0 | parser->header_state = h_general; |
1168 | 0 | } else if (parser->index == sizeof(CROW_PROXY_CONNECTION)-2) { |
1169 | 0 | parser->header_state = h_connection; |
1170 | 0 | } |
1171 | 0 | break; |
1172 | | |
1173 | | /* content-length */ |
1174 | | |
1175 | 0 | case h_matching_content_length: |
1176 | 0 | parser->index++; |
1177 | 0 | if (parser->index > sizeof(CROW_CONTENT_LENGTH)-1 || c != CROW_CONTENT_LENGTH[parser->index]) { |
1178 | 0 | parser->header_state = h_general; |
1179 | 0 | } else if (parser->index == sizeof(CROW_CONTENT_LENGTH)-2) { |
1180 | 0 | parser->header_state = h_content_length; |
1181 | 0 | } |
1182 | 0 | break; |
1183 | | |
1184 | | /* transfer-encoding */ |
1185 | | |
1186 | 0 | case h_matching_transfer_encoding: |
1187 | 0 | parser->index++; |
1188 | 0 | if (parser->index > sizeof(CROW_TRANSFER_ENCODING)-1 || c != CROW_TRANSFER_ENCODING[parser->index]) { |
1189 | 0 | parser->header_state = h_general; |
1190 | 0 | } else if (parser->index == sizeof(CROW_TRANSFER_ENCODING)-2) { |
1191 | 0 | parser->header_state = h_transfer_encoding; |
1192 | 0 | parser->uses_transfer_encoding = 1; |
1193 | 0 | } |
1194 | 0 | break; |
1195 | | |
1196 | | /* upgrade */ |
1197 | | |
1198 | 0 | case h_matching_upgrade: |
1199 | 0 | parser->index++; |
1200 | 0 | if (parser->index > sizeof(CROW_UPGRADE)-1 || c != CROW_UPGRADE[parser->index]) { |
1201 | 0 | parser->header_state = h_general; |
1202 | 0 | } else if (parser->index == sizeof(CROW_UPGRADE)-2) { |
1203 | 0 | parser->header_state = h_upgrade; |
1204 | 0 | } |
1205 | 0 | break; |
1206 | | |
1207 | 0 | case h_connection: |
1208 | 0 | case h_content_length: |
1209 | 0 | case h_transfer_encoding: |
1210 | 0 | case h_upgrade: |
1211 | 0 | if (ch != ' ') parser->header_state = h_general; |
1212 | 0 | break; |
1213 | | |
1214 | 0 | default: |
1215 | 0 | assert(0 && "Unknown header_state"); |
1216 | 0 | break; |
1217 | 0 | } |
1218 | 0 | } |
1219 | | |
1220 | 0 | if (p == data + len) { |
1221 | 0 | --p; |
1222 | 0 | CROW_COUNT_HEADER_SIZE(p - start); |
1223 | 0 | break; |
1224 | 0 | } |
1225 | | |
1226 | 0 | CROW_COUNT_HEADER_SIZE(p - start); |
1227 | | |
1228 | 0 | if (ch == ':') { |
1229 | 0 | parser->state = s_header_value_discard_ws; |
1230 | 0 | CROW_CALLBACK_DATA(header_field); |
1231 | 0 | break; |
1232 | 0 | } |
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 | 0 | CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN); |
1247 | 0 | goto error; |
1248 | 0 | } |
1249 | | |
1250 | 0 | case s_header_value_discard_ws: |
1251 | 0 | if (ch == ' ' || ch == '\t') break; |
1252 | | |
1253 | 0 | if (ch == cr) { |
1254 | 0 | parser->state = s_header_value_discard_ws_almost_done; |
1255 | 0 | break; |
1256 | 0 | } |
1257 | | |
1258 | 0 | if (ch == lf) { |
1259 | 0 | parser->state = s_header_value_discard_lws; |
1260 | 0 | break; |
1261 | 0 | } |
1262 | | |
1263 | | /* fall through */ |
1264 | | |
1265 | 0 | case s_header_value_start: |
1266 | 0 | { |
1267 | 0 | CROW_MARK(header_value); |
1268 | |
|
1269 | 0 | parser->state = s_header_value; |
1270 | 0 | parser->index = 0; |
1271 | |
|
1272 | 0 | c = CROW_LOWER(ch); |
1273 | |
|
1274 | 0 | switch (parser->header_state) { |
1275 | 0 | 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 | 0 | if (ch != 'h' || p+1 == (data + len) || *(p+1) != '2') { |
1282 | 0 | parser->flags |= F_UPGRADE; |
1283 | 0 | } |
1284 | 0 | parser->header_state = h_general; |
1285 | 0 | break; |
1286 | | |
1287 | 0 | case h_transfer_encoding: |
1288 | | /* looking for 'Transfer-Encoding: chunked' */ |
1289 | 0 | if ('c' == c) { |
1290 | 0 | parser->header_state = h_matching_transfer_encoding_chunked; |
1291 | 0 | } else { |
1292 | 0 | parser->header_state = h_matching_transfer_encoding_token; |
1293 | 0 | } |
1294 | 0 | break; |
1295 | | |
1296 | | /* Multi-value `Transfer-Encoding` header */ |
1297 | 0 | case h_matching_transfer_encoding_token_start: |
1298 | 0 | break; |
1299 | | |
1300 | 0 | case h_content_length: |
1301 | 0 | if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) { |
1302 | 0 | CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH); |
1303 | 0 | goto error; |
1304 | 0 | } |
1305 | | |
1306 | 0 | if (parser->flags & F_CONTENTLENGTH) { |
1307 | 0 | CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH); |
1308 | 0 | goto error; |
1309 | 0 | } |
1310 | 0 | parser->flags |= F_CONTENTLENGTH; |
1311 | 0 | parser->content_length = ch - '0'; |
1312 | 0 | parser->header_state = h_content_length_num; |
1313 | 0 | break; |
1314 | | |
1315 | | /* when obsolete line folding is encountered for content length |
1316 | | * continue to the s_header_value state */ |
1317 | 0 | case h_content_length_ws: |
1318 | 0 | break; |
1319 | | |
1320 | 0 | case h_connection: |
1321 | | /* looking for 'Connection: keep-alive' */ |
1322 | 0 | if (c == 'k') { |
1323 | 0 | parser->header_state = h_matching_connection_keep_alive; |
1324 | | /* looking for 'Connection: close' */ |
1325 | 0 | } else if (c == 'c') { |
1326 | 0 | parser->header_state = h_matching_connection_close; |
1327 | 0 | } else if (c == ' ' || c == '\t') { |
1328 | | /* Skip lws */ |
1329 | 0 | } else { |
1330 | 0 | parser->header_state = h_general; |
1331 | 0 | } |
1332 | 0 | break; |
1333 | | |
1334 | 0 | default: |
1335 | 0 | parser->header_state = h_general; |
1336 | 0 | break; |
1337 | 0 | } |
1338 | 0 | break; |
1339 | 0 | } |
1340 | | |
1341 | 0 | case s_header_value: |
1342 | 0 | { |
1343 | 0 | const char* start = p; |
1344 | 0 | enum header_states h_state = static_cast<header_states>(parser->header_state); |
1345 | 0 | for (; p != data + len; p++) { |
1346 | 0 | ch = *p; |
1347 | |
|
1348 | 0 | if (ch == cr) { |
1349 | 0 | parser->state = s_header_almost_done; |
1350 | 0 | parser->header_state = h_state; |
1351 | 0 | CROW_CALLBACK_DATA(header_value); |
1352 | 0 | break; |
1353 | 0 | } |
1354 | | |
1355 | 0 | if (ch == lf) { |
1356 | 0 | parser->state = s_header_almost_done; |
1357 | 0 | CROW_COUNT_HEADER_SIZE(p - start); |
1358 | 0 | parser->header_state = h_state; |
1359 | 0 | CROW_CALLBACK_DATA_NOADVANCE(header_value); |
1360 | 0 | CROW_REEXECUTE(); |
1361 | 0 | } |
1362 | | |
1363 | 0 | if (!lenient && !CROW_IS_HEADER_CHAR(ch)) { |
1364 | 0 | CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN); |
1365 | 0 | goto error; |
1366 | 0 | } |
1367 | | |
1368 | 0 | c = CROW_LOWER(ch); |
1369 | |
|
1370 | 0 | switch (h_state) { |
1371 | 0 | case h_general: |
1372 | 0 | { |
1373 | 0 | size_t left = data + len - p; |
1374 | 0 | const char* pe = p + CROW_MIN(left, max_header_size); |
1375 | |
|
1376 | 0 | for (; p != pe; p++) { |
1377 | 0 | ch = *p; |
1378 | 0 | if (ch == cr || ch == lf) { |
1379 | 0 | --p; |
1380 | 0 | break; |
1381 | 0 | } |
1382 | 0 | if (!lenient && !CROW_IS_HEADER_CHAR(ch)) { |
1383 | 0 | CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN); |
1384 | 0 | goto error; |
1385 | 0 | } |
1386 | 0 | } |
1387 | 0 | if (p == data + len) |
1388 | 0 | --p; |
1389 | 0 | break; |
1390 | 0 | } |
1391 | | |
1392 | 0 | case h_connection: |
1393 | 0 | case h_transfer_encoding: |
1394 | 0 | assert(0 && "Shouldn't get here."); |
1395 | 0 | break; |
1396 | | |
1397 | 0 | case h_content_length: |
1398 | 0 | if (ch == ' ') break; |
1399 | 0 | h_state = h_content_length_num; |
1400 | | /* fall through */ |
1401 | |
|
1402 | 0 | case h_content_length_num: |
1403 | 0 | { |
1404 | 0 | uint64_t t; |
1405 | |
|
1406 | 0 | if (ch == ' ') { |
1407 | 0 | h_state = h_content_length_ws; |
1408 | 0 | break; |
1409 | 0 | } |
1410 | | |
1411 | 0 | if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) { |
1412 | 0 | CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH); |
1413 | 0 | parser->header_state = h_state; |
1414 | 0 | goto error; |
1415 | 0 | } |
1416 | | |
1417 | 0 | t = parser->content_length; |
1418 | 0 | t *= 10; |
1419 | 0 | t += ch - '0'; |
1420 | | |
1421 | | /* Overflow? Test against a conservative limit for simplicity. */ |
1422 | 0 | if (CROW_UNLIKELY((CROW_ULLONG_MAX - 10) / 10 < parser->content_length)) { |
1423 | 0 | CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH); |
1424 | 0 | parser->header_state = h_state; |
1425 | 0 | goto error; |
1426 | 0 | } |
1427 | | |
1428 | 0 | parser->content_length = t; |
1429 | 0 | break; |
1430 | 0 | } |
1431 | | |
1432 | 0 | case h_content_length_ws: |
1433 | 0 | if (ch == ' ') break; |
1434 | 0 | CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH); |
1435 | 0 | parser->header_state = h_state; |
1436 | 0 | goto error; |
1437 | | |
1438 | | /* Transfer-Encoding: chunked */ |
1439 | 0 | case h_matching_transfer_encoding_token_start: |
1440 | | /* looking for 'Transfer-Encoding: chunked' */ |
1441 | 0 | if ('c' == c) { |
1442 | 0 | h_state = h_matching_transfer_encoding_chunked; |
1443 | 0 | } 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 | 0 | h_state = h_matching_transfer_encoding_token; |
1450 | 0 | } else if (c == ' ' || c == '\t') { |
1451 | | /* Skip lws */ |
1452 | 0 | } else { |
1453 | 0 | h_state = h_general; |
1454 | 0 | } |
1455 | 0 | break; |
1456 | | |
1457 | 0 | case h_matching_transfer_encoding_chunked: |
1458 | 0 | parser->index++; |
1459 | 0 | if (parser->index > sizeof(CROW_CHUNKED)-1 || c != CROW_CHUNKED[parser->index]) { |
1460 | 0 | h_state = h_matching_transfer_encoding_token; |
1461 | 0 | } else if (parser->index == sizeof(CROW_CHUNKED)-2) { |
1462 | 0 | h_state = h_transfer_encoding_chunked; |
1463 | 0 | } |
1464 | 0 | break; |
1465 | | |
1466 | 0 | case h_matching_transfer_encoding_token: |
1467 | 0 | if (ch == ',') { |
1468 | 0 | h_state = h_matching_transfer_encoding_token_start; |
1469 | 0 | parser->index = 0; |
1470 | 0 | } |
1471 | 0 | break; |
1472 | | |
1473 | | /* looking for 'Connection: keep-alive' */ |
1474 | 0 | case h_matching_connection_keep_alive: |
1475 | 0 | parser->index++; |
1476 | 0 | if (parser->index > sizeof(CROW_KEEP_ALIVE)-1 || c != CROW_KEEP_ALIVE[parser->index]) { |
1477 | 0 | h_state = h_general; |
1478 | 0 | } else if (parser->index == sizeof(CROW_KEEP_ALIVE)-2) { |
1479 | 0 | h_state = h_connection_keep_alive; |
1480 | 0 | } |
1481 | 0 | break; |
1482 | | |
1483 | | /* looking for 'Connection: close' */ |
1484 | 0 | case h_matching_connection_close: |
1485 | 0 | parser->index++; |
1486 | 0 | if (parser->index > sizeof(CROW_CLOSE)-1 || c != CROW_CLOSE[parser->index]) { |
1487 | 0 | h_state = h_general; |
1488 | 0 | } else if (parser->index == sizeof(CROW_CLOSE)-2) { |
1489 | 0 | h_state = h_connection_close; |
1490 | 0 | } |
1491 | 0 | break; |
1492 | | |
1493 | | // Edited from original (because of commits that werent included) |
1494 | 0 | case h_transfer_encoding_chunked: |
1495 | 0 | if (ch != ' ') h_state = h_matching_transfer_encoding_token; |
1496 | 0 | break; |
1497 | 0 | case h_connection_keep_alive: |
1498 | 0 | case h_connection_close: |
1499 | 0 | if (ch != ' ') h_state = h_general; |
1500 | 0 | break; |
1501 | | |
1502 | 0 | default: |
1503 | 0 | parser->state = s_header_value; |
1504 | 0 | h_state = h_general; |
1505 | 0 | break; |
1506 | 0 | } |
1507 | 0 | } |
1508 | 0 | parser->header_state = h_state; |
1509 | | |
1510 | |
|
1511 | 0 | if (p == data + len) |
1512 | 0 | --p; |
1513 | |
|
1514 | 0 | CROW_COUNT_HEADER_SIZE(p - start); |
1515 | 0 | break; |
1516 | 0 | } |
1517 | | |
1518 | 0 | case s_header_almost_done: |
1519 | 0 | { |
1520 | 0 | if (CROW_UNLIKELY(ch != lf)) { |
1521 | 0 | CROW_SET_ERRNO(CHPE_LF_EXPECTED); |
1522 | 0 | goto error; |
1523 | 0 | } |
1524 | | |
1525 | 0 | parser->state = s_header_value_lws; |
1526 | 0 | break; |
1527 | 0 | } |
1528 | | |
1529 | 0 | case s_header_value_lws: |
1530 | 0 | { |
1531 | 0 | if (ch == ' ' || ch == '\t') { |
1532 | 0 | if (parser->header_state == h_content_length_num) { |
1533 | | /* treat obsolete line folding as space */ |
1534 | 0 | parser->header_state = h_content_length_ws; |
1535 | 0 | } |
1536 | 0 | parser->state = s_header_value_start; |
1537 | 0 | CROW_REEXECUTE(); |
1538 | 0 | } |
1539 | | |
1540 | | /* finished the header */ |
1541 | 0 | switch (parser->header_state) { |
1542 | 0 | case h_connection_keep_alive: |
1543 | 0 | parser->flags |= F_CONNECTION_KEEP_ALIVE; |
1544 | 0 | break; |
1545 | 0 | case h_connection_close: |
1546 | 0 | parser->flags |= F_CONNECTION_CLOSE; |
1547 | 0 | break; |
1548 | 0 | case h_transfer_encoding_chunked: |
1549 | 0 | parser->flags |= F_CHUNKED; |
1550 | 0 | break; |
1551 | 0 | default: |
1552 | 0 | break; |
1553 | 0 | } |
1554 | | |
1555 | 0 | parser->state = s_header_field_start; |
1556 | 0 | CROW_REEXECUTE(); |
1557 | 0 | } |
1558 | | |
1559 | 0 | case s_header_value_discard_ws_almost_done: |
1560 | 0 | { |
1561 | 0 | CROW_STRICT_CHECK(ch != lf); |
1562 | 0 | parser->state = s_header_value_discard_lws; |
1563 | 0 | break; |
1564 | 0 | } |
1565 | | |
1566 | 0 | case s_header_value_discard_lws: |
1567 | 0 | { |
1568 | 0 | if (ch == ' ' || ch == '\t') { |
1569 | 0 | parser->state = s_header_value_discard_ws; |
1570 | 0 | break; |
1571 | 0 | } else { |
1572 | | /* header value was empty */ |
1573 | 0 | CROW_MARK(header_value); |
1574 | 0 | parser->state = s_header_field_start; |
1575 | 0 | CROW_CALLBACK_DATA_NOADVANCE(header_value); |
1576 | 0 | CROW_REEXECUTE(); |
1577 | 0 | } |
1578 | 0 | } |
1579 | | |
1580 | 0 | case s_headers_almost_done: |
1581 | 0 | { |
1582 | 0 | CROW_STRICT_CHECK(ch != lf); |
1583 | | |
1584 | 0 | if (parser->flags & F_TRAILING) { |
1585 | | /* End of a chunked request */ |
1586 | 0 | CROW_CALLBACK_NOTIFY(message_complete); |
1587 | 0 | break; |
1588 | 0 | } |
1589 | | |
1590 | | /* Cannot use transfer-encoding and a content-length header together |
1591 | | per the HTTP specification. (RFC 7230 Section 3.3.3) */ |
1592 | 0 | if ((parser->uses_transfer_encoding == 1) && |
1593 | 0 | (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 | 0 | if (parser->flags & F_CHUNKED) { |
1598 | 0 | if (!allow_chunked_length) { |
1599 | 0 | CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH); |
1600 | 0 | goto error; |
1601 | 0 | } |
1602 | 0 | } else if (!lenient) { |
1603 | 0 | CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH); |
1604 | 0 | goto error; |
1605 | 0 | } |
1606 | 0 | } |
1607 | | |
1608 | 0 | parser->state = s_headers_done; |
1609 | | |
1610 | | /* Set this here so that on_headers_complete() callbacks can see it */ |
1611 | 0 | parser->upgrade = |
1612 | 0 | (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 | 0 | if (settings->on_headers_complete) { |
1624 | 0 | switch (settings->on_headers_complete(parser)) { |
1625 | 0 | case 0: |
1626 | 0 | break; |
1627 | | |
1628 | 0 | case 2: |
1629 | 0 | parser->upgrade = 1; |
1630 | | //break; |
1631 | | |
1632 | | /* fall through */ |
1633 | 0 | case 1: |
1634 | 0 | parser->flags |= F_SKIPBODY; |
1635 | 0 | break; |
1636 | | |
1637 | 0 | default: |
1638 | 0 | CROW_SET_ERRNO(CHPE_CB_headers_complete); |
1639 | 0 | parser->nread = nread; |
1640 | 0 | return p - data; /* Error */ |
1641 | 0 | } |
1642 | 0 | } |
1643 | | |
1644 | 0 | if (CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK) { |
1645 | 0 | parser->nread = nread; |
1646 | 0 | return p - data; |
1647 | 0 | } |
1648 | | |
1649 | 0 | CROW_REEXECUTE(); |
1650 | 0 | } |
1651 | | |
1652 | 0 | case s_headers_done: |
1653 | 0 | { |
1654 | 0 | CROW_STRICT_CHECK(ch != lf); |
1655 | | |
1656 | 0 | parser->nread = 0; |
1657 | 0 | nread = 0; |
1658 | | |
1659 | | /* Exit, the rest of the connect is in a different protocol. */ |
1660 | 0 | if (parser->upgrade) { |
1661 | 0 | CROW_CALLBACK_NOTIFY(message_complete); |
1662 | 0 | parser->nread = nread; |
1663 | 0 | return (p - data) + 1; |
1664 | 0 | } |
1665 | | |
1666 | 0 | if (parser->flags & F_SKIPBODY) { |
1667 | 0 | CROW_CALLBACK_NOTIFY(message_complete); |
1668 | 0 | } else if (parser->flags & F_CHUNKED) { |
1669 | | /* chunked encoding - ignore Content-Length header, |
1670 | | * prepare for a chunk */ |
1671 | 0 | parser->state = s_chunk_size_start; |
1672 | 0 | } |
1673 | 0 | else if (parser->uses_transfer_encoding == 1) |
1674 | 0 | { |
1675 | 0 | if (!lenient) |
1676 | 0 | { |
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 | 0 | CROW_SET_ERRNO(CHPE_INVALID_TRANSFER_ENCODING); |
1686 | 0 | parser->nread = nread; |
1687 | 0 | return (p - data); /* Error */ |
1688 | 0 | } |
1689 | 0 | else |
1690 | 0 | { |
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 | 0 | parser->state = s_body_identity_eof; |
1699 | 0 | } |
1700 | 0 | } |
1701 | 0 | else |
1702 | 0 | { |
1703 | 0 | if (parser->content_length == 0) |
1704 | 0 | { |
1705 | | /* Content-Length header given but zero: Content-Length: 0\r\n */ |
1706 | 0 | CROW_CALLBACK_NOTIFY(message_complete); |
1707 | 0 | } |
1708 | 0 | else if (parser->content_length != CROW_ULLONG_MAX) |
1709 | 0 | { |
1710 | | /* Content-Length header given and non-zero */ |
1711 | 0 | parser->state = s_body_identity; |
1712 | 0 | } |
1713 | 0 | else |
1714 | 0 | { |
1715 | | /* Assume content-length 0 - read the next */ |
1716 | 0 | CROW_CALLBACK_NOTIFY(message_complete); |
1717 | 0 | } |
1718 | 0 | } |
1719 | | |
1720 | 0 | break; |
1721 | 0 | } |
1722 | | |
1723 | 0 | case s_body_identity: |
1724 | 0 | { |
1725 | 0 | uint64_t to_read = CROW_MIN(parser->content_length, |
1726 | 0 | (uint64_t) ((data + len) - p)); |
1727 | |
|
1728 | 0 | assert(parser->content_length != 0 |
1729 | 0 | && 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 | 0 | CROW_MARK(body); |
1737 | 0 | parser->content_length -= to_read; |
1738 | 0 | p += to_read - 1; |
1739 | |
|
1740 | 0 | if (parser->content_length == 0) { |
1741 | 0 | 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 | 0 | CROW_CALLBACK_DATA_(body, p - body_mark + 1, p - data); |
1753 | 0 | CROW_REEXECUTE(); |
1754 | 0 | } |
1755 | | |
1756 | 0 | break; |
1757 | 0 | } |
1758 | | |
1759 | | /* read until EOF */ |
1760 | 0 | case s_body_identity_eof: |
1761 | 0 | CROW_MARK(body); |
1762 | 0 | p = data + len - 1; |
1763 | |
|
1764 | 0 | break; |
1765 | | |
1766 | 0 | case s_message_done: |
1767 | 0 | CROW_CALLBACK_NOTIFY(message_complete); |
1768 | 0 | break; |
1769 | | |
1770 | 0 | case s_chunk_size_start: |
1771 | 0 | { |
1772 | 0 | assert(nread == 1); |
1773 | 0 | assert(parser->flags & F_CHUNKED); |
1774 | |
|
1775 | 0 | unhex_val = unhex[static_cast<unsigned char>(ch)]; |
1776 | 0 | if (CROW_UNLIKELY(unhex_val == -1)) { |
1777 | 0 | CROW_SET_ERRNO(CHPE_INVALID_CHUNK_SIZE); |
1778 | 0 | goto error; |
1779 | 0 | } |
1780 | | |
1781 | 0 | parser->content_length = unhex_val; |
1782 | 0 | parser->state = s_chunk_size; |
1783 | 0 | break; |
1784 | 0 | } |
1785 | | |
1786 | 0 | case s_chunk_size: |
1787 | 0 | { |
1788 | 0 | uint64_t t; |
1789 | |
|
1790 | 0 | assert(parser->flags & F_CHUNKED); |
1791 | |
|
1792 | 0 | if (ch == cr) { |
1793 | 0 | parser->state = s_chunk_size_almost_done; |
1794 | 0 | break; |
1795 | 0 | } |
1796 | | |
1797 | 0 | unhex_val = unhex[static_cast<unsigned char>(ch)]; |
1798 | |
|
1799 | 0 | if (unhex_val == -1) { |
1800 | 0 | if (ch == ';' || ch == ' ') { |
1801 | 0 | parser->state = s_chunk_parameters; |
1802 | 0 | break; |
1803 | 0 | } |
1804 | | |
1805 | 0 | CROW_SET_ERRNO(CHPE_INVALID_CHUNK_SIZE); |
1806 | 0 | goto error; |
1807 | 0 | } |
1808 | | |
1809 | 0 | t = parser->content_length; |
1810 | 0 | t *= 16; |
1811 | 0 | t += unhex_val; |
1812 | | |
1813 | | /* Overflow? Test against a conservative limit for simplicity. */ |
1814 | 0 | if (CROW_UNLIKELY((CROW_ULLONG_MAX - 16) / 16 < parser->content_length)) { |
1815 | 0 | CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH); |
1816 | 0 | goto error; |
1817 | 0 | } |
1818 | | |
1819 | 0 | parser->content_length = t; |
1820 | 0 | break; |
1821 | 0 | } |
1822 | | |
1823 | 0 | case s_chunk_parameters: |
1824 | 0 | { |
1825 | 0 | assert(parser->flags & F_CHUNKED); |
1826 | | /* just ignore this shit. TODO check for overflow */ |
1827 | 0 | if (ch == cr) { |
1828 | 0 | parser->state = s_chunk_size_almost_done; |
1829 | 0 | break; |
1830 | 0 | } |
1831 | 0 | break; |
1832 | 0 | } |
1833 | | |
1834 | 0 | case s_chunk_size_almost_done: |
1835 | 0 | { |
1836 | 0 | assert(parser->flags & F_CHUNKED); |
1837 | 0 | CROW_STRICT_CHECK(ch != lf); |
1838 | | |
1839 | 0 | parser->nread = 0; |
1840 | 0 | nread = 0; |
1841 | |
|
1842 | 0 | if (parser->content_length == 0) { |
1843 | 0 | parser->flags |= F_TRAILING; |
1844 | 0 | parser->state = s_header_field_start; |
1845 | 0 | } else { |
1846 | 0 | parser->state = s_chunk_data; |
1847 | 0 | } |
1848 | 0 | break; |
1849 | 0 | } |
1850 | | |
1851 | 0 | case s_chunk_data: |
1852 | 0 | { |
1853 | 0 | uint64_t to_read = CROW_MIN(parser->content_length, |
1854 | 0 | (uint64_t) ((data + len) - p)); |
1855 | |
|
1856 | 0 | assert(parser->flags & F_CHUNKED); |
1857 | 0 | assert(parser->content_length != 0 |
1858 | 0 | && 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 | 0 | CROW_MARK(body); |
1864 | 0 | parser->content_length -= to_read; |
1865 | 0 | p += to_read - 1; |
1866 | |
|
1867 | 0 | if (parser->content_length == 0) { |
1868 | 0 | parser->state = s_chunk_data_almost_done; |
1869 | 0 | } |
1870 | |
|
1871 | 0 | break; |
1872 | 0 | } |
1873 | | |
1874 | 0 | case s_chunk_data_almost_done: |
1875 | 0 | assert(parser->flags & F_CHUNKED); |
1876 | 0 | assert(parser->content_length == 0); |
1877 | 0 | CROW_STRICT_CHECK(ch != cr); |
1878 | 0 | parser->state = s_chunk_data_done; |
1879 | 0 | CROW_CALLBACK_DATA(body); |
1880 | 0 | break; |
1881 | | |
1882 | 0 | case s_chunk_data_done: |
1883 | 0 | assert(parser->flags & F_CHUNKED); |
1884 | 0 | CROW_STRICT_CHECK(ch != lf); |
1885 | 0 | parser->nread = 0; |
1886 | 0 | nread = 0; |
1887 | 0 | parser->state = s_chunk_size_start; |
1888 | 0 | break; |
1889 | | |
1890 | 0 | default: |
1891 | 0 | assert(0 && "unhandled state"); |
1892 | 0 | CROW_SET_ERRNO(CHPE_INVALID_INTERNAL_STATE); |
1893 | 0 | goto error; |
1894 | 0 | } |
1895 | 0 | } |
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 | 0 | assert(((header_field_mark ? 1 : 0) + |
1908 | 0 | (header_value_mark ? 1 : 0) + |
1909 | 0 | (url_mark ? 1 : 0) + |
1910 | 0 | (body_mark ? 1 : 0)) <= 1); |
1911 | |
|
1912 | 0 | CROW_CALLBACK_DATA_NOADVANCE(header_field); |
1913 | 0 | CROW_CALLBACK_DATA_NOADVANCE(header_value); |
1914 | 0 | CROW_CALLBACK_DATA_NOADVANCE(url); |
1915 | 0 | CROW_CALLBACK_DATA_NOADVANCE(body); |
1916 | | |
1917 | 0 | parser->nread = nread; |
1918 | 0 | return len; |
1919 | | |
1920 | 0 | error: |
1921 | 0 | if (CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK) { |
1922 | 0 | CROW_SET_ERRNO(CHPE_UNKNOWN); |
1923 | 0 | } |
1924 | |
|
1925 | 0 | parser->nread = nread; |
1926 | 0 | return (p - data); |
1927 | 0 | } Unexecuted instantiation: crow::http_parser_execute(crow::http_parser*, crow::http_parser_settings const*, char const*, unsigned long) Unexecuted instantiation: crow::http_parser_execute(crow::http_parser*, crow::http_parser_settings const*, char const*, unsigned long) |
1928 | | |
1929 | | inline void |
1930 | | http_parser_init(http_parser* parser) |
1931 | 0 | { |
1932 | 0 | void *data = parser->data; /* preserve application data */ |
1933 | 0 | memset(parser, 0, sizeof(*parser)); |
1934 | 0 | parser->data = data; |
1935 | 0 | parser->state = s_start_req; |
1936 | 0 | parser->http_errno = CHPE_OK; |
1937 | 0 | } |
1938 | | |
1939 | | /* Return a string name of the given error */ |
1940 | | inline const char * |
1941 | 0 | http_errno_name(enum http_errno err) { |
1942 | 0 | /* Map errno values to strings for human-readable output */ |
1943 | 0 | #define CROW_HTTP_STRERROR_GEN(n, s) { "CHPE_" #n, s }, |
1944 | 0 | static struct { |
1945 | 0 | const char *name; |
1946 | 0 | const char *description; |
1947 | 0 | } http_strerror_tab[] = { |
1948 | 0 | CROW_HTTP_ERRNO_MAP(CROW_HTTP_STRERROR_GEN) |
1949 | 0 | }; |
1950 | 0 | #undef CROW_HTTP_STRERROR_GEN |
1951 | 0 | assert(((size_t) err) < CROW_ARRAY_SIZE(http_strerror_tab)); |
1952 | 0 | return http_strerror_tab[err].name; |
1953 | 0 | } |
1954 | | |
1955 | | /* Return a string description of the given error */ |
1956 | | inline const char * |
1957 | 0 | http_errno_description(enum http_errno err) { |
1958 | | /* Map errno values to strings for human-readable output */ |
1959 | 0 | #define CROW_HTTP_STRERROR_GEN(n, s) { "CHPE_" #n, s }, |
1960 | 0 | static struct { |
1961 | 0 | const char *name; |
1962 | 0 | const char *description; |
1963 | 0 | } http_strerror_tab[] = { |
1964 | 0 | CROW_HTTP_ERRNO_MAP(CROW_HTTP_STRERROR_GEN) |
1965 | 0 | }; |
1966 | 0 | #undef CROW_HTTP_STRERROR_GEN |
1967 | 0 | assert(((size_t) err) < CROW_ARRAY_SIZE(http_strerror_tab)); |
1968 | 0 | return http_strerror_tab[err].description; |
1969 | 0 | } Unexecuted instantiation: crow::http_errno_description(crow::http_errno) Unexecuted instantiation: crow::http_errno_description(crow::http_errno) |
1970 | | |
1971 | | /* Checks if this is the final chunk of the body. */ |
1972 | | inline int |
1973 | 0 | http_body_is_final(const struct http_parser *parser) { |
1974 | 0 | return parser->state == s_message_done; |
1975 | 0 | } |
1976 | | |
1977 | | /* Change the maximum header size provided at compile time. */ |
1978 | | inline void |
1979 | 0 | http_parser_set_max_header_size(uint32_t size) { |
1980 | 0 | max_header_size = size; |
1981 | 0 | } |
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 |