iodine 0.7.58 → 0.7.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ #ifndef H_HTTP1_PARSER_H
1
2
  /*
2
3
  Copyright: Boaz Segev, 2017-2020
3
4
  License: MIT
@@ -8,10 +9,8 @@ Feel free to copy, use and enjoy according to the license provided.
8
9
  /**
9
10
  This is a callback based parser. It parses the skeleton of the HTTP/1.x protocol
10
11
  and leaves most of the work (validation, error checks, etc') to the callbacks.
11
-
12
- This is an attempt to replace the existing HTTP/1.x parser with something easier
13
- to maintain and that could be used for an HTTP/1.x client as well.
14
12
  */
13
+ #define H_HTTP1_PARSER_H
15
14
  #include <stddef.h>
16
15
  #include <stdint.h>
17
16
  #include <stdio.h>
@@ -39,19 +38,19 @@ Parser Settings
39
38
  #endif
40
39
 
41
40
  #ifndef FIO_MEMCHAR
42
- /** Prefer a custom memchr implementation. Usually memchr is better. */
41
+ /** Prefer a custom memchr implementation. Usualy memchr is better. */
43
42
  #define FIO_MEMCHAR 0
44
43
  #endif
45
44
 
46
- #ifndef HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
47
- /** Preforms some optimizations assuming unaligned memory access is okay. */
48
- #define HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED 0
45
+ #ifndef ALLOW_UNALIGNED_MEMORY_ACCESS
46
+ /** Peforms some optimizations assuming unaligned memory access is okay. */
47
+ #define ALLOW_UNALIGNED_MEMORY_ACCESS 0
49
48
  #endif
50
49
 
51
- #ifndef HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER
52
- /** The RFC doesn't allow this, but this parser can manage... probably... */
53
- #define HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER 0
50
+ #ifndef HTTP1_PARSER_CONVERT_EOL2NUL
51
+ #define HTTP1_PARSER_CONVERT_EOL2NUL 0
54
52
  #endif
53
+
55
54
  /* *****************************************************************************
56
55
  Parser API
57
56
  ***************************************************************************** */
@@ -66,9 +65,9 @@ typedef struct http1_parser_s {
66
65
  } state;
67
66
  } http1_parser_s;
68
67
 
