Coverage Report

Created: 2026-11-12 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
9.95k
#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
5.58k
# define CROW_ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
199
#endif
200
201
#ifndef CROW_MIN
202
70.5k
# 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
546k
  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
212
546k
   (1 << ((unsigned int) (i) & 7))))
213
#endif
214
215
3.96k
#define CROW_SET_ERRNO(e)                                            \
216
3.96k
do {                                                                 \
217
3.96k
  parser->nread = nread;                                             \
218
3.96k
  parser->http_errno = (e);                                          \
219
3.96k
} while(0)
220
221
/* Run the notify callback FOR, returning ER if it fails */
222
19.8k
#define CROW_CALLBACK_NOTIFY_(FOR, ER)                               \
223
19.8k
do {                                                                 \
224
19.8k
  assert(CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK);                 \
225
19.8k
                                                                     \
226
19.8k
  if (CROW_LIKELY(settings->on_##FOR)) {                             \
227
19.8k
    if (CROW_UNLIKELY(0 != settings->on_##FOR(parser))) {            \
228
0
      CROW_SET_ERRNO(CHPE_CB_##FOR);                                 \
229
0
    }                                                                \
230
19.8k
                                                                     \
231
19.8k
    /* We either errored above or got paused; get out */             \
232
19.8k
    if (CROW_UNLIKELY(CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK)) {  \
233
0
      return (ER);                                                   \
234
0
    }                                                                \
235
19.8k
  }                                                                  \
236
19.8k
} while (0)
237
238
/* Run the notify callback FOR and consume the current byte */
239
6.38k
#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
13.4k
#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
325k
#define CROW_CALLBACK_DATA_(FOR, LEN, ER)                            \
246
325k
do {                                                                 \
247
325k
  assert(CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK);                 \
248
325k
                                                                     \
249
325k
  if (FOR##_mark) {                                                  \
250
313k
    if (CROW_LIKELY(settings->on_##FOR)) {                           \
251
313k
      if (CROW_UNLIKELY(0 !=                                         \
252
313k
          settings->on_##FOR(parser, FOR##_mark, (LEN)))) {          \
253
0
        CROW_SET_ERRNO(CHPE_CB_##FOR);                               \
254
0
      }                                                              \
255
313k
                                                                     \
256
313k
      /* We either errored above or got paused; get out */           \
257
313k
      if (CROW_UNLIKELY(CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK)) {\
258
0
        return (ER);                                                 \
259
0
      }                                                              \
260
313k
    }                                                                \
261
313k
    FOR##_mark = NULL;                                               \
262
313k
  }                                                                  \
263
325k
} while (0)
264
265
/* Run the data callback FOR and consume the current byte */
266
#define CROW_CALLBACK_DATA(FOR)                                      \
267
155k
    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
169k
    CROW_CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
272
273
/* Set the mark FOR; non-destructive if mark is already set */
274
318k
#define CROW_MARK(FOR)                                               \
275
318k
do {                                                                 \
276
318k
  if (!FOR##_mark) {                                                 \
277
318k
    FOR##_mark = p;                                                  \
278
318k
  }                                                                  \
279
318k
} 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
2.10M
#define CROW_COUNT_HEADER_SIZE(V)                                    \
293
2.10M
do {                                                                 \
294
2.10M
  nread += (uint32_t)(V);                                            \
295
2.10M
  if (CROW_UNLIKELY(nread > max_header_size)) {                      \
296
49
    CROW_SET_ERRNO(CHPE_HEADER_OVERFLOW);                            \
297
49
    goto error;                                                      \
298
49
  }                                                                  \
299
2.10M
} while (0)
300
#define CROW_REEXECUTE()                                             \
301
284k
  goto reexecute;                                                    \
302
303
57.5k
#define CROW_PROXY_CONNECTION "proxy-connection"
304
120k
#define CROW_CONNECTION "connection"
305
27.3k
#define CROW_CONTENT_LENGTH "content-length"
306
178k
#define CROW_TRANSFER_ENCODING "transfer-encoding"
307
66.3k
#define CROW_UPGRADE "upgrade"
308
36.6k
#define CROW_CHUNKED "chunked"
309
16.0k
#define CROW_KEEP_ALIVE "keep-alive"
310
17.1k
#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
1.82M
#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
1.58M
#define CROW_LOWER(c)            (unsigned char)(c | 0x20)
440
1.17M
#define CROW_IS_ALPHA(c)         (CROW_LOWER(c) >= 'a' && CROW_LOWER(c) <= 'z')
441
199k
#define CROW_IS_NUM(c)           ((c) >= '0' && (c) <= '9')
442
1.17M
#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
704k
#define CROW_IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
445
116k
  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' ||    \
446
704k
  (c) == ')')
447
1.17M
#define CROW_IS_USERINFO_CHAR(c) (CROW_IS_ALPHANUM(c) || CROW_IS_MARK(c) || (c) == '%' || \
448
588k
  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' ||                   \
449
588k
  (c) == '$' || (c) == ',')
450
451
6.07M
#define CROW_TOKEN(c)            (tokens[(unsigned char)c])
452
546k
#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
21.4M
  (ch == cr || ch == lf || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
461
462
0
#define CROW_start_state s_start_req
463
464
11.1k
# define CROW_STRICT_CHECK(cond)                                     \
465
11.1k
do {                                                                 \
466
11.1k
  if (cond) {                                                        \
467
142
    CROW_SET_ERRNO(CHPE_STRICT);                                     \
468
142
    goto error;                                                      \
469
142
  }                                                                  \
470
11.1k
} 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
1.14M
{
487
2.28M
# define CROW_T(v) 0
488
489
490
1.14M
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
1.14M
        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
1.14M
        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
1.14M
        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
1.14M
        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
499
/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
500
1.14M
        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
501
/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
502
1.14M
        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.14M
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
505
/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
506
1.14M
        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.14M
        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.14M
        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.14M
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
513
/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
514
1.14M
        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.14M
        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.14M
        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.14M
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
521
/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
522
1.14M
        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
523
524
1.14M
#undef CROW_T
525
526
1.14M
  if (ch == ' ' || ch == '\r' || ch == '\n') {
527
2
    return s_dead;
528
2
  }
529
1.14M
  if (ch == '\t' || ch == '\f') {
530
12
    return s_dead;
531
12
  }
532
533
1.14M
  switch (s) {
534
4.15k
    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
4.15k
      if (ch == '/' || ch == '*') {
540
3.80k
        return s_req_path;
541
3.80k
      }
542
543
347
      if (CROW_IS_ALPHA(ch)) {
544
318
        return s_req_schema;
545
318
      }
546
547
29
      break;
548
549
496
    case s_req_schema:
550
496
      if (CROW_IS_ALPHA(ch)) {
551
200
        return s;
552
200
      }
553
554
296
      if (ch == ':') {
555
280
        return s_req_schema_slash;
556
280
      }
557
558
16
      break;
559
560
277
    case s_req_schema_slash:
561
277
      if (ch == '/') {
562
271
        return s_req_schema_slash_slash;
563
271
      }
564
565
6
      break;
566
567
268
    case s_req_schema_slash_slash:
568
268
      if (ch == '/') {
569
257
        return s_req_server_start;
570
257
      }
571
572
11
      break;
573
574
4.39k
    case s_req_server_with_at:
575
4.39k
      if (ch == '@') {
576
1
        return s_dead;
577
1
      }
578
579
    /* fall through */
580
4.70k
    case s_req_server_start:
581
592k
    case s_req_server:
582
592k
      if (ch == '/') {
583
12
        return s_req_path;
584
12
      }
585
586
592k
      if (ch == '?') {
587
19
          parser->qs_point = p - url_mark;
588
19
        return s_req_query_string_start;
589
19
      }
590
591
592k
      if (ch == '@') {
592
4.40k
        return s_req_server_with_at;
593
4.40k
      }
594
595
588k
      if (CROW_IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
596
588k
        return s_req_server;
597
588k
      }
598
599
37
      break;
600
601
211k
    case s_req_path:
602
211k
      if (CROW_IS_URL_CHAR(ch)) {
603
210k
        return s;
604
210k
      }
605
301
      else if (ch == '?')
606
293
      {
607
293
          parser->qs_point = p - url_mark;
608
293
          return s_req_query_string_start;
609
293
      }
610
611
8
      break;
612
613
290
    case s_req_query_string_start:
614
335k
    case s_req_query_string:
615
335k
      if (CROW_IS_URL_CHAR(ch)) {
616
333k
        return s_req_query_string;
617
333k
      }
618
2.05k
      else if (ch == '?')
619
2.04k
      {
620
2.04k
          return s_req_query_string;
621
2.04k
      }
622
623
8
      break;
624
625
8
    default:
626
0
      break;
627
1.14M
  }
628
629
  /* We should never fall out of the switch above unless there's an error */
630
115
  return s_dead;
631
1.14M
}
Unexecuted instantiation: crow::parse_url_char(crow::state, char, crow::http_parser*, char const*, char const*)
crow::parse_url_char(crow::state, char, crow::http_parser*, char const*, char const*)
Line
Count
Source
486
1.14M
{
487
1.14M
# define CROW_T(v) 0
488
489
490
1.14M
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
1.14M
        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
1.14M
        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
1.14M
        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
1.14M
        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
499
/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
500
1.14M
        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
501
/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
502
1.14M
        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.14M
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
505
/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
506
1.14M
        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.14M
        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.14M
        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.14M
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
513
/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
514
1.14M
        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.14M
        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.14M
        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.14M
        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
521
/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
522
1.14M
        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
523
524
1.14M
#undef CROW_T
525
526
1.14M
  if (ch == ' ' || ch == '\r' || ch == '\n') {
527
2
    return s_dead;
528
2
  }
529
1.14M
  if (ch == '\t' || ch == '\f') {
530
12
    return s_dead;
531
12
  }
532
533
1.14M
  switch (s) {
534
4.15k
    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
4.15k
      if (ch == '/' || ch == '*') {
540
3.80k
        return s_req_path;
541
3.80k
      }
542
543
347
      if (CROW_IS_ALPHA(ch)) {
544
318
        return s_req_schema;
545
318
      }
546
547
29
      break;
548
549
496
    case s_req_schema:
550
496
      if (CROW_IS_ALPHA(ch)) {
551
200
        return s;
552
200
      }
553
554
296
      if (ch == ':') {
555
280
        return s_req_schema_slash;
556
280
      }
557
558
16
      break;
559
560
277
    case s_req_schema_slash:
561
277
      if (ch == '/') {
562
271
        return s_req_schema_slash_slash;
563
271
      }
564
565
6
      break;
566
567
268
    case s_req_schema_slash_slash:
568
268
      if (ch == '/') {
569
257
        return s_req_server_start;
570
257
      }
571
572
11
      break;
573
574
4.39k
    case s_req_server_with_at:
575
4.39k
      if (ch == '@') {
576
1
        return s_dead;
577
1
      }
578
579
    /* fall through */
580
4.70k
    case s_req_server_start:
581
592k
    case s_req_server:
582
592k
      if (ch == '/') {
583
12
        return s_req_path;
584
12
      }
585
586
592k
      if (ch == '?') {
587
19
          parser->qs_point = p - url_mark;
588
19
        return s_req_query_string_start;
589
19
      }
590
591
592k
      if (ch == '@') {
592
4.40k
        return s_req_server_with_at;
593
4.40k
      }
594
595
588k
      if (CROW_IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
596
588k
        return s_req_server;
597
588k
      }
598
599
37
      break;
600
601
211k
    case s_req_path:
602
211k
      if (CROW_IS_URL_CHAR(ch)) {
603
210k
        return s;
604
210k
      }
605
301
      else if (ch == '?')
606
293
      {
607
293
          parser->qs_point = p - url_mark;
608
293
          return s_req_query_string_start;
609
293
      }
610
611
8
      break;
612
613
290
    case s_req_query_string_start:
614
335k
    case s_req_query_string:
615
335k
      if (CROW_IS_URL_CHAR(ch)) {
616
333k
        return s_req_query_string;
617
333k
      }
618
2.05k
      else if (ch == '?')
619
2.04k
      {
620
2.04k
          return s_req_query_string;
621
2.04k
      }
622
623
8
      break;
624
625
8
    default:
626
0
      break;
627
1.14M
  }
628
629
  /* We should never fall out of the switch above unless there's an error */
630
115
  return s_dead;
631
1.14M
}
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
7.64k
{
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
7.64k
static const char tokens[256] = {
647
/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
648
7.64k
        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
7.64k
        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
7.64k
        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
7.64k
        0,       0,       0,       0,       0,       0,       0,       0,
655
/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
656
7.64k
        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
657
/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
658
7.64k
        0,       0,      '*',     '+',      0,      '-',     '.',      0,
659
/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
660
7.64k
       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
661
/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
662
7.64k
       '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
7.64k
        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
7.64k
       '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
7.64k
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
669
/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
670
7.64k
       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
671
/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
672
7.64k
       '`',     '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
7.64k
       '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
7.64k
       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
677
/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
678
7.64k
       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
679
680
681
7.64k
static const int8_t unhex[256] =
682
7.64k
  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
683
7.64k
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
684
7.64k
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
685
7.64k
  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
686
7.64k
  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
687
7.64k
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
688
7.64k
  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
689
7.64k
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
690
7.64k
  };
691
692
693
694
7.64k
  char c, ch;
695
7.64k
  int8_t unhex_val;
696
7.64k
  const char *p = data;
697
7.64k
  const char *header_field_mark = 0;
698
7.64k
  const char *header_value_mark = 0;
699
7.64k
  const char *url_mark = 0;
700
7.64k
  const char *url_start_mark = 0;
701
7.64k
  const char *body_mark = 0;
702
7.64k
  const unsigned int lenient = parser->lenient_http_headers;
703
7.64k
  const unsigned int allow_chunked_length = parser->allow_chunked_length;
704
705
7.64k
  uint32_t nread = parser->nread;
706
707
  /* We're in an error state. Don't bother doing anything. */
708
7.64k
  if (CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK) {
709
0
    return 0;
710
0
  }
711
712
7.64k
  if (len == 0) {
713
3.03k
    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
16
      case s_start_req:
721
16
        return 0;
722
723
3.02k
      default:
724
3.02k
        CROW_SET_ERRNO(CHPE_INVALID_EOF_STATE);
725
3.02k
        return 1;
726
3.03k
    }
727
3.03k
  }
728
729
730
4.60k
  if (parser->state == s_header_field)
731
0
    header_field_mark = data;
732
4.60k
  if (parser->state == s_header_value)
733
0
    header_value_mark = data;
734
4.60k
  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
4.60k
  default:
747
4.60k
    break;
748
4.60k
  }
749
750
1.82M
  for (p=data; p != data + len; p++) {
751
1.82M
    ch = *p;
752
753
1.82M
    if (CROW_PARSING_HEADER(parser->state))
754
1.81M
      CROW_COUNT_HEADER_SIZE(1);
755
756
2.13M
reexecute:
757
2.13M
    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
5.00k
      case s_start_req:
770
5.00k
      {
771
5.00k
        if (ch == cr || ch == lf)
772
412
          break;
773
4.59k
        parser->flags = 0;
774
4.59k
        parser->uses_transfer_encoding = 0;
775
4.59k
        parser->content_length = CROW_ULLONG_MAX;
776
777
4.59k
        if (CROW_UNLIKELY(!CROW_IS_ALPHA(ch))) {
778
25
          CROW_SET_ERRNO(CHPE_INVALID_METHOD);
779
25
          goto error;
780
25
        }
781
782
4.56k
        parser->method = 0;
783
4.56k
        parser->index = 1;
784
4.56k
        switch (ch) {
785
38
          case 'A': parser->method = (unsigned)HTTPMethod::Acl;                                                              break;
786
9
          case 'B': parser->method = (unsigned)HTTPMethod::Bind;                                                             break;
787
124
          case 'C': parser->method = (unsigned)HTTPMethod::Connect;   /* or COPY, CHECKOUT */                                break;
788
10
          case 'D': parser->method = (unsigned)HTTPMethod::Delete;                                                           break;
789
3.51k
          case 'G': parser->method = (unsigned)HTTPMethod::Get;                                                              break;
790
15
          case 'H': parser->method = (unsigned)HTTPMethod::Head;                                                             break;
791
32
          case 'L': parser->method = (unsigned)HTTPMethod::Lock;      /* or LINK */                                          break;
792
45
          case 'M': parser->method = (unsigned)HTTPMethod::MkCol;     /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
793
10
          case 'N': parser->method = (unsigned)HTTPMethod::Notify;                                                           break;
794
8
          case 'O': parser->method = (unsigned)HTTPMethod::Options;                                                          break;
795
594
          case 'P': parser->method = (unsigned)HTTPMethod::Post;      /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */            break;
796
18
          case 'R': parser->method = (unsigned)HTTPMethod::Report;    /* or REBIND */                                        break;
797
63
          case 'S': parser->method = (unsigned)HTTPMethod::Subscribe; /* or SEARCH, SOURCE */                                break;
798
33
          case 'T': parser->method = (unsigned)HTTPMethod::Trace;                                                            break;
799
45
          case 'U': parser->method = (unsigned)HTTPMethod::Unlock;    /* or UNSUBSCRIBE, UNBIND, UNLINK */                   break;
800
6
          default:
801
6
            CROW_SET_ERRNO(CHPE_INVALID_METHOD);
802
6
            goto error;
803
4.56k
        }
804
4.56k
        parser->state = s_req_method;
805
806
4.56k
        CROW_CALLBACK_NOTIFY(message_begin);
807
808
4.56k
        break;
809
4.56k
      }
810
811
13.7k
      case s_req_method:
812
13.7k
      {
813
13.7k
        const char *matcher;
814
13.7k
        if (CROW_UNLIKELY(ch == '\0')) {
815
1
          CROW_SET_ERRNO(CHPE_INVALID_METHOD);
816
1
          goto error;
817
1
        }
818
819
13.7k
        matcher = method_strings[parser->method];
820
13.7k
        if (ch == ' ' && matcher[parser->index] == '\0') {
821
4.22k
          parser->state = s_req_spaces_before_url;
822
9.50k
        } else if (ch == matcher[parser->index]) {
823
8.52k
          ; /* nada */
824
8.52k
        } else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
825
826
947
          switch (parser->method << 16 | parser->index << 8 | ch) {
827
0
#define CROW_XX(meth, pos, ch, new_meth) \
828
684
            case ((unsigned)HTTPMethod::meth << 16 | pos << 8 | ch): \
829
684
              parser->method = (unsigned)HTTPMethod::new_meth; break;
830
831
540
            CROW_XX(Post,      1, 'U', Put)
832
2
            CROW_XX(Post,      1, 'A', Patch)
833
20
            CROW_XX(Post,      1, 'R', Propfind)
834
2
            CROW_XX(Put,       2, 'R', Purge)
835
16
            CROW_XX(Connect,   1, 'H', Checkout)
836
28
            CROW_XX(Connect,   2, 'P', Copy)
837
8
            CROW_XX(MkCol,     1, 'O', Move)
838
3
            CROW_XX(MkCol,     1, 'E', Merge)
839
2
            CROW_XX(MkCol,     1, '-', MSearch)
840
1
            CROW_XX(MkCol,     2, 'A', MkActivity)
841
1
            CROW_XX(MkCol,     3, 'A', MkCalendar)
842
5
            CROW_XX(Subscribe, 1, 'E', Search)
843
36
            CROW_XX(Subscribe, 1, 'O', Source)
844
2
            CROW_XX(Report,    2, 'B', Rebind)
845
3
            CROW_XX(Propfind,  4, 'P', Proppatch)
846
9
            CROW_XX(Lock,      1, 'I', Link)
847
2
            CROW_XX(Unlock,    2, 'S', Unsubscribe)
848
2
            CROW_XX(Unlock,    2, 'B', Unbind)
849
2
            CROW_XX(Unlock,    3, 'I', Unlink)
850
0
#undef CROW_XX
851
263
            default:
852
263
              CROW_SET_ERRNO(CHPE_INVALID_METHOD);
853
263
              goto error;
854
947
          }
855
947
        } else {
856
33
          CROW_SET_ERRNO(CHPE_INVALID_METHOD);
857
33
          goto error;
858
33
        }
859
860
13.4k
        CROW_CALLBACK_NOTIFY_NOADVANCE(method);
861
862
13.4k
        ++parser->index;
863
13.4k
        break;
864
13.4k
      }
865
866
4.44k
      case s_req_spaces_before_url:
867
4.44k
      {
868
4.44k
        if (ch == ' ') break;
869
870
4.21k
        CROW_MARK(url);
871
4.21k
        CROW_MARK(url_start);
872
4.21k
        if (parser->method == (unsigned)HTTPMethod::Connect) {
873
52
          parser->state = s_req_server_start;
874
52
        }
875
876
4.21k
        parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p);
877
4.21k
        if (CROW_UNLIKELY(parser->state == s_dead)) {
878
42
          CROW_SET_ERRNO(CHPE_INVALID_URL);
879
42
          goto error;
880
42
        }
881
882
4.16k
        break;
883
4.21k
      }
884
885
4.16k
      case s_req_schema:
886
780
      case s_req_schema_slash:
887
1.05k
      case s_req_schema_slash_slash:
888
1.30k
      case s_req_server_start:
889
1.30k
      {
890
1.30k
        switch (ch) {
891
          /* No whitespace allowed here */
892
4
          case ' ':
893
5
          case cr:
894
6
          case lf:
895
6
            CROW_SET_ERRNO(CHPE_INVALID_URL);
896
6
            goto error;
897
1.30k
          default:
898
1.30k
            parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p);
899
1.30k
            if (CROW_UNLIKELY(parser->state == s_dead)) {
900
57
              CROW_SET_ERRNO(CHPE_INVALID_URL);
901
57
              goto error;
902
57
            }
903
1.30k
        }
904
905
1.24k
        break;
906
1.30k
      }
907
908
587k
      case s_req_server:
909
592k
      case s_req_server_with_at:
910
806k
      case s_req_path:
911
807k
      case s_req_query_string_start:
912
1.14M
      case s_req_query_string:
913
1.14M
      {
914
1.14M
        switch (ch) {
915
364
          case ' ':
916
364
            parser->state = s_req_http_start;
917
364
            CROW_CALLBACK_DATA(url);
918
364
            break;
919
364
          case cr: // No space after URL means no HTTP version. Which means the request is using HTTP/0.9
920
3.15k
          case lf:
921
3.15k
            if (CROW_UNLIKELY(parser->method != (unsigned)HTTPMethod::Get)) // HTTP/0.9 doesn't define any method other than GET
922
5
            {
923
5
              parser->state = s_dead;
924
5
              CROW_SET_ERRNO(CHPE_INVALID_VERSION);
925
5
              goto error;
926
5
            }
927
3.14k
            parser->http_major = 0;
928
3.14k
            parser->http_minor = 9;
929
3.14k
            parser->state = (ch == cr) ?
930
15
              s_req_line_almost_done :
931
3.14k
              s_header_field_start;
932
3.14k
            CROW_CALLBACK_DATA(url);
933
3.14k
            break;
934
1.13M
          default:
935
1.13M
            parser->state = parse_url_char(static_cast<state>(parser->state), ch, parser, url_start_mark, p);
936
1.13M
            if (CROW_UNLIKELY(parser->state == s_dead)) {
937
31
              CROW_SET_ERRNO(CHPE_INVALID_URL);
938
31
              goto error;
939
31
            }
940
1.14M
        }
941
1.14M
        break;
942
1.14M
      }
943
944
1.14M
      case s_req_http_start:
945
580
        switch (ch) {
946
230
          case ' ':
947
230
            break;
948
314
          case 'H':
949
314
            parser->state = s_req_http_H;
950
314
            break;
951
34
          case 'I':
952
34
            if (parser->method == (unsigned)HTTPMethod::Source) {
953
28
              parser->state = s_req_http_I;
954
28
              break;
955
28
            }
956
            /* fall through */
957
8
          default:
958
8
            CROW_SET_ERRNO(CHPE_INVALID_CONSTANT);
959
8
            goto error;
960
580
        }
961
572
        break;
962
963
572
      case s_req_http_H:
964
313
        CROW_STRICT_CHECK(ch != 'T');
965
301
        parser->state = s_req_http_HT;
966
301
        break;
967
968
300
      case s_req_http_HT:
969
300
        CROW_STRICT_CHECK(ch != 'T');
970
295
        parser->state = s_req_http_HTT;
971
295
        break;
972
973
293
      case s_req_http_HTT:
974
293
        CROW_STRICT_CHECK(ch != 'P');
975
281
        parser->state = s_req_http_HTTP;
976
281
        break;
977
978
27
      case s_req_http_I:
979
27
        CROW_STRICT_CHECK(ch != 'C');
980
14
        parser->state = s_req_http_IC;
981
14
        break;
982
983
13
      case s_req_http_IC:
984
13
        CROW_STRICT_CHECK(ch != 'E');
985
1
        parser->state = s_req_http_HTTP;  /* Treat "ICE" as "HTTP". */
986
1
        break;
987
988
280
      case s_req_http_HTTP:
989
280
        CROW_STRICT_CHECK(ch != '/');
990
268
        parser->state = s_req_http_major;
991
268
        break;
992
993
      /* dot */
994
267
      case s_req_http_major:
995
267
        if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) {
996
4
          CROW_SET_ERRNO(CHPE_INVALID_VERSION);
997
4
          goto error;
998
4
        }
999
1000
263
        parser->http_major = ch - '0';
1001
263
        parser->state = s_req_http_dot;
1002
263
        break;
1003
1004
262
      case s_req_http_dot:
1005
262
      {
1006
262
        if (CROW_UNLIKELY(ch != '.')) {
1007
12
          CROW_SET_ERRNO(CHPE_INVALID_VERSION);
1008
12
          goto error;
1009
12
        }
1010
1011
250
        parser->state = s_req_http_minor;
1012
250
        break;
1013
262
      }
1014
1015
      /* minor HTTP version */
1016
249
      case s_req_http_minor:
1017
249
        if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) {
1018
16
          CROW_SET_ERRNO(CHPE_INVALID_VERSION);
1019
16
          goto error;
1020
16
        }
