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.
- checksums.yaml +4 -4
- data/README.md +8 -8
- data/SECURITY.md +32 -0
- data/ext/iodine/fio.c +1 -1
- data/ext/iodine/fio.h +12 -14
- data/ext/iodine/fio_json_parser.h +5 -4
- data/ext/iodine/fiobj_data.c +13 -11
- data/ext/iodine/fiobj_str.h +1 -1
- data/ext/iodine/fiobject.h +5 -4
- data/ext/iodine/http1.c +92 -7
- data/ext/iodine/http1_parser.h +164 -1121
- data/ext/iodine/iodine_store.c +23 -19
- data/ext/iodine/websockets.c +7 -16
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine.rb +4 -0
- metadata +5 -6
data/ext/iodine/http1_parser.h
CHANGED
|
@@ -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.
|
|
41
|
+
/** Prefer a custom memchr implementation. Usualy memchr is better. */
|
|
43
42
|
#define FIO_MEMCHAR 0
|
|
44
43
|
#endif
|
|
45
44
|
|
|
46
|
-
#ifndef
|
|
47
|
-
/**
|
|
48
|
-
#define
|
|
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
|
|
52
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
310
|
-
if (
|
|
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
|
-
|
|
329
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
442
|
-
inline /* inline the function
|
|
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'
|
|
461
|
-
(start_value[2] | 32) == 'u'
|
|
462
|
-
(start_value[4] | 32) == 'k'
|
|
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'
|
|
476
|
-
(end[(-7 + 2)] | 32) == 'u'
|
|
477
|
-
(end[(-7 + 4)] | 32) == 'k'
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
589
|
-
|
|
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
|
-
}
|
|
622
|
-
{
|
|
623
|
-
{NULL, 0},
|
|
602
|
+
} http1_trailer_white_list[] = {
|
|
603
|
+
{"server-timing", 13}, /* specific for client data... */
|
|
604
|
+
{NULL, 0}, /* end of list marker */
|
|
624
605
|
};
|
|
625
|
-
|
|
626
|
-
|
|
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
|
-
|
|
633
|
-
|
|
609
|
+
http1_trailer_white_list[i].name,
|
|
610
|
+
http1_trailer_white_list[i].len)) {
|
|
634
611
|
/* header disallowed here */
|
|
635
|
-
goto
|
|
612
|
+
goto white_listed;
|
|
636
613
|
}
|
|
637
614
|
}
|
|
638
615
|
return 0;
|
|
639
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
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'
|
|
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
|
-
#
|
|
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
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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
|
-
|
|
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
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
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
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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
|