69
- #define HTTP1_PARSER_INIT \
70
- { \
71
- { 0 } \
68
+ #define HTTP1_PARSER_INIT \
69
+ { \
70
+ { 0 } \
72
71
  }
73
72
 
74
73
  /**
@@ -88,40 +87,45 @@ typedef struct http1_parser_s {
88
87
  */
89
88
  static size_t http1_parse(http1_parser_s *parser, void *buffer, size_t length);
90
89
 
91
- /** Returns true if the parsing stopped after a complete request / response. */
92
- inline static int http1_complete(http1_parser_s *parser);
93
-
94
90
  /* *****************************************************************************
95
91
  Required Callbacks (MUST be implemented by including file)
96
92
  ***************************************************************************** */
97
- // clang-format off
98
93
 
99
94
  /** called when a request was received. */
100
95
  static int http1_on_request(http1_parser_s *parser);
101
96
  /** called when a response was received. */
102
97
  static int http1_on_response(http1_parser_s *parser);
103
98
  /** called when a request method is parsed. */
104
- static int
105
- http1_on_method(http1_parser_s *parser, char *method, size_t method_len);
99
+ static int http1_on_method(http1_parser_s *parser,
100
+ char *method,
101
+ size_t method_len);
106
102
  /** called when a response status is parsed. the status_str is the string
107
103
  * without the prefixed numerical status indicator.*/
108
- static int http1_on_status(http1_parser_s *parser, size_t status, char *status_str, size_t len);
104
+ static int http1_on_status(http1_parser_s *parser,
105
+ size_t status,
106
+ char *status_str,
107
+ size_t len);
109
108
  /** called when a request path (excluding query) is parsed. */
110
109
  static int http1_on_path(http1_parser_s *parser, char *path, size_t path_len);
111
110
  /** called when a request path (excluding query) is parsed. */
112
- static int
113
- http1_on_query(http1_parser_s *parser, char *query, size_t query_len);
111
+ static int http1_on_query(http1_parser_s *parser,
112
+ char *query,
113
+ size_t query_len);
114
114
  /** called when a the HTTP/1.x version is parsed. */
115
115
  static int http1_on_version(http1_parser_s *parser, char *version, size_t len);
116
116
  /** called when a header is parsed. */
117
- static int http1_on_header(http1_parser_s *parser, char *name, size_t name_len, char *data, size_t data_len);
117
+ static int http1_on_header(http1_parser_s *parser,
118
+ char *name,
119
+ size_t name_len,
120
+ char *data,
121
+ size_t data_len);
118
122
  /** called when a body chunk is parsed. */
119
- static int
120
- http1_on_body_chunk(http1_parser_s *parser, char *data, size_t data_len);
123
+ static int http1_on_body_chunk(http1_parser_s *parser,
124
+ char *data,
125
+ size_t data_len);
121
126
  /** called when a protocol error occurred. */
122
127
  static int http1_on_error(http1_parser_s *parser);
123
128
 
124
- // clang-format on
125
129
  /* *****************************************************************************
126
130
 
127
131
 
@@ -161,10 +165,10 @@ static int http1_on_error(http1_parser_s *parser);
161
165
  ***************************************************************************** */
162
166
 
163
167
  #if HTTP_HEADERS_LOWERCASE
164
- #define HEADER_NAME_IS_EQ(var_name, const_name, len) \
168
+ #define HEADER_NAME_IS_EQ(var_name, const_name, len) \
165
169
  (!memcmp((var_name), (const_name), (len)))
166
170
  #else
167
- #define HEADER_NAME_IS_EQ(var_name, const_name, len) \
171
+ #define HEADER_NAME_IS_EQ(var_name, const_name, len) \
168
172
  (!strncasecmp((var_name), (const_name), (len)))
169
173
  #endif
170
174
 
@@ -177,9 +181,6 @@ static int http1_on_error(http1_parser_s *parser);
177
181
  #define HTTP1_P_FLAG_CHUNKED 64
178
182
  #define HTTP1_P_FLAG_RESPONSE 128
179
183
 
180
- #ifdef __cplusplus
181
- #define _Bool bool
182
- #endif
183
184
  /* *****************************************************************************
184
185
  Seeking for characters in a string
185
186
  ***************************************************************************** */
@@ -194,8 +195,7 @@ Seeking for characters in a string
194
195
  static int seek2ch(uint8_t **buffer,
195
196
  register uint8_t *const limit,
196
197
  const uint8_t c) {
197
- if (*buffer >= limit)
198
- return 0;
198
+ if (*buffer >= limit) return 0;
199
199
  if (**buffer == c) {
200
200
  return 1;
201
201
  }
@@ -209,8 +209,7 @@ static int seek2ch(uint8_t **buffer,
209
209
  {
210
210
  const uint8_t *alignment =
211
211
  (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
212
- if (*buffer < alignment)
213
- *buffer += 1; /* we already tested this char */
212
+ if (*buffer < alignment) *buffer += 1; /* we already tested this char */
214
213
  if (limit >= alignment) {
215
214
  while (*buffer < alignment) {
216
215
  if (**buffer == c) {
@@ -250,8 +249,7 @@ finish:
250
249
  /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
251
250
  inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
252
251
  /* This is library based alternative that is sometimes slower */
253
- if (*pos >= limit)
254
- return 0;
252
+ if (*pos >= limit) return 0;
255
253
  if (**pos == ch) {
256
254
  return 1;
257
255
  }
@@ -269,11 +267,16 @@ inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
269
267
  /* a helper that seeks the EOL, converts it to NUL and returns it's length */
270
268
  inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
271
269
  /* single char lookup using memchr might be better when target is far... */
272
- if (!seek2ch(pos, limit, '\n'))
273
- return 0;
270
+ if (!seek2ch(pos, limit, '\n')) return 0;
274
271
  if ((*pos)[-1] == '\r') {
272
+ #if HTTP1_PARSER_CONVERT_EOL2NUL
273
+ (*pos)[-1] = (*pos)[0] = 0;
274
+ #endif
275
275
  return 2;
276
276
  }
277
+ #if HTTP1_PARSER_CONVERT_EOL2NUL
278
+ (*pos)[0] = 0;
279
+ #endif
277
280
  return 1;
278
281
  }
279
282
 
@@ -282,8 +285,7 @@ Change a letter to lower case (latin only)
282
285
  ***************************************************************************** */
283
286
 
284
287
  static uint8_t http_tolower(uint8_t c) {
285
- if (((c >= 'A') & (c <= 'Z')))
286
- c |= 32;
288
+ if (c >= 'A' && c <= 'Z') c |= 32;
287
289
  return c;
288
290
  }
289
291
 
@@ -295,22 +297,17 @@ String to Number
295
297
  static long long http1_atol(const uint8_t *buf, const uint8_t **end) {
296
298
  register unsigned long long i = 0;
297
299
  uint8_t inv = 0;
298
- while (*buf == ' ' || *buf == '\t' || *buf == '\f')
299
- ++buf;
300
- while (*buf == '-' || *buf == '+')
301
- inv ^= (*(buf++) == '-');
300
+ while (*buf == ' ' || *buf == '\t' || *buf == '\f') ++buf;
301
+ while (*buf == '-' || *buf == '+') inv ^= (*(buf++) == '-');
302
302
  while (i <= ((((~0ULL) >> 1) / 10)) && *buf >= '0' && *buf <= '9') {
303
303
  i = i * 10;
304
304
  i += *buf - '0';
305
305
  ++buf;
306
306
  }
307
307
  /* test for overflow */
308
- if (i >= (~((~0ULL) >> 1)) || (*buf >= '0' && *buf <= '9'))
309
- i = (~0ULL >> 1);
310
- if (inv)
311
- i = 0ULL - i;
312
- if (end)
313
- *end = buf;
308
+ if (i >= (~((~0ULL) >> 1)) || (*buf >= '0' && *buf <= '9')) i = (~0ULL >> 1);
309
+ if (inv) i = 0ULL - i;
310
+ if (end) *end = buf;
314
311
  return i;
315
312
  }
316
313
 
@@ -324,12 +321,9 @@ static long long http1_atol16(const uint8_t *buf, const uint8_t **end) {
324
321
  ++buf;
325
322
  for (int limit_ = 0; (*buf == '-' || *buf == '+') && limit_ < 32; ++limit_)
326
323
  inv ^= (*(buf++) == '-');
327
- if (*buf == '0')
328
- ++buf;
329
- if ((*buf | 32) == 'x')
330
- ++buf;
331
- for (int limit_ = 0; (*buf == '0') && limit_ < 32; ++limit_)
332
- ++buf;
324
+ if (*buf == '0') ++buf;
325
+ if ((*buf | 32) == 'x') ++buf;
326
+ for (int limit_ = 0; (*buf == '0') && limit_ < 32; ++limit_) ++buf;
333
327
  while (!(i & (~((~(0ULL)) >> 4)))) {
334
328
  if (*buf >= '0' && *buf <= '9') {
335
329
  i <<= 4;
@@ -341,10 +335,8 @@ static long long http1_atol16(const uint8_t *buf, const uint8_t **end) {
341
335
  break;
342
336
  ++buf;
343
337
  }
344
- if (inv)
345
- i = 0ULL - i;
346
- if (end)
347
- *end = buf;
338
+ if (inv) i = 0ULL - i;
339
+ if (end) *end = buf;
348
340
  return i;
349
341
  }
350
342
 
@@ -357,13 +349,10 @@ inline static int http1_consume_response_line(http1_parser_s *parser,
357
349
  uint8_t *end) {
358
350
  parser->state.reserved |= HTTP1_P_FLAG_RESPONSE;
359
351
  uint8_t *tmp = start;
360
- if (!seek2ch(&tmp, end, ' '))
361
- return -1;
362
- if (http1_on_version(parser, (char *)start, tmp - start))
363
- return -1;
352
+ if (!seek2ch(&tmp, end, ' ')) return -1;
353
+ if (http1_on_version(parser, (char *)start, tmp - start)) return -1;
364
354
  tmp = start = tmp + 1;
365
- if (!seek2ch(&tmp, end, ' '))
366
- return -1;
355
+ if (!seek2ch(&tmp, end, ' ')) return -1;
367
356
  if (http1_on_status(parser,
368
357
  http1_atol(start, NULL),
369
358
  (char *)(tmp + 1),
@@ -378,10 +367,8 @@ inline static int http1_consume_request_line(http1_parser_s *parser,
378
367
  uint8_t *tmp = start;
379
368
  uint8_t *host_start = NULL;
380
369
  uint8_t *host_end = NULL;
381
- if (!seek2ch(&tmp, end, ' '))
382
- return -1;
383
- if (http1_on_method(parser, (char *)start, tmp - start))
384
- return -1;
370
+ if (!seek2ch(&tmp, end, ' ')) return -1;
371
+ if (http1_on_method(parser, (char *)start, tmp - start)) return -1;
385
372
  tmp = start = tmp + 1;
386
373
  if (start[0] == 'h' && start[1] == 't' && start[2] == 't' &&
387
374
  start[3] == 'p') {
@@ -394,12 +381,10 @@ inline static int http1_consume_request_line(http1_parser_s *parser,
394
381
  tmp = host_end = host_start = (start += 8);
395
382
  } else
396
383
  goto review_path;
397
- if (!seek2ch(&tmp, end, ' '))
398
- return -1;
384
+ if (!seek2ch(&tmp, end, ' ')) return -1;
399
385
  *tmp = ' ';
400
386
  if (!seek2ch(&host_end, tmp, '/')) {
401
- if (http1_on_path(parser, (char *)"/", 1))
402
- return -1;
387
+ if (http1_on_path(parser, (char *)"/", 1)) return -1;
403
388
  goto start_version;
404
389
  }
405
390
  host_end[0] = '/';
@@ -408,26 +393,21 @@ inline static int http1_consume_request_line(http1_parser_s *parser,
408
393
  review_path:
409
394
  tmp = start;
410
395
  if (seek2ch(&tmp, end, '?')) {
411
- if (http1_on_path(parser, (char *)start, tmp - start))
412
- return -1;
396
+ if (http1_on_path(parser, (char *)start, tmp - start)) return -1;
413
397
  tmp = start = tmp + 1;
414
- if (!seek2ch(&tmp, end, ' '))
415
- return -1;
398
+ if (!seek2ch(&tmp, end, ' ')) return -1;
416
399
  if (tmp - start > 0 && http1_on_query(parser, (char *)start, tmp - start))
417
400
  return -1;
418
401
  } else {
419
402
  tmp = start;
420
- if (!seek2ch(&tmp, end, ' '))
421
- return -1;
422
- if (http1_on_path(parser, (char *)start, tmp - start))
423
- return -1;
403
+ if (!seek2ch(&tmp, end, ' ')) return -1;
404
+ if (http1_on_path(parser, (char *)start, tmp - start)) return -1;
424
405
  }
425
406
  start_version:
426
407
  start = tmp + 1;
427
408
  if (start + 5 >= end) /* require "HTTP/" */
428
409
  return -1;
429
- if (http1_on_version(parser, (char *)start, end - start))
430
- return -1;
410
+ if (http1_on_version(parser, (char *)start, end - start)) return -1;
431
411
  /* */
432
412
  if (host_start && http1_on_header(parser,
433
413
  (char *)"host",
@@ -438,8 +418,8 @@ start_version:
438
418
  return 0;
439
419
  }
440
420
 
441
- #if !HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER
442
- inline /* inline the function if it's short enough */
421
+ #ifndef HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER
422
+ inline /* inline the function of it's short enough */
443
423
  #endif
444
424
  static int
445
425
  http1_consume_header_transfer_encoding(http1_parser_s *parser,
@@ -448,8 +428,7 @@ inline /* inline the function if it's short enough */
448
428
  uint8_t *start_value,
449
429
  uint8_t *end) {
450
430
  /* this removes the `chunked` marker and prepares to "unchunk" the data */
451
- while (start_value < end && (end[-1] == ',' || end[-1] == ' '))
452
- --end;
431
+ while (start_value < end && (end[-1] == ',' || end[-1] == ' ')) --end;
453
432
  if ((end - start_value) == 7 &&
454
433
  #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
455
434
  (((uint32_t *)(start_value))[0] | 0x20202020) ==
@@ -457,35 +436,34 @@ inline /* inline the function if it's short enough */
457
436
  (((uint32_t *)(start_value + 3))[0] | 0x20202020) ==
458
437
  ((uint32_t *)"nked")[0]
459
438
  #else
460
- ((start_value[0] | 32) == 'c' & (start_value[1] | 32) == 'h' &
461
- (start_value[2] | 32) == 'u' & (start_value[3] | 32) == 'n' &
462
- (start_value[4] | 32) == 'k' & (start_value[5] | 32) == 'e' &
439
+ ((start_value[0] | 32) == 'c' && (start_value[1] | 32) == 'h' &&
440
+ (start_value[2] | 32) == 'u' && (start_value[3] | 32) == 'n' &&
441
+ (start_value[4] | 32) == 'k' && (start_value[5] | 32) == 'e' &&
463
442
  (start_value[6] | 32) == 'd')
464
443
  #endif
465
444
  ) {
445
+ if (parser->state.content_length) return -1;
466
446
  /* simple case,only `chunked` as a value */
467
447
  parser->state.reserved |= HTTP1_P_FLAG_CHUNKED;
468
448
  parser->state.content_length = 0;
469
449
  start_value += 7;
470
450
  while (start_value < end && (*start_value == ',' || *start_value == ' '))
471
451
  ++start_value;
472
- if (!(end - start_value))
473
- return 0;
452
+ if (!(end - start_value)) return 0;
474
453
  } else if ((end - start_value) > 7 &&
475
- ((end[(-7 + 0)] | 32) == 'c' & (end[(-7 + 1)] | 32) == 'h' &
476
- (end[(-7 + 2)] | 32) == 'u' & (end[(-7 + 3)] | 32) == 'n' &
477
- (end[(-7 + 4)] | 32) == 'k' & (end[(-7 + 5)] | 32) == 'e' &
454
+ ((end[(-7 + 0)] | 32) == 'c' && (end[(-7 + 1)] | 32) == 'h' &&
455
+ (end[(-7 + 2)] | 32) == 'u' && (end[(-7 + 3)] | 32) == 'n' &&
456
+ (end[(-7 + 4)] | 32) == 'k' && (end[(-7 + 5)] | 32) == 'e' &&
478
457
  (end[(-7 + 6)] | 32) == 'd')) {
479
458
  /* simple case,`chunked` at the end of list (RFC required) */
459
+ if (parser->state.content_length) return -1;
480
460
  parser->state.reserved |= HTTP1_P_FLAG_CHUNKED;
481
461
  parser->state.content_length = 0;
482
462
  end -= 7;
483
- while (start_value < end && (end[-1] == ',' || end[-1] == ' '))
484
- --end;
485
- if (!(end - start_value))
486
- return 0;
463
+ while (start_value < end && (end[-1] == ',' || end[-1] == ' ')) --end;
464
+ if (!(end - start_value)) return 0;
487
465
  }
488
- #if HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER /* RFC disallows this */
466
+ #ifdef HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER /* RFC diisallows this */
489
467
  else if ((end - start_value) > 7 && (end - start_value) < 256) {
490
468
  /* complex case, `the, chunked, marker, is in the middle of list */
491
469
  uint8_t val[256];
@@ -506,6 +484,7 @@ inline /* inline the function if it's short enough */
506
484
  #endif
507
485
 
508
486
  ) {
487
+ if (parser->state.content_length) return -1;
509
488
  parser->state.reserved |= HTTP1_P_FLAG_CHUNKED;
510
489
  parser->state.content_length = 0;
511
490
  start_value += 7;
@@ -536,8 +515,7 @@ inline /* inline the function if it's short enough */
536
515
  val[val_len++] = *start_value;
537
516
  ++start_value;
538
517
  }
539
- if (val_len < 255)
540
- val[val_len] = 0;
518
+ val[val_len] = 0;
541
519
  }
542
520
  /* perform callback with `val` or indicate error */
543
521
  if (val_len == 256 || (val_len && http1_on_header(parser,
@@ -583,11 +561,9 @@ inline static int http1_consume_header_top(http1_parser_s *parser,
583
561
  }
584
562
  parser->state.reserved |= HTTP1_P_FLAG_CLENGTH;
585
563
  } else if ((end_name - start) == 17 && (end - start_value) >= 7 &&
586
- !parser->state.content_length &&
587
564
  #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
588
- ((*((uint64_t *)start) == *((uint64_t *)"transfer")) &
589
- ((*((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin")) &
590
- (start[16] == 'g')))
565
+ *((uint64_t *)start) == *((uint64_t *)"transfer") &&
566
+ *((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin")
591
567
  #else
592
568
  HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17)
593
569
  #endif
@@ -614,29 +590,30 @@ inline static int http1_consume_header_trailer(http1_parser_s *parser,
614
590
  uint8_t *end_name,
615
591
  uint8_t *start_value,
616
592
  uint8_t *end) {
593
+ if ((end_name - start) > 1 && start[0] == 'x') {
594
+ /* X- headers are allowed */
595
+ goto white_listed;
596
+ }
597
+
617
598
  /* white listed trailer names */
618
599
  const struct {
619
600
  char *name;
620
601
  long len;
621
- } http1_trailer_allowed_list[] = {
622
- {(char *)"server-timing", 13}, /* specific for client data... */
623
- {NULL, 0}, /* end of list marker */
602
+ } http1_trailer_white_list[] = {
603
+ {"server-timing", 13}, /* specific for client data... */
604
+ {NULL, 0}, /* end of list marker */
624
605
  };
625
- if ((end_name - start) > 1 && start[0] == 'x') {
626
- /* X- headers are allowed */
627
- goto allowed_list;
628
- }
629
- for (size_t i = 0; http1_trailer_allowed_list[i].name; ++i) {
630
- if ((long)(end_name - start) == http1_trailer_allowed_list[i].len &&
606
+ for (size_t i = 0; http1_trailer_white_list[i].name; ++i) {
607
+ if ((long)(end_name - start) == http1_trailer_white_list[i].len &&
631
608
  HEADER_NAME_IS_EQ((char *)start,
632
- http1_trailer_allowed_list[i].name,
633
- http1_trailer_allowed_list[i].len)) {
609
+ http1_trailer_white_list[i].name,
610
+ http1_trailer_white_list[i].len)) {
634
611
  /* header disallowed here */
635
- goto allowed_list;
612
+ goto white_listed;
636
613
  }
637
614
  }
638
615
  return 0;
639
- allowed_list:
616
+ white_listed:
640
617
  /* perform callback */
641
618
  if (http1_on_header(parser,
642
619
  (char *)start,
@@ -650,40 +627,21 @@ allowed_list:
650
627
  inline static int http1_consume_header(http1_parser_s *parser,
651
628
  uint8_t *start,
652
629
  uint8_t *end) {
653
- static const _Bool forbidden_name_chars[256] = {
654
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
655
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
656
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
657
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
658
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
659
- 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
660
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
661
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
662
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
663
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
664
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
665
630
  uint8_t *end_name = start;
666
631
  /* divide header name from data */
667
- if (!seek2ch(&end_name, end, ':'))
668
- return -1;
669
- if (end_name[-1] == ' ' || end_name[-1] == '\t')
670
- return -1;
671
- if (forbidden_name_chars[start[0] & 0xFF])
672
- return -1;
632
+ if (!seek2ch(&end_name, end, ':')) return -1;
633
+ if (end_name[-1] == ' ' || end_name[-1] == '\t') return -1;
673
634
  #if HTTP_HEADERS_LOWERCASE
674
- for (uint8_t *t = start; t < end_name; t++)
635
+ for (uint8_t *t = start; t < end_name; t++) {
675
636
  *t = http_tolower(*t);
637
+ }
676
638
  #endif
677
639
  uint8_t *start_value = end_name + 1;
678
640
  // clear away leading white space from value.
679
641
  while (start_value < end &&
680
- ((start_value[0] == ' ') | (start_value[0] == '\t'))) {
642
+ (start_value[0] == ' ' || start_value[0] == '\t')) {
681
643
  start_value++;
682
644
  };
683
- // clear away added white space from value.
684
- while (start_value < end && ((end[0] == ' ') | (end[0] == '\t'))) {
685
- end++;
686
- };
687
645
  return (parser->state.read ? http1_consume_header_trailer
688
646
  : http1_consume_header_top)(parser,
689
647
  start,
@@ -702,8 +660,7 @@ inline static int http1_consume_body_streamed(http1_parser_s *parser,
702
660
  uint8_t **start) {
703
661
  uint8_t *end = *start + parser->state.content_length - parser->state.read;
704
662
  uint8_t *const stop = ((uint8_t *)buffer) + length;
705
- if (end > stop)
706
- end = stop;
663
+ if (end > stop) end = stop;
707
664
  if (end > *start &&
708
665
  http1_on_body_chunk(parser, (char *)(*start), end - *start))
709
666
  return -1;
@@ -722,19 +679,17 @@ inline static int http1_consume_body_chunked(http1_parser_s *parser,
722
679
  uint8_t *end = *start;
723
680
  while (*start < stop) {
724
681
  if (parser->state.content_length == 0) {
725
- if (end + 2 >= stop)
726
- return 0;
682
+ if (end + 2 >= stop) return 0;
727
683
  if ((end[0] == '\r' && end[1] == '\n')) {
728
684
  /* remove tailing EOL that wasn't processed and retest */
729
685
  end += 2;
730
686
  *start = end;
731
- if (end + 2 >= stop)
732
- return 0;
687
+ if (end + 2 >= stop) return 0;
733
688
  }
734
689
  long long chunk_len = http1_atol16(end, (const uint8_t **)&end);
735
690
  if (end + 2 > stop) /* overflowed? */
736
691
  return 0;
737
- if ((end[0] != '\r') | (end[1] != '\n') | (chunk_len < 0))
692
+ if ((end[0] != '\r' || end[1] != '\n'))
738
693
  return -1; /* required EOL after content length */
739
694
  end += 2;
740
695
 
@@ -744,24 +699,20 @@ inline static int http1_consume_body_chunked(http1_parser_s *parser,
744
699
  /* all chunked data was parsed */
745
700
  /* update content-length */
746
701
  parser->state.content_length = parser->state.read;
747
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
702
+ #ifdef HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
748
703
  { /* add virtual header ... ? */
749
704
  char buf[512];
750
705
  size_t buf_len = 512;
751
706
  size_t tmp_len = parser->state.read;
752
707
  buf[--buf_len] = 0;
753
- if (tmp_len) {
754
- while (tmp_len) {
755
- size_t mod = tmp_len / 10;
756
- buf[--buf_len] = '0' + (tmp_len - (mod * 10));
757
- tmp_len = mod;
758
- }
759
- } else {
760
- buf[--buf_len] = '0';
708
+ while (tmp_len) {
709
+ size_t mod = tmp_len / 10;
710
+ buf[--buf_len] = '0' + (tmp_len - (mod * 10));
711
+ tmp_len = mod;
761
712
  }
762
713
  if (!(parser->state.reserved & HTTP1_P_FLAG_CLENGTH) &&
763
714
  http1_on_header(parser,
764
- (char *)"content-length",
715
+ "content-length",
765
716
  14,
766
717
  (char *)buf + buf_len,
767
718
  511 - buf_len)) {
@@ -769,7 +720,7 @@ inline static int http1_consume_body_chunked(http1_parser_s *parser,
769
720
  }
770
721
  }
771
722
  #endif
772
- /* consume trailing EOL */
723
+ /* FIXME: consume trailing EOL */
773
724
  if (*start + 2 <= stop && (start[0][0] == '\r' || start[0][0] == '\n'))
774
725
  *start += 1 + (start[0][1] == '\r' || start[0][1] == '\n');
775
726
  else {
@@ -784,8 +735,7 @@ inline static int http1_consume_body_chunked(http1_parser_s *parser,
784
735
  }
785
736
  }
786
737
  end = *start + (0 - parser->state.content_length);
787
- if (end > stop)
788
- end = stop;
738
+ if (end > stop) end = stop;
789
739
  if (end > *start &&
790
740
  http1_on_body_chunk(parser, (char *)(*start), end - *start)) {
791
741
  return -1;
@@ -842,8 +792,7 @@ HTTP/1.1 parsre function
842
792
  * allowing the user to adjust or refresh the state of the data.
843
793
  */
844
794
  static size_t http1_parse(http1_parser_s *parser, void *buffer, size_t length) {
845
- if (!length)
846
- return 0;
795
+ if (!length) return 0;
847
796
  HTTP1_ASSERT(parser && buffer);
848
797
  parser->state.next = NULL;
849
798
  uint8_t *start = (uint8_t *)buffer;
@@ -854,67 +803,60 @@ static size_t http1_parse(http1_parser_s *parser, void *buffer, size_t length) {
854
803
 
855
804
  re_eval:
856
805
  switch ((parser->state.reserved & 7)) {
857
-
858
- case 0: /* request / response line */
859
- /* clear out any leading white space */
860
- while ((start < stop) &&
861
- (*start == '\r' || *start == '\n' || *start == ' ' || *start == 0)) {
862
- ++start;
863
- }
864
- end = start;
865
- /* make sure the whole line is available*/
866
- if (!(eol_len = seek2eol(&end, stop)))
867
- return HTTP1_CONSUMED;
868
-
869
- if (start[0] == 'H' && start[1] == 'T' && start[2] == 'T' &&
870
- start[3] == 'P') {
871
- /* HTTP response */
872
- if (http1_consume_response_line(parser, start, end - eol_len + 1))
873
- goto error;
874
- } else if (http_tolower(start[0]) >= 'a' && http_tolower(start[0]) <= 'z') {
875
- /* HTTP request */
876
- if (http1_consume_request_line(parser, start, end - eol_len + 1))
877
- goto error;
878
- } else
879
- goto error;
880
- end = start = end + 1;
881
- parser->state.reserved |= HTTP1_P_FLAG_STATUS_LINE;
882
-
883
- /* fallthrough */
884
- case 1: /* headers */
885
- do {
886
- if (start >= stop)
887
- return HTTP1_CONSUMED; /* buffer ended on header line */
888
- if ((*start == '\n') |
889
- (start + 1 < stop && ((start[0] == '\r') & (start[1] == '\n')))) {
890
- goto finished_headers; /* empty line, end of headers */
806
+ case 0: /* request / response line */
807
+ /* clear out any leading white space */
808
+ while ((start < stop) && (*start == '\r' || *start == '\n' ||
809
+ *start == ' ' || *start == 0)) {
810
+ ++start;
891
811
  }
892
812
  end = start;
893
- if (!(eol_len = seek2eol(&end, stop)))
894
- return HTTP1_CONSUMED;
895
- if (http1_consume_header(parser, start, end - eol_len + 1))
813
+ /* make sure the whole line is available*/
814
+ if (!(eol_len = seek2eol(&end, stop))) return HTTP1_CONSUMED;
815
+
816
+ if (start[0] == 'H' && start[1] == 'T' && start[2] == 'T' &&
817
+ start[3] == 'P') {
818
+ /* HTTP response */
819
+ if (http1_consume_response_line(parser, start, end - eol_len + 1))
820
+ goto error;
821
+ } else if (http_tolower(start[0]) >= 'a' &&
822
+ http_tolower(start[0]) <= 'z') {
823
+ /* HTTP request */
824
+ if (http1_consume_request_line(parser, start, end - eol_len + 1))
825
+ goto error;
826
+ } else
896
827
  goto error;
897
828
  end = start = end + 1;
898
- } while ((parser->state.reserved & HTTP1_P_FLAG_HEADER_COMPLETE) == 0);
899
- finished_headers:
900
- ++start;
901
- if (*start == '\n')
829
+ parser->state.reserved |= HTTP1_P_FLAG_STATUS_LINE;
830
+
831
+ /* fallthrough */
832
+ case 1: /* headers */
833
+ do {
834
+ if (start >= stop)
835
+ return HTTP1_CONSUMED; /* buffer ended on header line */
836
+ if (*start == '\r' || *start == '\n') {
837
+ goto finished_headers; /* empty line, end of headers */
838
+ }
839
+ end = start;
840
+ if (!(eol_len = seek2eol(&end, stop))) return HTTP1_CONSUMED;
841
+ if (http1_consume_header(parser, start, end - eol_len + 1)) goto error;
842
+ end = start = end + 1;
843
+ } while ((parser->state.reserved & HTTP1_P_FLAG_HEADER_COMPLETE) == 0);
844
+ finished_headers:
902
845
  ++start;
903
- end = start;
904
- parser->state.reserved |= HTTP1_P_FLAG_HEADER_COMPLETE;
905
- /* fall through */
906
- case (HTTP1_P_FLAG_HEADER_COMPLETE | HTTP1_P_FLAG_STATUS_LINE):
907
- /* request body */
908
- {
909
- int t3 = http1_consume_body(parser, buffer, length, &start);
910
- switch (t3) {
911
- case -1:
912
- goto error;
913
- case -2:
914
- goto re_eval;
846
+ if (*start == '\n') ++start;
847
+ end = start;
848
+ parser->state.reserved |= HTTP1_P_FLAG_HEADER_COMPLETE;
849
+ /* fallthrough */
850
+ case (HTTP1_P_FLAG_HEADER_COMPLETE | HTTP1_P_FLAG_STATUS_LINE):
851
+ /* request body */
852
+ {
853
+ int t3 = http1_consume_body(parser, buffer, length, &start);
854
+ switch (t3) {
855
+ case -1: goto error;
856
+ case -2: goto re_eval;
857
+ }
858
+ break;
915
859
  }
916
- break;
917
- }
918
860
  }
919
861
  /* are we done ? */
920
862
  if (parser->state.reserved & HTTP1_P_FLAG_COMPLETE) {
@@ -933,903 +875,4 @@ error:
933
875
  #undef HTTP1_CONSUMED
934
876
  }
935
877
 
936
- /** Returns true if the parsing stopped after a complete request / response. */
937
- inline static int http1_complete(http1_parser_s *parser) {
938
- return !parser->state.reserved;
939
- }
940
-
941
- /* *****************************************************************************
942
-
943
-
944
-
945
-
946
- HTTP/1.1 TESTING
947
-
948
-
949
-
950
-
951
- ***************************************************************************** */
952
- #ifdef HTTP1_TEST_PARSER
953
- #include "signal.h"
954
-
955
- #define HTTP1_TEST_ASSERT(cond, ...) \
956
- if (!(cond)) { \
957
- fprintf(stderr, __VA_ARGS__); \
958
- fprintf(stderr, "\n"); \
959
- kill(0, SIGINT); \
960
- exit(-1); \
961
- }
962
-
963
- static size_t http1_test_pos;
964
- static char http1_test_temp_buf[8092];
965
- static size_t http1_test_temp_buf_pos;
966
- static struct {
967
- char *test_name;
968
- char *request[16];
969
- struct {
970
- char body[1024];
971
- size_t body_len;
972
- const char *method;
973
- ssize_t status;
974
- const char *path;
975
- const char *query;
976
- const char *version;
977
- struct http1_test_header_s {
978
- const char *name;
979
- size_t name_len;
980
- const char *val;
981
- size_t val_len;
982
- } headers[12];
983
- } result, expect;
984
- } http1_test_data[] = {
985
- {
986
- .test_name = "simple empty request",
987
- .request = {"GET / HTTP/1.1\r\nHost:localhost\r\n\r\n"},
988
- .expect =
989
- {
990
- .body = "",
991
- .body_len = 0,
992
- .method = "GET",
993
- .path = "/",
994
- .query = NULL,
995
- .version = "HTTP/1.1",
996
- .headers =
997
- {
998
- {.name = "host",
999
- .name_len = 4,
1000
- .val = "localhost",
1001
- .val_len = 9},
1002
- },
1003
- },
1004
- },
1005
- {
1006
- .test_name = "space before header data",
1007
- .request = {"POST /my/path HTTP/1.2\r\nHost: localhost\r\n\r\n"},
1008
- .expect =
1009
- {
1010
- .body = "",
1011
- .body_len = 0,
1012
- .method = "POST",
1013
- .path = "/my/path",
1014
- .query = NULL,
1015
- .version = "HTTP/1.2",
1016
- .headers =
1017
- {
1018
- {.name = "host",
1019
- .name_len = 4,
1020
- .val = "localhost",
1021
- .val_len = 9},
1022
- },
1023
- },
1024
- },
1025
- {
1026
- .test_name = "simple request, fragmented header (in new line)",
1027
- .request = {"GET / HTTP/1.1\r\n", "Host:localhost\r\n\r\n"},
1028
- .expect =
1029
- {
1030
- .body = "",
1031
- .body_len = 0,
1032
- .method = "GET",
1033
- .path = "/",
1034
- .query = NULL,
1035
- .version = "HTTP/1.1",
1036
- .headers =
1037
- {
1038
- {.name = "host",
1039
- .name_len = 4,
1040
- .val = "localhost",
1041
- .val_len = 9},
1042
- },
1043
- },
1044
- },
1045
- {
1046
- .test_name = "request with query",
1047
- .request = {"METHOD /path?q=query HTTP/1.3\r\nHost:localhost\r\n\r\n"},
1048
- .expect =
1049
- {
1050
- .body = "",
1051
- .body_len = 0,
1052
- .method = "METHOD",
1053
- .path = "/path",
1054
- .query = "q=query",
1055
- .version = "HTTP/1.3",
1056
- .headers =
1057
- {
1058
- {.name = "host",
1059
- .name_len = 4,
1060
- .val = "localhost",
1061
- .val_len = 9},
1062
- },
1063
- },
1064
- },
1065
- {
1066
- .test_name = "mid-fragmented header",
1067
- .request = {"GET / HTTP/1.1\r\nHost: loca", "lhost\r\n\r\n"},
1068
- .expect =
1069
- {
1070
- .body = "",
1071
- .body_len = 0,
1072
- .method = "GET",
1073
- .path = "/",
1074
- .query = NULL,
1075
- .version = "HTTP/1.1",
1076
- .headers =
1077
- {
1078
- {.name = "host",
1079
- .name_len = 4,
1080
- .val = "localhost",
1081
- .val_len = 9},
1082
- },
1083
- },
1084
- },
1085
- {
1086
- .test_name = "simple with body",
1087
- .request = {"GET / HTTP/1.1\r\nHost:with body\r\n"
1088
- "Content-lEnGth: 5\r\n\r\nHello"},
1089
- .expect =
1090
- {
1091
- .body = "Hello",
1092
- .body_len = 5,
1093
- .method = "GET",
1094
- .path = "/",
1095
- .query = NULL,
1096
- .version = "HTTP/1.1",
1097
- .headers =
1098
- {
1099
- {
1100
- .name = "host",
1101
- .name_len = 4,
1102
- .val = "with body",
1103
- .val_len = 9,
1104
- },
1105
- {
1106
- .name = "content-length",
1107
- .name_len = 14,
1108
- .val = "5",
1109
- .val_len = 1,
1110
- },
1111
- },
1112
- },
1113
- },
1114
- {
1115
- .test_name = "fragmented body",
1116
- .request = {"GET / HTTP/1.1\r\nHost:with body\r\n",
1117
- "Content-lEnGth: 5\r\n\r\nHe",
1118
- "llo"},
1119
- .expect =
1120
- {
1121
- .body = "Hello",
1122
- .body_len = 5,
1123
- .method = "GET",
1124
- .path = "/",
1125
- .query = NULL,
1126
- .version = "HTTP/1.1",
1127
- .headers =
1128
- {
1129
- {
1130
- .name = "host",
1131
- .name_len = 4,
1132
- .val = "with body",
1133
- .val_len = 9,
1134
- },
1135
- {
1136
- .name = "content-length",
1137
- .name_len = 14,
1138
- .val = "5",
1139
- .val_len = 1,
1140
- },
1141
- },
1142
- },
1143
- },
1144
- {
1145
- .test_name = "fragmented body 2 (cuts EOL)",
1146
- .request = {"POST / HTTP/1.1\r\nHost:with body\r\n",
1147
- "Content-lEnGth: 5\r\n",
1148
- "\r\n",
1149
- "He",
1150
- "llo"},
1151
- .expect =
1152
- {
1153
- .body = "Hello",
1154
- .body_len = 5,
1155
- .method = "POST",
1156
- .path = "/",
1157
- .query = NULL,
1158
- .version = "HTTP/1.1",
1159
- .headers =
1160
- {
1161
- {
1162
- .name = "host",
1163
- .name_len = 4,
1164
- .val = "with body",
1165
- .val_len = 9,
1166
- },
1167
- {
1168
- .name = "content-length",
1169
- .name_len = 14,
1170
- .val = "5",
1171
- .val_len = 1,
1172
- },
1173
- },
1174
- },
1175
- },
1176
- {
1177
- .test_name = "chunked body (simple)",
1178
- .request = {"POST / HTTP/1.1\r\nHost:with body\r\n"
1179
- "Transfer-Encoding: chunked\r\n"
1180
- "\r\n"
1181
- "5\r\n"
1182
- "Hello"
1183
- "\r\n0\r\n\r\n"},
1184
- .expect =
1185
- {
1186
- .body = "Hello",
1187
- .body_len = 5,
1188
- .method = "POST",
1189
- .path = "/",
1190
- .query = NULL,
1191
- .version = "HTTP/1.1",
1192
- .headers =
1193
- {
1194
- {
1195
- .name = "host",
1196
- .name_len = 4,
1197
- .val = "with body",
1198
- .val_len = 9,
1199
- },
1200
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1201
- {
1202
- .name = "content-length",
1203
- .name_len = 14,
1204
- .val = "5",
1205
- .val_len = 1,
1206
- },
1207
- #endif
1208
- },
1209
- },
1210
- },
1211
- {
1212
- .test_name = "chunked body (empty)",
1213
- .request = {"POST / HTTP/1.1\r\nHost:with body\r\n"
1214
- "Transfer-Encoding: chunked\r\n"
1215
- "\r\n0\r\n\r\n"},
1216
- .expect =
1217
- {
1218
- .body = "",
1219
- .body_len = 0,
1220
- .method = "POST",
1221
- .path = "/",
1222
- .query = NULL,
1223
- .version = "HTTP/1.1",
1224
- .headers =
1225
- {
1226
- {
1227
- .name = "host",
1228
- .name_len = 4,
1229
- .val = "with body",
1230
- .val_len = 9,
1231
- },
1232
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1233
- {
1234
- .name = "content-length",
1235
- .name_len = 14,
1236
- .val = "0",
1237
- .val_len = 1,
1238
- },
1239
- #endif
1240
- },
1241
- },
1242
- },
1243
- {
1244
- .test_name = "chunked body (end of list)",
1245
- .request = {"POST / HTTP/1.1\r\nHost:with body\r\n"
1246
- "Transfer-Encoding: gzip, foo, chunked\r\n"
1247
- "\r\n"
1248
- "5\r\n"
1249
- "Hello"
1250
- "\r\n0\r\n\r\n"},
1251
- .expect =
1252
- {
1253
- .body = "Hello",
1254
- .body_len = 5,
1255
- .method = "POST",
1256
- .path = "/",
1257
- .query = NULL,
1258
- .version = "HTTP/1.1",
1259
- .headers =
1260
- {
1261
- {
1262
- .name = "host",
1263
- .name_len = 4,
1264
- .val = "with body",
1265
- .val_len = 9,
1266
- },
1267
- {
1268
- .name = "transfer-encoding",
1269
- .name_len = 17,
1270
- .val = "gzip, foo",
1271
- .val_len = 9,
1272
- },
1273
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1274
- {
1275
- .name = "content-length",
1276
- .name_len = 14,
1277
- .val = "5",
1278
- .val_len = 1,
1279
- },
1280
- #endif
1281
- },
1282
- },
1283
- },
1284
- #if HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER
1285
- {
1286
- .test_name = "chunked body (middle of list - RFC violation)",
1287
- .request = {"POST / HTTP/1.1\r\nHost:with body\r\n"
1288
- "Transfer-Encoding: gzip, chunked, foo\r\n"
1289
- "\r\n",
1290
- "5\r\n"
1291
- "Hello"
1292
- "\r\n0\r\n\r\n"},
1293
- .expect =
1294
- {
1295
- .body = "Hello",
1296
- .body_len = 5,
1297
- .method = "POST",
1298
- .path = "/",
1299
- .query = NULL,
1300
- .version = "HTTP/1.1",
1301
- .headers =
1302
- {
1303
- {
1304
- .name = "host",
1305
- .name_len = 4,
1306
- .val = "with body",
1307
- .val_len = 9,
1308
- },
1309
- {
1310
- .name = "transfer-encoding",
1311
- .name_len = 17,
1312
- .val = "gzip,foo",
1313
- .val_len = 8,
1314
- },
1315
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1316
- {
1317
- .name = "content-length",
1318
- .name_len = 14,
1319
- .val = "5",
1320
- .val_len = 1,
1321
- },
1322
- #endif
1323
- },
1324
- },
1325
- },
1326
- #endif /* HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER */
1327
- {
1328
- .test_name = "chunked body (fragmented)",
1329
- .request =
1330
- {
1331
- "POST / HTTP/1.1\r\nHost:with body\r\n",
1332
- "Transfer-Encoding: chunked\r\n",
1333
- "\r\n"
1334
- "5\r\n",
1335
- "He",
1336
- "llo",
1337
- "\r\n0\r\n\r\n",
1338
- },
1339
- .expect =
1340
- {
1341
- .body = "Hello",
1342
- .body_len = 5,
1343
- .method = "POST",
1344
- .path = "/",
1345
- .query = NULL,
1346
- .version = "HTTP/1.1",
1347
- .headers =
1348
- {
1349
- {
1350
- .name = "host",
1351
- .name_len = 4,
1352
- .val = "with body",
1353
- .val_len = 9,
1354
- },
1355
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1356
- {
1357
- .name = "content-length",
1358
- .name_len = 14,
1359
- .val = "5",
1360
- .val_len = 1,
1361
- },
1362
- #endif
1363
- },
1364
- },
1365
- },
1366
- {
1367
- .test_name = "chunked body (fragmented + multi-message)",
1368
- .request =
1369
- {
1370
- "POST / HTTP/1.1\r\nHost:with body\r\n",
1371
- "Transfer-Encoding: chunked\r\n",
1372
- "\r\n"
1373
- "2\r\n",
1374
- "He",
1375
- "3\r\nl",
1376
- "lo",
1377
- "\r\n0\r\n\r\n",
1378
- },
1379
- .expect =
1380
- {
1381
- .body = "Hello",
1382
- .body_len = 5,
1383
- .method = "POST",
1384
- .path = "/",
1385
- .query = NULL,
1386
- .version = "HTTP/1.1",
1387
- .headers =
1388
- {
1389
- {
1390
- .name = "host",
1391
- .name_len = 4,
1392
- .val = "with body",
1393
- .val_len = 9,
1394
- },
1395
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1396
- {
1397
- .name = "content-length",
1398
- .name_len = 14,
1399
- .val = "5",
1400
- .val_len = 1,
1401
- },
1402
- #endif
1403
- },
1404
- },
1405
- },
1406
- {
1407
- .test_name = "chunked body (fragmented + broken-multi-message)",
1408
- .request =
1409
- {
1410
- "POST / HTTP/1.1\r\nHost:with body\r\n",
1411
- "Transfer-Encoding: chunked\r\n",
1412
- "\r\n",
1413
- "2\r\n",
1414
- "H",
1415
- "e",
1416
- "3\r\nl",
1417
- "l"
1418
- "o",
1419
- "\r\n0\r\n\r\n",
1420
- },
1421
- .expect =
1422
- {
1423
- .body = "Hello",
1424
- .body_len = 5,
1425
- .method = "POST",
1426
- .path = "/",
1427
- .query = NULL,
1428
- .version = "HTTP/1.1",
1429
- .headers =
1430
- {
1431
- {
1432
- .name = "host",
1433
- .name_len = 4,
1434
- .val = "with body",
1435
- .val_len = 9,
1436
- },
1437
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1438
- {
1439
- .name = "content-length",
1440
- .name_len = 14,
1441
- .val = "5",
1442
- .val_len = 1,
1443
- },
1444
- #endif
1445
- },
1446
- },
1447
- },
1448
- {
1449
- .test_name = "chunked body (...longer + trailer + empty value...)",
1450
- .request =
1451
- {
1452
- "POST / HTTP/1.1\r\nHost:with body\r\n",
1453
- "Transfer-Encoding: chunked\r\n",
1454
- "\r\n",
1455
- "4\r\n",
1456
- "Wiki\r\n",
1457
- "5\r\n",
1458
- "pedia\r\n",
1459
- "E\r\n",
1460
- " in\r\n",
1461
- "\r\n",
1462
- "chunks.\r\n",
1463
- "0\r\n",
1464
- "X-Foo: trailer\r\n",
1465
- "sErvEr-tiMing: \r\n",
1466
- "\r\n",
1467
- },
1468
- .expect =
1469
- {
1470
- .body = "Wikipedia in\r\n\r\nchunks.",
1471
- .body_len = 23,
1472
- .method = "POST",
1473
- .path = "/",
1474
- .query = NULL,
1475
- .version = "HTTP/1.1",
1476
- .headers =
1477
- {
1478
- {
1479
- .name = "host",
1480
- .name_len = 4,
1481
- .val = "with body",
1482
- .val_len = 9,
1483
- },
1484
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1485
- {
1486
- .name = "content-length",
1487
- .name_len = 14,
1488
- .val = "23",
1489
- .val_len = 2,
1490
- },
1491
- #endif
1492
- {
1493
- .name = "x-foo",
1494
- .name_len = 5,
1495
- .val = "trailer",
1496
- .val_len = 7,
1497
- },
1498
- {
1499
- .name = "server-timing",
1500
- .name_len = 13,
1501
- .val = "",
1502
- .val_len = 0,
1503
- },
1504
- },
1505
- },
1506
- },
1507
- {
1508
- .test_name = "chunked body (fragmented + surprize trailer)",
1509
- .request =
1510
- {
1511
- "POST / HTTP/1.1\r\nHost:with body\r\n",
1512
- "Transfer-Encoding: chunked\r\n",
1513
- "\r\n"
1514
- "5\r\n",
1515
- "He",
1516
- "llo",
1517
- "\r\n0\r\nX-Foo: trailer\r\n\r\n",
1518
- },
1519
- .expect =
1520
- {
1521
- .body = "Hello",
1522
- .body_len = 5,
1523
- .method = "POST",
1524
- .path = "/",
1525
- .query = NULL,
1526
- .version = "HTTP/1.1",
1527
- .headers =
1528
- {
1529
- {
1530
- .name = "host",
1531
- .name_len = 4,
1532
- .val = "with body",
1533
- .val_len = 9,
1534
- },
1535
- #if HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
1536
- {
1537
- .name = "content-length",
1538
- .name_len = 14,
1539
- .val = "5",
1540
- .val_len = 1,
1541
- },
1542
- #endif
1543
- {
1544
- .name = "x-foo",
1545
- .name_len = 5,
1546
- .val = "trailer",
1547
- .val_len = 7,
1548
- },
1549
- },
1550
- },
1551
- },
1552
- /* stop marker */
1553
- {
1554
- .request = {NULL},
1555
- },
1556
- };
1557
-
1558
- /** called when a request was received. */
1559
- static int http1_on_request(http1_parser_s *parser) {
1560
- (void)parser;
1561
- return 0;
1562
- }
1563
- /** called when a response was received. */
1564
- static int http1_on_response(http1_parser_s *parser) {
1565
- (void)parser;
1566
- return 0;
1567
- }
1568
- /** called when a request method is parsed. */
1569
- static int http1_on_method(http1_parser_s *parser,
1570
- char *method,
1571
- size_t method_len) {
1572
- (void)parser;
1573
- http1_test_data[http1_test_pos].result.method = method;
1574
- HTTP1_TEST_ASSERT(method_len ==
1575
- strlen(http1_test_data[http1_test_pos].expect.method),
1576
- "method_len test error for: %s",
1577
- http1_test_data[http1_test_pos].test_name);
1578
- return 0;
1579
- }
1580
- /** called when a response status is parsed. the status_str is the string
1581
- * without the prefixed numerical status indicator.*/
1582
- static int http1_on_status(http1_parser_s *parser,
1583
- size_t status,
1584
- char *status_str,
1585
- size_t len) {
1586
- (void)parser;
1587
- http1_test_data[http1_test_pos].result.status = status;
1588
- http1_test_data[http1_test_pos].result.method = status_str;
1589
- HTTP1_TEST_ASSERT(len ==
1590
- strlen(http1_test_data[http1_test_pos].expect.method),
1591
- "status length test error for: %s",
1592
- http1_test_data[http1_test_pos].test_name);
1593
- return 0;
1594
- }
1595
- /** called when a request path (excluding query) is parsed. */
1596
- static int http1_on_path(http1_parser_s *parser, char *path, size_t len) {
1597
- (void)parser;
1598
- http1_test_data[http1_test_pos].result.path = path;
1599
- HTTP1_TEST_ASSERT(len == strlen(http1_test_data[http1_test_pos].expect.path),
1600
- "path length test error for: %s",
1601
- http1_test_data[http1_test_pos].test_name);
1602
- return 0;
1603
- }
1604
- /** called when a request path (excluding query) is parsed. */
1605
- static int http1_on_query(http1_parser_s *parser, char *query, size_t len) {
1606
- (void)parser;
1607
- http1_test_data[http1_test_pos].result.query = query;
1608
- HTTP1_TEST_ASSERT(len == strlen(http1_test_data[http1_test_pos].expect.query),
1609
- "query length test error for: %s",
1610
- http1_test_data[http1_test_pos].test_name);
1611
- return 0;
1612
- }
1613
- /** called when a the HTTP/1.x version is parsed. */
1614
- static int http1_on_version(http1_parser_s *parser, char *version, size_t len) {
1615
- (void)parser;
1616
- http1_test_data[http1_test_pos].result.version = version;
1617
- HTTP1_TEST_ASSERT(len ==
1618
- strlen(http1_test_data[http1_test_pos].expect.version),
1619
- "version length test error for: %s",
1620
- http1_test_data[http1_test_pos].test_name);
1621
- return 0;
1622
- }
1623
- /** called when a header is parsed. */
1624
- static int http1_on_header(http1_parser_s *parser,
1625
- char *name,
1626
- size_t name_len,
1627
- char *val,
1628
- size_t val_len) {
1629
- (void)parser;
1630
- size_t pos = 0;
1631
- while (pos < 12 && http1_test_data[http1_test_pos].result.headers[pos].name)
1632
- ++pos;
1633
- HTTP1_TEST_ASSERT(pos < 12,
1634
- "header result overflow for: %s",
1635
- http1_test_data[http1_test_pos].test_name);
1636
- memcpy(http1_test_temp_buf + http1_test_temp_buf_pos, name, name_len);
1637
- name = http1_test_temp_buf + http1_test_temp_buf_pos;
1638
- http1_test_temp_buf_pos += name_len;
1639
- http1_test_temp_buf[http1_test_temp_buf_pos++] = 0;
1640
- memcpy(http1_test_temp_buf + http1_test_temp_buf_pos, val, val_len);
1641
- val = http1_test_temp_buf + http1_test_temp_buf_pos;
1642
- http1_test_temp_buf_pos += val_len;
1643
- http1_test_temp_buf[http1_test_temp_buf_pos++] = 0;
1644
- http1_test_data[http1_test_pos].result.headers[pos].name = name;
1645
- http1_test_data[http1_test_pos].result.headers[pos].name_len = name_len;
1646
- http1_test_data[http1_test_pos].result.headers[pos].val = val;
1647
- http1_test_data[http1_test_pos].result.headers[pos].val_len = val_len;
1648
- return 0;
1649
- }
1650
- /** called when a body chunk is parsed. */
1651
- static int http1_on_body_chunk(http1_parser_s *parser,
1652
- char *data,
1653
- size_t data_len) {
1654
- (void)parser;
1655
- http1_test_data[http1_test_pos]
1656
- .result.body[http1_test_data[http1_test_pos].result.body_len] = 0;
1657
- HTTP1_TEST_ASSERT(data_len +
1658
- http1_test_data[http1_test_pos].result.body_len <=
1659
- http1_test_data[http1_test_pos].expect.body_len,
1660
- "body overflow for: %s"
1661
- "\r\n Expect:\n%s\nGot:\n%s%s\n",
1662
- http1_test_data[http1_test_pos].test_name,
1663
- http1_test_data[http1_test_pos].expect.body,
1664
- http1_test_data[http1_test_pos].result.body,
1665
- data);
1666
- memcpy(http1_test_data[http1_test_pos].result.body +
1667
- http1_test_data[http1_test_pos].result.body_len,
1668
- data,
1669
- data_len);
1670
- http1_test_data[http1_test_pos].result.body_len += data_len;
1671
- http1_test_data[http1_test_pos]
1672
- .result.body[http1_test_data[http1_test_pos].result.body_len] = 0;
1673
- return 0;
1674
- }
1675
-
1676
- /** called when a protocol error occurred. */
1677
- static int http1_on_error(http1_parser_s *parser) {
1678
- (void)parser;
1679
- http1_test_data[http1_test_pos].result.status = -1;
1680
- return 0;
1681
- }
1682
-
1683
- #define HTTP1_TEST_STRING_FIELD(field, i) \
1684
- HTTP1_TEST_ASSERT((!http1_test_data[i].expect.field && \
1685
- !http1_test_data[i].result.field) || \
1686
- http1_test_data[i].expect.field && \
1687
- http1_test_data[i].result.field && \
1688
- !memcmp(http1_test_data[i].expect.field, \
1689
- http1_test_data[i].result.field, \
1690
- strlen(http1_test_data[i].expect.field)), \
1691
- "string field error for %s - " #field " \n%s\n%s", \
1692
- http1_test_data[i].test_name, \
1693
- http1_test_data[i].expect.field, \
1694
- http1_test_data[i].result.field);
1695
- static void http1_parser_test(void) {
1696
- http1_test_pos = 0;
1697
- struct {
1698
- const char *str;
1699
- long long num;
1700
- long long (*fn)(const uint8_t *, const uint8_t **);
1701
- } atol_test[] = {
1702
- {
1703
- .str = "0",
1704
- .num = 0,
1705
- .fn = http1_atol,
1706
- },
1707
- {
1708
- .str = "-0",
1709
- .num = 0,
1710
- .fn = http1_atol,
1711
- },
1712
- {
1713
- .str = "1",
1714
- .num = 1,
1715
- .fn = http1_atol,
1716
- },
1717
- {
1718
- .str = "-1",
1719
- .num = -1,
1720
- .fn = http1_atol,
1721
- },
1722
- {
1723
- .str = "123456789",
1724
- .num = 123456789,
1725
- .fn = http1_atol,
1726
- },
1727
- {
1728
- .str = "-123456789",
1729
- .num = -123456789,
1730
- .fn = http1_atol,
1731
- },
1732
- {
1733
- .str = "0x0",
1734
- .num = 0,
1735
- .fn = http1_atol16,
1736
- },
1737
- {
1738
- .str = "-0x0",
1739
- .num = 0,
1740
- .fn = http1_atol16,
1741
- },
1742
- {
1743
- .str = "-0x1",
1744
- .num = -1,
1745
- .fn = http1_atol16,
1746
- },
1747
- {
1748
- .str = "-f",
1749
- .num = -15,
1750
- .fn = http1_atol16,
1751
- },
1752
- {
1753
- .str = "-20",
1754
- .num = -32,
1755
- .fn = http1_atol16,
1756
- },
1757
- {
1758
- .str = "0xf0EAf9ff",
1759
- .num = 0xf0eaf9ff,
1760
- .fn = http1_atol16,
1761
- },
1762
- /* stop marker */
1763
- {
1764
- .str = NULL,
1765
- },
1766
- };
1767
- fprintf(stderr, "* testing string=>number conversion\n");
1768
- for (size_t i = 0; atol_test[i].str; ++i) {
1769
- const uint8_t *end;
1770
- fprintf(stderr, " %s", atol_test[i].str);
1771
- HTTP1_TEST_ASSERT(atol_test[i].fn((const uint8_t *)atol_test[i].str,
1772
- &end) == atol_test[i].num,
1773
- "\nhttp1_atol error: %s != %lld",
1774
- atol_test[i].str,
1775
- atol_test[i].num);
1776
- HTTP1_TEST_ASSERT((char *)end ==
1777
- (atol_test[i].str + strlen(atol_test[i].str)),
1778
- "\nhttp1_atol error: didn't end after (%s): %s",
1779
- atol_test[i].str,
1780
- (char *)end)
1781
- }
1782
- fprintf(stderr, "\n");
1783
- for (unsigned long long i = 1; i; i <<= 1) {
1784
- char tmp[128];
1785
- size_t tmp_len = sprintf(tmp, "%llx", i);
1786
- uint8_t *pos = (uint8_t *)tmp;
1787
- HTTP1_TEST_ASSERT(http1_atol16(pos, (const uint8_t **)&pos) ==
1788
- (long long)i &&
1789
- pos == (uint8_t *)(tmp + tmp_len),
1790
- "http1_atol16 roundtrip error.");
1791
- }
1792
-
1793
- for (size_t i = 0; http1_test_data[i].request[0]; ++i) {
1794
- fprintf(stderr, "* http1 parser test: %s\n", http1_test_data[i].test_name);
1795
- /* parse each request / response */
1796
- http1_parser_s parser = HTTP1_PARSER_INIT;
1797
- char buf[4096];
1798
- size_t r = 0;
1799
- size_t w = 0;
1800
- http1_test_temp_buf_pos = 0;
1801
- for (int j = 0; http1_test_data[i].request[j]; ++j) {
1802
- memcpy(buf + w,
1803
- http1_test_data[i].request[j],
1804
- strlen(http1_test_data[i].request[j]));
1805
- w += strlen(http1_test_data[i].request[j]);
1806
- size_t p = http1_parse(&parser, buf + r, w - r);
1807
- r += p;
1808
- HTTP1_TEST_ASSERT(r <= w, "parser consumed more than the buffer holds!");
1809
- }
1810
- /* test each request / response before overwriting the buffer */
1811
- HTTP1_TEST_STRING_FIELD(body, i);
1812
- HTTP1_TEST_STRING_FIELD(method, i);
1813
- HTTP1_TEST_STRING_FIELD(path, i);
1814
- HTTP1_TEST_STRING_FIELD(version, i);
1815
- r = 0;
1816
- while (http1_test_data[i].result.headers[r].name) {
1817
- HTTP1_TEST_STRING_FIELD(headers[r].name, i);
1818
- HTTP1_TEST_STRING_FIELD(headers[r].val, i);
1819
- HTTP1_TEST_ASSERT(http1_test_data[i].expect.headers[r].val_len ==
1820
- http1_test_data[i].result.headers[r].val_len &&
1821
- http1_test_data[i].expect.headers[r].name_len ==
1822
- http1_test_data[i].result.headers[r].name_len,
1823
- "--- name / value length error");
1824
- ++r;
1825
- }
1826
- HTTP1_TEST_ASSERT(!http1_test_data[i].expect.headers[r].name,
1827
- "Expected header missing:\n\t%s: %s",
1828
- http1_test_data[i].expect.headers[r].name,
1829
- http1_test_data[i].expect.headers[r].val);
1830
- /* advance counter */
1831
- ++http1_test_pos;
1832
- }
1833
- }
1834
-
1835
878
  #endif