1021
1022
233
        parser->http_minor = ch - '0';
1023
233
        parser->state = s_req_http_end;
1024
233
        break;
1025
1026
      /* end of request line */
1027
227
      case s_req_http_end:
1028
227
      {
1029
227
        if (ch == cr) {
1030
9
          parser->state = s_req_line_almost_done;
1031
9
          break;
1032
9
        }
1033
1034
218
        if (ch == lf) {
1035
205
          parser->state = s_header_field_start;
1036
205
          break;
1037
205
        }
1038
1039
13
        CROW_SET_ERRNO(CHPE_INVALID_VERSION);
1040
13
        goto error;
1041
0
        break;
1042
218
      }
1043
1044
      /* end of request line */
1045
21
      case s_req_line_almost_done:
1046
21
      {
1047
21
        if (CROW_UNLIKELY(ch != lf)) {
1048
11
          CROW_SET_ERRNO(CHPE_LF_EXPECTED);
1049
11
          goto error;
1050
11
        }
1051
1052
10
        parser->state = s_header_field_start;
1053
10
        break;
1054
21
      }
1055
1056
143k
      case s_header_field_start:
1057
143k
      {
1058
143k
        if (ch == cr) {
1059
15
          parser->state = s_headers_almost_done;
1060
15
          break;
1061
15
        }
1062
1063
143k
        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
1.38k
          parser->state = s_headers_almost_done;
1067
1.38k
          CROW_REEXECUTE();
1068
0
        }
