Coverage Report

Created: 2025-12-19 06:24

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
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