1069
1070
142k
        c = CROW_TOKEN(ch);
1071
1072
142k
        if (CROW_UNLIKELY(!c)) {
1073
34
          CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN);
1074
34
          goto error;
1075
34
        }
1076
1077
142k
        CROW_MARK(header_field);
1078
1079
142k
        parser->index = 0;
1080
142k
        parser->state = s_header_field;
1081
1082
142k
        switch (c) {
1083
14.8k
          case 'c':
1084
14.8k
            parser->header_state = h_C;
1085
14.8k
            break;
1086
1087
7.02k
          case 'p':
1088
7.02k
            parser->header_state = h_matching_proxy_connection;
1089
7.02k
            break;
1090
1091
7.04k
          case 't':
1092
7.04k
            parser->header_state = h_matching_transfer_encoding;
1093
7.04k
            break;
1094
1095
8.02k
          case 'u':
1096
8.02k
            parser->header_state = h_matching_upgrade;
1097
8.02k
            break;
1098
1099
105k
          default:
1100
105k
            parser->header_state = h_general;
1101
105k
            break;
1102
142k
        }
1103
142k
        break;
1104
142k
      }
1105
1106
142k
      case s_header_field:
1107
142k
      {
1108
142k
        const char* start = p;
1109
360k
        for (; p != data + len; p++) {
1110
360k
          ch = *p;
1111
360k
          c = CROW_TOKEN(ch);
1112
1113
360k
          if (!c)
1114
141k
            break;
1115
1116
218k
          switch (parser->header_state) {
1117
24.1k
            case h_general: {
1118
24.1k
              size_t left = data + len - p;
1119
24.1k
              const char* pe = p + CROW_MIN(left, max_header_size);
1120
5.56M
              while (p+1 < pe && CROW_TOKEN(p[1])) {
1121
5.54M
                p++;
1122
5.54M
              }
1123
24.1k
              break;
1124
0
            }
1125
1126
13.6k
            case h_C:
1127
13.6k
              parser->index++;
1128
13.6k
              parser->header_state = (c == 'o' ? h_CO : h_general);
1129
13.6k
              break;
1130
1131
12.7k
            case h_CO:
1132
12.7k
              parser->index++;
1133
12.7k
              parser->header_state = (c == 'n' ? h_CON : h_general);
1134
12.7k
              break;
1135
1136
12.3k
            case h_CON:
1137
12.3k
              parser->index++;
1138
12.3k
              switch (c) {
1139
9.86k
                case 'n':
1140
9.86k
                  parser->header_state = h_matching_connection;
1141
9.86k
                  break;
1142
1.65k
                case 't':
1143
1.65k
                  parser->header_state = h_matching_content_length;
1144
1.65k
                  break;
1145
807
                default:
1146
807
                  parser->header_state = h_general;
1147
807
                  break;
1148
12.3k
              }
1149
12.3k
              break;
1150
1151
            /* connection */
1152
1153
41.4k
            case h_matching_connection:
1154
41.4k
              parser->index++;
1155
41.4k
              if (parser->index > sizeof(CROW_CONNECTION)-1 || c != CROW_CONNECTION[parser->index]) {
1156
3.99k
                parser->header_state = h_general;
1157
37.4k
              } else if (parser->index == sizeof(CROW_CONNECTION)-2) {
1158
5.60k
                parser->header_state = h_connection;
1159
5.60k
              }
1160
41.4k
              break;
1161
1162
            /* proxy-connection */
1163
1164
20.2k
            case h_matching_proxy_connection:
1165
20.2k
              parser->index++;
1166
20.2k
              if (parser->index > sizeof(CROW_PROXY_CONNECTION)-1 || c != CROW_PROXY_CONNECTION[parser->index]) {
1167
3.22k
                parser->header_state = h_general;
1168
17.0k
              } else if (parser->index == sizeof(CROW_PROXY_CONNECTION)-2) {
1169
1.08k
                parser->header_state = h_connection;
1170
1.08k
              }
1171
20.2k
              break;
1172
1173
            /* content-length */
1174
1175
9.34k
            case h_matching_content_length:
1176
9.34k
              parser->index++;
1177
9.34k
              if (parser->index > sizeof(CROW_CONTENT_LENGTH)-1 || c != CROW_CONTENT_LENGTH[parser->index]) {
1178
695
                parser->header_state = h_general;
1179
8.65k
              } else if (parser->index == sizeof(CROW_CONTENT_LENGTH)-2) {
1180
716
                parser->header_state = h_content_length;
1181
716
              }
1182
9.34k
              break;
1183
1184
            /* transfer-encoding */
1185
1186
60.2k
            case h_matching_transfer_encoding:
1187
60.2k
              parser->index++;
1188
60.2k
              if (parser->index > sizeof(CROW_TRANSFER_ENCODING)-1 || c != CROW_TRANSFER_ENCODING[parser->index]) {
1189
2.84k
                parser->header_state = h_general;
1190
57.4k
              } else if (parser->index == sizeof(CROW_TRANSFER_ENCODING)-2) {
1191
3.38k
                parser->header_state = h_transfer_encoding;
1192
3.38k
                parser->uses_transfer_encoding = 1;
1193
3.38k
              }
1194
60.2k
              break;
1195
1196
            /* upgrade */
1197
1198
23.0k
            case h_matching_upgrade:
1199
23.0k
              parser->index++;
1200
23.0k
              if (parser->index > sizeof(CROW_UPGRADE)-1 || c != CROW_UPGRADE[parser->index]) {
1201
2.81k
                parser->header_state = h_general;
1202
20.2k
              } else if (parser->index == sizeof(CROW_UPGRADE)-2) {
1203
2.94k
                parser->header_state = h_upgrade;
1204
2.94k
              }
1205
23.0k
              break;
1206
1207
534
            case h_connection:
1208
745
            case h_content_length:
1209
962
            case h_transfer_encoding:
1210
1.67k
            case h_upgrade:
1211
1.67k
              if (ch != ' ') parser->header_state = h_general;
1212
1.67k
              break;
1213
1214
0
            default:
1215
0
              assert(0 && "Unknown header_state");
1216
0
              break;
1217
218k
          }
1218
218k
        }
1219
1220
142k
        if (p == data + len) {
1221
460
          --p;
1222
460
          CROW_COUNT_HEADER_SIZE(p - start);
1223
450
          break;
1224
460
        }
1225
1226
141k
        CROW_COUNT_HEADER_SIZE(p - start);
1227
1228
141k
        if (ch == ':') {
1229
141k
          parser->state = s_header_value_discard_ws;
1230
141k
          CROW_CALLBACK_DATA(header_field);
1231
141k
          break;
1232
141k
        }
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
54
        CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN);
1247
54
        goto error;
1248
141k
      }
1249
1250
156k
      case s_header_value_discard_ws:
1251
156k
        if (ch == ' ' || ch == '\t') break;
1252
1253
144k
        if (ch == cr) {
1254
1.20k
          parser->state = s_header_value_discard_ws_almost_done;
1255
1.20k
          break;
1256
1.20k
        }
1257
1258
142k
        if (ch == lf) {
1259
14.1k
          parser->state = s_header_value_discard_lws;
1260
14.1k
          break;
1261
14.1k
        }
1262
1263
        /* fall through */
1264
1265
152k
      case s_header_value_start:
1266
152k
      {
1267
152k
        CROW_MARK(header_value);
1268
1269
152k
        parser->state = s_header_value;
1270
152k
        parser->index = 0;
1271
1272
152k
        c = CROW_LOWER(ch);
1273
1274
152k
        switch (parser->header_state) {
1275
2.16k
          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
2.16k
            if (ch != 'h' || p+1 == (data + len) || *(p+1) != '2') {
1282
1.48k
              parser->flags |= F_UPGRADE;
1283
1.48k
            }
1284
2.16k
            parser->header_state = h_general;
1285
2.16k
            break;
1286
1287
3.02k
          case h_transfer_encoding:
1288
            /* looking for 'Transfer-Encoding: chunked' */
1289
3.02k
            if ('c' == c) {
1290
2.04k
              parser->header_state = h_matching_transfer_encoding_chunked;
1291
2.04k
            } else {
1292
978
              parser->header_state = h_matching_transfer_encoding_token;
1293
978
            }
1294
3.02k
            break;
1295
1296
          /* Multi-value `Transfer-Encoding` header */
1297
608
          case h_matching_transfer_encoding_token_start:
1298
608
            break;
1299
1300
431
          case h_content_length:
1301
431
            if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) {
1302
11
              CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1303
11
              goto error;
1304
11
            }
1305
1306
420
            if (parser->flags & F_CONTENTLENGTH) {
1307
1
              CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH);
1308
1
              goto error;
1309
1
            }
1310
419
            parser->flags |= F_CONTENTLENGTH;
1311
419
            parser->content_length = ch - '0';
1312
419
            parser->header_state = h_content_length_num;
1313
419
            break;
1314
1315
          /* when obsolete line folding is encountered for content length
1316
           * continue to the s_header_value state */
1317
198
          case h_content_length_ws:
1318
198
            break;
1319
1320
8.11k
          case h_connection:
1321
            /* looking for 'Connection: keep-alive' */
1322
8.11k
            if (c == 'k') {
1323
2.17k
              parser->header_state = h_matching_connection_keep_alive;
1324
            /* looking for 'Connection: close' */
1325
5.93k
            } else if (c == 'c') {
1326
2.10k
              parser->header_state = h_matching_connection_close;
1327
3.83k
            } else if (c == ' ' || c == '\t') {
1328
              /* Skip lws */
1329
3.15k
            } else {
1330
676
              parser->header_state = h_general;
1331
676
            }
1332
8.11k
            break;
1333
1334
138k
          default:
1335
138k
            parser->header_state = h_general;
1336
138k
            break;
1337
152k
        }
1338
152k
        break;
1339
152k
      }
1340
1341
152k
      case s_header_value:
1342
152k
      {
1343
152k
        const char* start = p;
1344
152k
        enum header_states h_state = static_cast<header_states>(parser->header_state);
1345
597k
        for (; p != data + len; p++) {
1346
596k
          ch = *p;
1347
1348
596k
          if (ch == cr) {
1349
9.24k
            parser->state = s_header_almost_done;
1350
9.24k
            parser->header_state = h_state;
1351
9.24k
            CROW_CALLBACK_DATA(header_value);
1352
9.24k
            break;
1353
9.24k
          }
1354
1355
587k
          if (ch == lf) {
1356
142k
            parser->state = s_header_almost_done;
1357
142k
            CROW_COUNT_HEADER_SIZE(p - start);
1358
142k
            parser->header_state = h_state;
1359
142k
            CROW_CALLBACK_DATA_NOADVANCE(header_value);
1360
142k
            CROW_REEXECUTE();
1361
0
          }
1362
1363
444k
          if (!lenient && !CROW_IS_HEADER_CHAR(ch)) {
1364
21
            CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN);
1365
21
            goto error;
1366
21
          }
1367
1368
444k
          c = CROW_LOWER(ch);
1369
1370
444k
          switch (h_state) {
1371
44.4k
            case h_general:
1372
44.4k
              {
1373
44.4k
                size_t left = data + len - p;
1374
44.4k
                const char* pe = p + CROW_MIN(left, max_header_size);
1375
1376
21.0M
                for (; p != pe; p++) {
1377
21.0M
                  ch = *p;
1378
21.0M
                  if (ch == cr || ch == lf) {
1379
44.0k
                    --p;
1380
44.0k
                    break;
1381
44.0k
                  }
1382
21.0M
                  if (!lenient && !CROW_IS_HEADER_CHAR(ch)) {
1383
25
                    CROW_SET_ERRNO(CHPE_INVALID_HEADER_TOKEN);
1384
25
                    goto error;
1385
25
                  }
1386
21.0M
                }
1387
44.4k
                if (p == data + len)
1388
247
                  --p;
1389
44.4k
                break;
1390
44.4k
              }
1391
1392
219k
            case h_connection:
1393
219k
            case h_transfer_encoding:
1394
219k
              assert(0 && "Shouldn't get here.");
1395
219k
              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
4.62k
            case h_content_length_num:
1403
4.62k
            {
1404
4.62k
              uint64_t t;
1405
1406
4.62k
              if (ch == ' ') {
1407
19
                h_state = h_content_length_ws;
1408
19
                break;
1409
19
              }
1410
1411
4.60k
              if (CROW_UNLIKELY(!CROW_IS_NUM(ch))) {
1412
9
                CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1413
9
                parser->header_state = h_state;
1414
9
                goto error;
1415
9
              }
1416
1417
4.59k
              t = parser->content_length;
1418
4.59k
              t *= 10;
1419
4.59k
              t += ch - '0';
1420
1421
              /* Overflow? Test against a conservative limit for simplicity. */
1422
4.59k
              if (CROW_UNLIKELY((CROW_ULLONG_MAX - 10) / 10 < parser->content_length)) {
1423
5
                CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1424
5
                parser->header_state = h_state;
1425
5
                goto error;
1426
5
              }
1427
1428
4.58k
              parser->content_length = t;
1429
4.58k
              break;
1430
4.59k
            }
1431
1432
264
            case h_content_length_ws:
1433
264
              if (ch == ' ') break;
1434
9
              CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1435
9
              parser->header_state = h_state;
1436
9
              goto error;
1437
1438
            /* Transfer-Encoding: chunked */
1439
3.19k
            case h_matching_transfer_encoding_token_start:
1440
              /* looking for 'Transfer-Encoding: chunked' */
1441
3.19k
              if ('c' == c) {
1442
518
                h_state = h_matching_transfer_encoding_chunked;
1443
2.67k
              } 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
893
                h_state = h_matching_transfer_encoding_token;
1450
1.78k
              } else if (c == ' ' || c == '\t') {
1451
                /* Skip lws */
1452
1.53k
              } else {
1453
244
                h_state = h_general;
1454
244
              }
1455
3.19k
              break;
1456
1457
12.4k
            case h_matching_transfer_encoding_chunked:
1458
12.4k
              parser->index++;
1459
12.4k
              if (parser->index > sizeof(CROW_CHUNKED)-1 || c != CROW_CHUNKED[parser->index]) {
1460
829
                h_state = h_matching_transfer_encoding_token;
1461
11.6k
              } else if (parser->index == sizeof(CROW_CHUNKED)-2) {
1462
1.54k
                h_state = h_transfer_encoding_chunked;
1463
1.54k
              }
1464
12.4k
              break;
1465
1466
144k
            case h_matching_transfer_encoding_token:
1467
144k
              if (ch == ',') {
1468
1.75k
                h_state = h_matching_transfer_encoding_token_start;
1469
1.75k
                parser->index = 0;
1470
1.75k
              }
1471
144k
              break;
1472
1473
            /* looking for 'Connection: keep-alive' */
1474
5.94k
            case h_matching_connection_keep_alive:
1475
5.94k
              parser->index++;
1476
5.94k
              if (parser->index > sizeof(CROW_KEEP_ALIVE)-1 || c != CROW_KEEP_ALIVE[parser->index]) {
1477
1.83k
                h_state = h_general;
1478
4.11k
              } else if (parser->index == sizeof(CROW_KEEP_ALIVE)-2) {
1479
280
                h_state = h_connection_keep_alive;
1480
280
              }
1481
5.94k
              break;
1482
1483
            /* looking for 'Connection: close' */
1484
5.94k
            case h_matching_connection_close:
1485
5.94k
              parser->index++;
1486
5.94k
              if (parser->index > sizeof(CROW_CLOSE)-1 || c != CROW_CLOSE[parser->index]) {
1487
726
                h_state = h_general;
1488
5.21k
              } else if (parser->index == sizeof(CROW_CLOSE)-2) {
1489
789
                h_state = h_connection_close;
1490
789
              }
1491
5.94k
              break;
1492
1493
              // Edited from original (because of commits that werent included)
1494
1.35k
            case h_transfer_encoding_chunked:
1495
1.35k
              if (ch != ' ') h_state = h_matching_transfer_encoding_token;
1496
1.35k
              break;
1497
1.22k
            case h_connection_keep_alive:
1498
2.24k
            case h_connection_close:
1499
2.24k
              if (ch != ' ') h_state = h_general;
1500
2.24k
              break;
1501
1502
0
            default:
1503
0
              parser->state = s_header_value;
1504
0
              h_state = h_general;
1505
0
              break;
1506
444k
          }
1507
444k
        }
1508
9.84k
        parser->header_state = h_state;
1509
1510
1511
9.84k
        if (p == data + len)
1512
595
          --p;
1513
1514
9.84k
        CROW_COUNT_HEADER_SIZE(p - start);
1515
9.81k
        break;
1516
9.84k
      }
1517
1518
151k
      case s_header_almost_done:
1519
151k
      {
1520
151k
        if (CROW_UNLIKELY(ch != lf)) {
1521
8
          CROW_SET_ERRNO(CHPE_LF_EXPECTED);
1522
8
          goto error;
1523
8
        }
1524
1525
151k
        parser->state = s_header_value_lws;
1526
151k
        break;
1527
151k
      }
1528
1529
151k
      case s_header_value_lws:
1530
151k
      {
1531
151k
        if (ch == ' ' || ch == '\t') {
1532
24.0k
          if (parser->header_state == h_content_length_num) {
1533
              /* treat obsolete line folding as space */
1534
11
              parser->header_state = h_content_length_ws;
1535
11
          }
1536
24.0k
          parser->state = s_header_value_start;
1537
24.0k
          CROW_REEXECUTE();
1538
0
        }
1539
1540
        /* finished the header */
1541
127k
        switch (parser->header_state) {
1542
253
          case h_connection_keep_alive:
1543
253
            parser->flags |= F_CONNECTION_KEEP_ALIVE;
1544
253
            break;
1545
283
          case h_connection_close:
1546
283
            parser->flags |= F_CONNECTION_CLOSE;
1547
283
            break;
1548
744
          case h_transfer_encoding_chunked:
1549
744
            parser->flags |= F_CHUNKED;
1550
744
            break;
1551
126k
          default:
1552
126k
            break;
1553
127k
        }
1554
1555
127k
        parser->state = s_header_field_start;
1556
127k
        CROW_REEXECUTE();
1557
0
      }
1558
1559
1.19k
      case s_header_value_discard_ws_almost_done:
1560
1.19k
      {
1561
1.19k
        CROW_STRICT_CHECK(ch != lf);
1562
1.18k
        parser->state = s_header_value_discard_lws;
1563
1.18k
        break;
1564
1.19k
      }
1565
1566
15.2k
      case s_header_value_discard_lws:
1567
15.2k
      {
1568
15.2k
        if (ch == ' ' || ch == '\t') {
1569
2.65k
          parser->state = s_header_value_discard_ws;
1570
2.65k
          break;
1571
12.6k
        } else {
1572
          /* header value was empty */
1573
12.6k
          CROW_MARK(header_value);
1574
12.6k
          parser->state = s_header_field_start;
1575
12.6k
          CROW_CALLBACK_DATA_NOADVANCE(header_value);
1576
12.6k
          CROW_REEXECUTE();
1577
0
        }
1578
15.2k
      }
1579
1580
1.63k
      case s_headers_almost_done:
1581
1.63k
      {
1582
1.63k
        CROW_STRICT_CHECK(ch != lf);
1583
1584
1.61k
        if (parser->flags & F_TRAILING) {
1585
          /* End of a chunked request */
1586
245
          CROW_CALLBACK_NOTIFY(message_complete);
1587
245
          break;
1588
245
        }
1589
1590
        /* Cannot use transfer-encoding and a content-length header together
1591
           per the HTTP specification. (RFC 7230 Section 3.3.3) */
1592
1.37k
        if ((parser->uses_transfer_encoding == 1) &&
1593
511
            (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
2
          if (parser->flags & F_CHUNKED) {
1598
1
            if (!allow_chunked_length) {
1599
1
              CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH);
1600
1
              goto error;
1601
1
            }
1602
1
          } else if (!lenient) {
1603
1
            CROW_SET_ERRNO(CHPE_UNEXPECTED_CONTENT_LENGTH);
1604
1
            goto error;
1605
1
          }
1606
2
        }
1607
1608
1.37k
        parser->state = s_headers_done;
1609
1610
        /* Set this here so that on_headers_complete() callbacks can see it */
1611
1.37k
        parser->upgrade =
1612
1.37k
          (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
1.37k
        if (settings->on_headers_complete) {
1624
1.37k
          switch (settings->on_headers_complete(parser)) {
1625
1.37k
            case 0:
1626
1.37k
              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
1.37k
          }
1642
1.37k
        }
1643
1644
1.37k
        if (CROW_HTTP_PARSER_ERRNO(parser) != CHPE_OK) {
1645
0
          parser->nread = nread;
1646
0
          return p - data;
1647
0
        }
1648
1649
1.37k
        CROW_REEXECUTE();
1650
0
      }
1651
1652
1.77k
      case s_headers_done:
1653
1.77k
      {
1654
1.77k
        CROW_STRICT_CHECK(ch != lf);
1655
1656
1.74k
        parser->nread = 0;
1657
1.74k
        nread = 0;
1658
1659
        /* Exit, the rest of the connect is in a different protocol. */
1660
1.74k
        if (parser->upgrade) {
1661
48
          CROW_CALLBACK_NOTIFY(message_complete);
1662
48
          parser->nread = nread;
1663
48
          return (p - data) + 1;
1664
48
        }
1665
1666
1.69k
        if (parser->flags & F_SKIPBODY) {
1667
0
          CROW_CALLBACK_NOTIFY(message_complete);
1668
1.69k
        } else if (parser->flags & F_CHUNKED) {
1669
          /* chunked encoding - ignore Content-Length header,
1670
           * prepare for a chunk */
1671
506
            parser->state = s_chunk_size_start;
1672
506
        }
1673
1.19k
        else if (parser->uses_transfer_encoding == 1)
1674
1
        {
1675
1
            if (!lenient)
1676
1
            {
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
1
                CROW_SET_ERRNO(CHPE_INVALID_TRANSFER_ENCODING);
1686
1
                parser->nread = nread;
1687
1
                return (p - data); /* Error */
1688
1
            }
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
1
        }
1701
1.19k
        else
1702
1.19k
        {
1703
1.19k
            if (parser->content_length == 0)
1704
194
            {
1705
                /* Content-Length header given but zero: Content-Length: 0\r\n */
1706
194
                CROW_CALLBACK_NOTIFY(message_complete);
1707
194
            }
1708
997
            else if (parser->content_length != CROW_ULLONG_MAX)
1709
245
            {
1710
                /* Content-Length header given and non-zero */
1711
245
                parser->state = s_body_identity;
1712
245
            }
1713
752
            else
1714
752
            {
1715
                /* Assume content-length 0 - read the next */
1716
752
                CROW_CALLBACK_NOTIFY(message_complete);
1717
752
            }
1718
1.19k
        }
1719
1720
1.69k
        break;
1721
1.69k
      }
1722
1723
1.69k
      case s_body_identity:
1724
136
      {
1725
136
        uint64_t to_read = CROW_MIN(parser->content_length,
1726
136
                               (uint64_t) ((data + len) - p));
1727
1728
136
        assert(parser->content_length != 0
1729
136
            && 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
136
        CROW_MARK(body);
1737
136
        parser->content_length -= to_read;
1738
136
        p += to_read - 1;
1739
1740
136
        if (parser->content_length == 0) {
1741
25
          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
25
          CROW_CALLBACK_DATA_(body, p - body_mark + 1, p - data);
1753
25
          CROW_REEXECUTE();
1754
0
        }
1755
1756
111
        break;
1757
136
      }
1758
1759
      /* read until EOF */
1760
111
      case s_body_identity_eof:
1761
0
        CROW_MARK(body);
1762
0
        p = data + len - 1;
1763
1764
0
        break;
1765
1766
585
      case s_message_done:
1767
585
        CROW_CALLBACK_NOTIFY(message_complete);
1768
585
        break;
1769
1770
2.14k
      case s_chunk_size_start:
1771
2.14k
      {
1772
2.14k
        assert(nread == 1);
1773
2.14k
        assert(parser->flags & F_CHUNKED);
1774
1775
2.14k
        unhex_val = unhex[static_cast<unsigned char>(ch)];
1776
2.14k
        if (CROW_UNLIKELY(unhex_val == -1)) {
1777
5
          CROW_SET_ERRNO(CHPE_INVALID_CHUNK_SIZE);
1778
5
          goto error;
1779
5
        }
1780
1781
2.14k
        parser->content_length = unhex_val;
1782
2.14k
        parser->state = s_chunk_size;
1783
2.14k
        break;
1784
2.14k
      }
1785
1786
5.93k
      case s_chunk_size:
1787
5.93k
      {
1788
5.93k
        uint64_t t;
1789
1790
5.93k
        assert(parser->flags & F_CHUNKED);
1791
1792
5.93k
        if (ch == cr) {
1793
1.18k
          parser->state = s_chunk_size_almost_done;
1794
1.18k
          break;
1795
1.18k
        }
1796
1797
4.74k
        unhex_val = unhex[static_cast<unsigned char>(ch)];
1798
1799
4.74k
        if (unhex_val == -1) {
1800
843
          if (ch == ';' || ch == ' ') {
1801
828
            parser->state = s_chunk_parameters;
1802
828
            break;
1803
828
          }
1804
1805
15
          CROW_SET_ERRNO(CHPE_INVALID_CHUNK_SIZE);
1806
15
          goto error;
1807
843
        }
1808
1809
3.90k
        t = parser->content_length;
1810
3.90k
        t *= 16;
1811
3.90k
        t += unhex_val;
1812
1813
        /* Overflow? Test against a conservative limit for simplicity. */
1814
3.90k
        if (CROW_UNLIKELY((CROW_ULLONG_MAX - 16) / 16 < parser->content_length)) {
1815
9
          CROW_SET_ERRNO(CHPE_INVALID_CONTENT_LENGTH);
1816
9
          goto error;
1817
9
        }
1818
1819
3.89k
        parser->content_length = t;
1820
3.89k
        break;
1821
3.90k
      }
1822
1823
1.74k
      case s_chunk_parameters:
1824
1.74k
      {
1825
1.74k
        assert(parser->flags & F_CHUNKED);
1826
        /* just ignore this shit. TODO check for overflow */
1827
1.74k
        if (ch == cr) {
1828
774
          parser->state = s_chunk_size_almost_done;
1829
774
          break;
1830
774
        }
1831
        /* A bare LF or other control character in a chunk extension would let
1832
         * a peer that ends the chunk line at LF frame the body differently. */
1833
971
        if (CROW_UNLIKELY(ch == lf || !CROW_IS_HEADER_CHAR(ch))) {
1834
9
          CROW_SET_ERRNO(CHPE_INVALID_CHUNK_SIZE);
1835
9
          goto error;
1836
9
        }
1837
962
        break;
1838
971
      }
1839
1840
1.94k
      case s_chunk_size_almost_done:
1841
1.94k
      {
1842
1.94k
        assert(parser->flags & F_CHUNKED);
1843
1.94k
        CROW_STRICT_CHECK(ch != lf);
1844
1845
1.94k
        parser->nread = 0;
1846
1.94k
        nread = 0;
1847
1848
1.94k
        if (parser->content_length == 0) {
1849
13
          parser->flags |= F_TRAILING;
1850
13
          parser->state = s_header_field_start;
1851
1.93k
        } else {
1852
1.93k
          parser->state = s_chunk_data;
1853
1.93k
        }
1854
1.94k
        break;
1855
1.94k
      }
1856
1857
1.82k
      case s_chunk_data:
1858
1.82k
      {
1859
1.82k
        uint64_t to_read = CROW_MIN(parser->content_length,
1860
1.82k
                               (uint64_t) ((data + len) - p));
1861
1862
1.82k
        assert(parser->flags & F_CHUNKED);
1863
1.82k
        assert(parser->content_length != 0
1864
1.82k
            && parser->content_length != CROW_ULLONG_MAX);
1865
1866
        /* See the explanation in s_body_identity for why the content
1867
         * length and data pointers are managed this way.
1868
         */
1869
1.82k
        CROW_MARK(body);
1870
1.82k
        parser->content_length -= to_read;
1871
1.82k
        p += to_read - 1;
1872
1873
1.82k
        if (parser->content_length == 0) {
1874
1.70k
          parser->state = s_chunk_data_almost_done;
1875
1.70k
        }
1876
1877
1.82k
        break;
1878
1.94k
      }
1879
1880
1.68k
      case s_chunk_data_almost_done:
1881
1.68k
        assert(parser->flags & F_CHUNKED);
1882
1.68k
        assert(parser->content_length == 0);
1883
1.68k
        CROW_STRICT_CHECK(ch != cr);
1884
1.67k
        parser->state = s_chunk_data_done;
1885
1.67k
        CROW_CALLBACK_DATA(body);
1886
1.67k
        break;
1887
1888
1.67k
      case s_chunk_data_done:
1889
1.66k
        assert(parser->flags & F_CHUNKED);
1890
1.66k
        CROW_STRICT_CHECK(ch != lf);
1891
1.65k
        parser->nread = 0;
1892
1.65k
        nread = 0;
1893
1.65k
        parser->state = s_chunk_size_start;
1894
1.65k
        break;
1895
1896
0
      default:
1897
0
        assert(0 && "unhandled state");
1898
0
        CROW_SET_ERRNO(CHPE_INVALID_INTERNAL_STATE);
1899
0
        goto error;
1900
2.13M
    }
1901
2.13M
  }
1902
1903
  /* Run callbacks for any marks that we have leftover after we ran out of
1904
   * bytes. There should be at most one of these set, so it's OK to invoke
1905
   * them in series (unset marks will not result in callbacks).
1906
   *
1907
   * We use the NOADVANCE() variety of callbacks here because 'p' has already
1908
   * overflowed 'data' and this allows us to correct for the off-by-one that
1909
   * we'd otherwise have (since CROW_CALLBACK_DATA() is meant to be run with a 'p'
1910
   * value that's in-bounds).
1911
   */
1912
1913
4.60k
  assert(((header_field_mark ? 1 : 0) +
1914
3.61k
          (header_value_mark ? 1 : 0) +
1915
3.61k
          (url_mark ? 1 : 0)  +
1916
3.61k
          (body_mark ? 1 : 0)) <= 1);
1917
1918
3.61k
  CROW_CALLBACK_DATA_NOADVANCE(header_field);
1919
3.61k
  CROW_CALLBACK_DATA_NOADVANCE(header_value);
1920
3.61k
  CROW_CALLBACK_DATA_NOADVANCE(url);
1921
3.61k
  CROW_CALLBACK_DATA_NOADVANCE(body);
1922
1923
3.61k
  parser->nread = nread;
1924
3.61k
  return len;
1925
1926
941
error:
1927
941
  if (CROW_HTTP_PARSER_ERRNO(parser) == CHPE_OK) {
1928
0
    CROW_SET_ERRNO(CHPE_UNKNOWN);
1929
0
  }
1930
1931
941
  parser->nread = nread;
1932
941
  return (p - data);
1933
3.61k
}
1934
1935
inline void
1936
  http_parser_init(http_parser* parser)
1937
4.60k
{
1938
4.60k
  void *data = parser->data; /* preserve application data */
1939
4.60k
  memset(parser, 0, sizeof(*parser));
1940
4.60k
  parser->data = data;
1941
4.60k
  parser->state = s_start_req;
1942
4.60k
  parser->http_errno = CHPE_OK;
1943
4.60k
}
1944
1945
/* Return a string name of the given error */
1946
inline const char *
1947
0
http_errno_name(enum http_errno err) {
1948
0
/* Map errno values to strings for human-readable output */
1949
0
#define CROW_HTTP_STRERROR_GEN(n, s) { "CHPE_" #n, s },
1950
0
static struct {
1951
0
  const char *name;
1952
0
  const char *description;
1953
0
} http_strerror_tab[] = {
1954
0
  CROW_HTTP_ERRNO_MAP(CROW_HTTP_STRERROR_GEN)
1955
0
};
1956
0
#undef CROW_HTTP_STRERROR_GEN
1957
0
  assert(((size_t) err) < CROW_ARRAY_SIZE(http_strerror_tab));
1958
0
  return http_strerror_tab[err].name;
1959
0
}
1960
1961
/* Return a string description of the given error */
1962
inline const char *
1963
0
http_errno_description(enum http_errno err) {
1964
/* Map errno values to strings for human-readable output */
1965
0
#define CROW_HTTP_STRERROR_GEN(n, s) { "CHPE_" #n, s },
1966
0
static struct {
1967
0
  const char *name;
1968
0
  const char *description;
1969
0
} http_strerror_tab[] = {
1970
0
  CROW_HTTP_ERRNO_MAP(CROW_HTTP_STRERROR_GEN)
1971
0
};
1972
0
#undef CROW_HTTP_STRERROR_GEN
1973
0
  assert(((size_t) err) < CROW_ARRAY_SIZE(http_strerror_tab));
1974
0
  return http_strerror_tab[err].description;
1975
0
}
Unexecuted instantiation: crow::http_errno_description(crow::http_errno)
Unexecuted instantiation: crow::http_errno_description(crow::http_errno)
1976
1977
/* Checks if this is the final chunk of the body. */
1978
inline int
1979
0
http_body_is_final(const struct http_parser *parser) {
1980
0
    return parser->state == s_message_done;
1981
0
}
1982
1983
/* Change the maximum header size provided at compile time. */
1984
inline void
1985
0
http_parser_set_max_header_size(uint32_t size) {
1986
0
  max_header_size = size;
1987
0
}
1988
1989
#undef CROW_HTTP_ERRNO_MAP
1990
#undef CROW_SET_ERRNO
1991
#undef CROW_CALLBACK_NOTIFY_
1992
#undef CROW_CALLBACK_NOTIFY
1993
#undef CROW_CALLBACK_NOTIFY_NOADVANCE
1994
#undef CROW_CALLBACK_DATA_
1995
#undef CROW_CALLBACK_DATA
1996
#undef CROW_CALLBACK_DATA_NOADVANCE
1997
#undef CROW_MARK
1998
#undef CROW_PROXY_CONNECTION
1999
#undef CROW_CONNECTION
2000
#undef CROW_CONTENT_LENGTH
2001
#undef CROW_TRANSFER_ENCODING
2002
#undef CROW_UPGRADE
2003
#undef CROW_CHUNKED
2004
#undef CROW_KEEP_ALIVE
2005
#undef CROW_CLOSE
2006
#undef CROW_PARSING_HEADER
2007
#undef CROW_LOWER
2008
#undef CROW_IS_ALPHA
2009
#undef CROW_IS_NUM
2010
#undef CROW_IS_ALPHANUM
2011
//#undef CROW_IS_HEX
2012
#undef CROW_IS_MARK
2013
#undef CROW_IS_USERINFO_CHAR
2014
#undef CROW_TOKEN
2015
#undef CROW_IS_URL_CHAR
2016
//#undef CROW_IS_HOST_CHAR
2017
#undef CROW_STRICT_CHECK
2018
2019
}
2020
2021
// clang-format on