iodine 0.7.60 → 0.8.0
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/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -21
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -148
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
data/ext/iodine/http1_parser.h
DELETED
|
@@ -1,878 +0,0 @@
|
|
|
1
|
-
#ifndef H_HTTP1_PARSER_H
|
|
2
|
-
/*
|
|
3
|
-
Copyright: Boaz Segev, 2017-2020
|
|
4
|
-
License: MIT
|
|
5
|
-
|
|
6
|
-
Feel free to copy, use and enjoy according to the license provided.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
This is a callback based parser. It parses the skeleton of the HTTP/1.x protocol
|
|
11
|
-
and leaves most of the work (validation, error checks, etc') to the callbacks.
|
|
12
|
-
*/
|
|
13
|
-
#define H_HTTP1_PARSER_H
|
|
14
|
-
#include <stddef.h>
|
|
15
|
-
#include <stdint.h>
|
|
16
|
-
#include <stdio.h>
|
|
17
|
-
#include <stdlib.h>
|
|
18
|
-
#include <string.h>
|
|
19
|
-
#include <sys/types.h>
|
|
20
|
-
|
|
21
|
-
/* *****************************************************************************
|
|
22
|
-
Parser Settings
|
|
23
|
-
***************************************************************************** */
|
|
24
|
-
|
|
25
|
-
#ifndef HTTP_HEADERS_LOWERCASE
|
|
26
|
-
/**
|
|
27
|
-
* When defined, HTTP headers will be converted to lowercase and header
|
|
28
|
-
* searches will be case sensitive.
|
|
29
|
-
*
|
|
30
|
-
* This is highly recommended, required by facil.io and helps with HTTP/2
|
|
31
|
-
* compatibility.
|
|
32
|
-
*/
|
|
33
|
-
#define HTTP_HEADERS_LOWERCASE 1
|
|
34
|
-
#endif
|
|
35
|
-
|
|
36
|
-
#ifndef HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
|
|
37
|
-
#define HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING 1
|
|
38
|
-
#endif
|
|
39
|
-
|
|
40
|
-
#ifndef FIO_MEMCHAR
|
|
41
|
-
/** Prefer a custom memchr implementation. Usualy memchr is better. */
|
|
42
|
-
#define FIO_MEMCHAR 0
|
|
43
|
-
#endif
|
|
44
|
-
|
|
45
|
-
#ifndef ALLOW_UNALIGNED_MEMORY_ACCESS
|
|
46
|
-
/** Peforms some optimizations assuming unaligned memory access is okay. */
|
|
47
|
-
#define ALLOW_UNALIGNED_MEMORY_ACCESS 0
|
|
48
|
-
#endif
|
|
49
|
-
|
|
50
|
-
#ifndef HTTP1_PARSER_CONVERT_EOL2NUL
|
|
51
|
-
#define HTTP1_PARSER_CONVERT_EOL2NUL 0
|
|
52
|
-
#endif
|
|
53
|
-
|
|
54
|
-
/* *****************************************************************************
|
|
55
|
-
Parser API
|
|
56
|
-
***************************************************************************** */
|
|
57
|
-
|
|
58
|
-
/** this struct contains the state of the parser. */
|
|
59
|
-
typedef struct http1_parser_s {
|
|
60
|
-
struct http1_parser_protected_read_only_state_s {
|
|
61
|
-
long long content_length; /* negative values indicate chuncked data state */
|
|
62
|
-
ssize_t read; /* total number of bytes read so far (body only) */
|
|
63
|
-
uint8_t *next; /* the known position for the end of request/response */
|
|
64
|
-
uint8_t reserved; /* for internal use */
|
|
65
|
-
} state;
|
|
66
|
-
} http1_parser_s;
|
|
67
|
-
|
|
68
|
-
#define HTTP1_PARSER_INIT \
|
|
69
|
-
{ \
|
|
70
|
-
{ 0 } \
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Returns the amount of data actually consumed by the parser.
|
|
75
|
-
*
|
|
76
|
-
* The value 0 indicates there wasn't enough data to be parsed and the same
|
|
77
|
-
* buffer (with more data) should be resubmitted.
|
|
78
|
-
*
|
|
79
|
-
* A value smaller than the buffer size indicates that EITHER a request /
|
|
80
|
-
* response was detected OR that the leftover could not be consumed because more
|
|
81
|
-
* data was required.
|
|
82
|
-
*
|
|
83
|
-
* Simply resubmit the reminder of the data to continue parsing.
|
|
84
|
-
*
|
|
85
|
-
* A request / response callback automatically stops the parsing process,
|
|
86
|
-
* allowing the user to adjust or refresh the state of the data.
|
|
87
|
-
*/
|
|
88
|
-
static size_t http1_parse(http1_parser_s *parser, void *buffer, size_t length);
|
|
89
|
-
|
|
90
|
-
/* *****************************************************************************
|
|
91
|
-
Required Callbacks (MUST be implemented by including file)
|
|
92
|
-
***************************************************************************** */
|
|
93
|
-
|
|
94
|
-
/** called when a request was received. */
|
|
95
|
-
static int http1_on_request(http1_parser_s *parser);
|
|
96
|
-
/** called when a response was received. */
|
|
97
|
-
static int http1_on_response(http1_parser_s *parser);
|
|
98
|
-
/** called when a request method is parsed. */
|
|
99
|
-
static int http1_on_method(http1_parser_s *parser,
|
|
100
|
-
char *method,
|
|
101
|
-
size_t method_len);
|
|
102
|
-
/** called when a response status is parsed. the status_str is the string
|
|
103
|
-
* without the prefixed numerical status indicator.*/
|
|
104
|
-
static int http1_on_status(http1_parser_s *parser,
|
|
105
|
-
size_t status,
|
|
106
|
-
char *status_str,
|
|
107
|
-
size_t len);
|
|
108
|
-
/** called when a request path (excluding query) is parsed. */
|
|
109
|
-
static int http1_on_path(http1_parser_s *parser, char *path, size_t path_len);
|
|
110
|
-
/** called when a request path (excluding query) is parsed. */
|
|
111
|
-
static int http1_on_query(http1_parser_s *parser,
|
|
112
|
-
char *query,
|
|
113
|
-
size_t query_len);
|
|
114
|
-
/** called when a the HTTP/1.x version is parsed. */
|
|
115
|
-
static int http1_on_version(http1_parser_s *parser, char *version, size_t len);
|
|
116
|
-
/** called when a header is parsed. */
|
|
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);
|
|
122
|
-
/** called when a body chunk is parsed. */
|
|
123
|
-
static int http1_on_body_chunk(http1_parser_s *parser,
|
|
124
|
-
char *data,
|
|
125
|
-
size_t data_len);
|
|
126
|
-
/** called when a protocol error occurred. */
|
|
127
|
-
static int http1_on_error(http1_parser_s *parser);
|
|
128
|
-
|
|
129
|
-
/* *****************************************************************************
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
Implementation Details
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
***************************************************************************** */
|
|
166
|
-
|
|
167
|
-
#if HTTP_HEADERS_LOWERCASE
|
|
168
|
-
#define HEADER_NAME_IS_EQ(var_name, const_name, len) \
|
|
169
|
-
(!memcmp((var_name), (const_name), (len)))
|
|
170
|
-
#else
|
|
171
|
-
#define HEADER_NAME_IS_EQ(var_name, const_name, len) \
|
|
172
|
-
(!strncasecmp((var_name), (const_name), (len)))
|
|
173
|
-
#endif
|
|
174
|
-
|
|
175
|
-
#define HTTP1_P_FLAG_STATUS_LINE 1
|
|
176
|
-
#define HTTP1_P_FLAG_HEADER_COMPLETE 2
|
|
177
|
-
#define HTTP1_P_FLAG_COMPLETE 4
|
|
178
|
-
#define HTTP1_P_FLAG_CLENGTH 8
|
|
179
|
-
#define HTTP1_PARSER_BIT_16 16
|
|
180
|
-
#define HTTP1_PARSER_BIT_32 32
|
|
181
|
-
#define HTTP1_P_FLAG_CHUNKED 64
|
|
182
|
-
#define HTTP1_P_FLAG_RESPONSE 128
|
|
183
|
-
|
|
184
|
-
/* *****************************************************************************
|
|
185
|
-
Seeking for characters in a string
|
|
186
|
-
***************************************************************************** */
|
|
187
|
-
|
|
188
|
-
#if FIO_MEMCHAR
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* This seems to be faster on some systems, especially for smaller distances.
|
|
192
|
-
*
|
|
193
|
-
* On newer systems, `memchr` should be faster.
|
|
194
|
-
*/
|
|
195
|
-
static int seek2ch(uint8_t **buffer,
|
|
196
|
-
register uint8_t *const limit,
|
|
197
|
-
const uint8_t c) {
|
|
198
|
-
if (*buffer >= limit) return 0;
|
|
199
|
-
if (**buffer == c) {
|
|
200
|
-
return 1;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
#if !HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
|
|
204
|
-
/* too short for this mess */
|
|
205
|
-
if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
|
|
206
|
-
goto finish;
|
|
207
|
-
|
|
208
|
-
/* align memory */
|
|
209
|
-
{
|
|
210
|
-
const uint8_t *alignment =
|
|
211
|
-
(uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
|
|
212
|
-
if (*buffer < alignment) *buffer += 1; /* we already tested this char */
|
|
213
|
-
if (limit >= alignment) {
|
|
214
|
-
while (*buffer < alignment) {
|
|
215
|
-
if (**buffer == c) {
|
|
216
|
-
return 1;
|
|
217
|
-
}
|
|
218
|
-
*buffer += 1;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
|
|
223
|
-
#else
|
|
224
|
-
const uint8_t *limit64 = (uint8_t *)limit - 7;
|
|
225
|
-
#endif
|
|
226
|
-
uint64_t wanted1 = 0x0101010101010101ULL * c;
|
|
227
|
-
for (; *buffer < limit64; *buffer += 8) {
|
|
228
|
-
const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
|
|
229
|
-
const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
|
|
230
|
-
const uint64_t t1 = (eq1 & 0x8080808080808080llu);
|
|
231
|
-
if ((t0 & t1)) {
|
|
232
|
-
break;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
#if !HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
|
|
236
|
-
finish:
|
|
237
|
-
#endif
|
|
238
|
-
while (*buffer < limit) {
|
|
239
|
-
if (**buffer == c) {
|
|
240
|
-
return 1;
|
|
241
|
-
}
|
|
242
|
-
(*buffer)++;
|
|
243
|
-
}
|
|
244
|
-
return 0;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
#else
|
|
248
|
-
|
|
249
|
-
/* a helper that seeks any char, converts it to NUL and returns 1 if found. */
|
|
250
|
-
inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
|
|
251
|
-
/* This is library based alternative that is sometimes slower */
|
|
252
|
-
if (*pos >= limit) return 0;
|
|
253
|
-
if (**pos == ch) {
|
|
254
|
-
return 1;
|
|
255
|
-
}
|
|
256
|
-
uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
|
|
257
|
-
if (tmp) {
|
|
258
|
-
*pos = tmp;
|
|
259
|
-
return 1;
|
|
260
|
-
}
|
|
261
|
-
*pos = limit;
|
|
262
|
-
return 0;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
#endif
|
|
266
|
-
|
|
267
|
-
/* a helper that seeks the EOL, converts it to NUL and returns it's length */
|
|
268
|
-
inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
|
|
269
|
-
/* single char lookup using memchr might be better when target is far... */
|
|
270
|
-
if (!seek2ch(pos, limit, '\n')) return 0;
|
|
271
|
-
if ((*pos)[-1] == '\r') {
|
|
272
|
-
#if HTTP1_PARSER_CONVERT_EOL2NUL
|
|
273
|
-
(*pos)[-1] = (*pos)[0] = 0;
|
|
274
|
-
#endif
|
|
275
|
-
return 2;
|
|
276
|
-
}
|
|
277
|
-
#if HTTP1_PARSER_CONVERT_EOL2NUL
|
|
278
|
-
(*pos)[0] = 0;
|
|
279
|
-
#endif
|
|
280
|
-
return 1;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/* *****************************************************************************
|
|
284
|
-
Change a letter to lower case (latin only)
|
|
285
|
-
***************************************************************************** */
|
|
286
|
-
|
|
287
|
-
static uint8_t http_tolower(uint8_t c) {
|
|
288
|
-
if (c >= 'A' && c <= 'Z') c |= 32;
|
|
289
|
-
return c;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/* *****************************************************************************
|
|
293
|
-
String to Number
|
|
294
|
-
***************************************************************************** */
|
|
295
|
-
|
|
296
|
-
/** Converts a String to a number using base 10 */
|
|
297
|
-
static long long http1_atol(const uint8_t *buf, const uint8_t **end) {
|
|
298
|
-
register unsigned long long i = 0;
|
|
299
|
-
uint8_t inv = 0;
|
|
300
|
-
while (*buf == ' ' || *buf == '\t' || *buf == '\f') ++buf;
|
|
301
|
-
while (*buf == '-' || *buf == '+') inv ^= (*(buf++) == '-');
|
|
302
|
-
while (i <= ((((~0ULL) >> 1) / 10)) && *buf >= '0' && *buf <= '9') {
|
|
303
|
-
i = i * 10;
|
|
304
|
-
i += *buf - '0';
|
|
305
|
-
++buf;
|
|
306
|
-
}
|
|
307
|
-
/* test for overflow */
|
|
308
|
-
if (i >= (~((~0ULL) >> 1)) || (*buf >= '0' && *buf <= '9')) i = (~0ULL >> 1);
|
|
309
|
-
if (inv) i = 0ULL - i;
|
|
310
|
-
if (end) *end = buf;
|
|
311
|
-
return i;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/** Converts a String to a number using base 16, overflow limited to 113bytes */
|
|
315
|
-
static long long http1_atol16(const uint8_t *buf, const uint8_t **end) {
|
|
316
|
-
register unsigned long long i = 0;
|
|
317
|
-
uint8_t inv = 0;
|
|
318
|
-
for (int limit_ = 0;
|
|
319
|
-
(*buf == ' ' || *buf == '\t' || *buf == '\f') && limit_ < 32;
|
|
320
|
-
++limit_)
|
|
321
|
-
++buf;
|
|
322
|
-
for (int limit_ = 0; (*buf == '-' || *buf == '+') && limit_ < 32; ++limit_)
|
|
323
|
-
inv ^= (*(buf++) == '-');
|
|
324
|
-
if (*buf == '0') ++buf;
|
|
325
|
-
if ((*buf | 32) == 'x') ++buf;
|
|
326
|
-
for (int limit_ = 0; (*buf == '0') && limit_ < 32; ++limit_) ++buf;
|
|
327
|
-
while (!(i & (~((~(0ULL)) >> 4)))) {
|
|
328
|
-
if (*buf >= '0' && *buf <= '9') {
|
|
329
|
-
i <<= 4;
|
|
330
|
-
i |= *buf - '0';
|
|
331
|
-
} else if ((*buf | 32) >= 'a' && (*buf | 32) <= 'f') {
|
|
332
|
-
i <<= 4;
|
|
333
|
-
i |= (*buf | 32) - ('a' - 10);
|
|
334
|
-
} else
|
|
335
|
-
break;
|
|
336
|
-
++buf;
|
|
337
|
-
}
|
|
338
|
-
if (inv) i = 0ULL - i;
|
|
339
|
-
if (end) *end = buf;
|
|
340
|
-
return i;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/* *****************************************************************************
|
|
344
|
-
HTTP/1.1 parsre stages
|
|
345
|
-
***************************************************************************** */
|
|
346
|
-
|
|
347
|
-
inline static int http1_consume_response_line(http1_parser_s *parser,
|
|
348
|
-
uint8_t *start,
|
|
349
|
-
uint8_t *end) {
|
|
350
|
-
parser->state.reserved |= HTTP1_P_FLAG_RESPONSE;
|
|
351
|
-
uint8_t *tmp = start;
|
|
352
|
-
if (!seek2ch(&tmp, end, ' ')) return -1;
|
|
353
|
-
if (http1_on_version(parser, (char *)start, tmp - start)) return -1;
|
|
354
|
-
tmp = start = tmp + 1;
|
|
355
|
-
if (!seek2ch(&tmp, end, ' ')) return -1;
|
|
356
|
-
if (http1_on_status(parser,
|
|
357
|
-
http1_atol(start, NULL),
|
|
358
|
-
(char *)(tmp + 1),
|
|
359
|
-
end - tmp))
|
|
360
|
-
return -1;
|
|
361
|
-
return 0;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
inline static int http1_consume_request_line(http1_parser_s *parser,
|
|
365
|
-
uint8_t *start,
|
|
366
|
-
uint8_t *end) {
|
|
367
|
-
uint8_t *tmp = start;
|
|
368
|
-
uint8_t *host_start = NULL;
|
|
369
|
-
uint8_t *host_end = NULL;
|
|
370
|
-
if (!seek2ch(&tmp, end, ' ')) return -1;
|
|
371
|
-
if (http1_on_method(parser, (char *)start, tmp - start)) return -1;
|
|
372
|
-
tmp = start = tmp + 1;
|
|
373
|
-
if (start[0] == 'h' && start[1] == 't' && start[2] == 't' &&
|
|
374
|
-
start[3] == 'p') {
|
|
375
|
-
if (start[4] == ':' && start[5] == '/' && start[6] == '/') {
|
|
376
|
-
/* Request URI is in long form... emulate Host header instead. */
|
|
377
|
-
tmp = host_end = host_start = (start += 7);
|
|
378
|
-
} else if (start[4] == 's' && start[5] == ':' && start[6] == '/' &&
|
|
379
|
-
start[7] == '/') {
|
|
380
|
-
/* Secure request is in long form... emulate Host header instead. */
|
|
381
|
-
tmp = host_end = host_start = (start += 8);
|
|
382
|
-
} else
|
|
383
|
-
goto review_path;
|
|
384
|
-
if (!seek2ch(&tmp, end, ' ')) return -1;
|
|
385
|
-
*tmp = ' ';
|
|
386
|
-
if (!seek2ch(&host_end, tmp, '/')) {
|
|
387
|
-
if (http1_on_path(parser, (char *)"/", 1)) return -1;
|
|
388
|
-
goto start_version;
|
|
389
|
-
}
|
|
390
|
-
host_end[0] = '/';
|
|
391
|
-
start = host_end;
|
|
392
|
-
}
|
|
393
|
-
review_path:
|
|
394
|
-
tmp = start;
|
|
395
|
-
if (seek2ch(&tmp, end, '?')) {
|
|
396
|
-
if (http1_on_path(parser, (char *)start, tmp - start)) return -1;
|
|
397
|
-
tmp = start = tmp + 1;
|
|
398
|
-
if (!seek2ch(&tmp, end, ' ')) return -1;
|
|
399
|
-
if (tmp - start > 0 && http1_on_query(parser, (char *)start, tmp - start))
|
|
400
|
-
return -1;
|
|
401
|
-
} else {
|
|
402
|
-
tmp = start;
|
|
403
|
-
if (!seek2ch(&tmp, end, ' ')) return -1;
|
|
404
|
-
if (http1_on_path(parser, (char *)start, tmp - start)) return -1;
|
|
405
|
-
}
|
|
406
|
-
start_version:
|
|
407
|
-
start = tmp + 1;
|
|
408
|
-
if (start + 5 >= end) /* require "HTTP/" */
|
|
409
|
-
return -1;
|
|
410
|
-
if (http1_on_version(parser, (char *)start, end - start)) return -1;
|
|
411
|
-
/* */
|
|
412
|
-
if (host_start && http1_on_header(parser,
|
|
413
|
-
(char *)"host",
|
|
414
|
-
4,
|
|
415
|
-
(char *)host_start,
|
|
416
|
-
host_end - host_start))
|
|
417
|
-
return -1;
|
|
418
|
-
return 0;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
#ifndef HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER
|
|
422
|
-
inline /* inline the function of it's short enough */
|
|
423
|
-
#endif
|
|
424
|
-
static int
|
|
425
|
-
http1_consume_header_transfer_encoding(http1_parser_s *parser,
|
|
426
|
-
uint8_t *start,
|
|
427
|
-
uint8_t *end_name,
|
|
428
|
-
uint8_t *start_value,
|
|
429
|
-
uint8_t *end) {
|
|
430
|
-
/* this removes the `chunked` marker and prepares to "unchunk" the data */
|
|
431
|
-
while (start_value < end && (end[-1] == ',' || end[-1] == ' ')) --end;
|
|
432
|
-
if ((end - start_value) == 7 &&
|
|
433
|
-
#if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
|
|
434
|
-
(((uint32_t *)(start_value))[0] | 0x20202020) ==
|
|
435
|
-
((uint32_t *)"chun")[0] &&
|
|
436
|
-
(((uint32_t *)(start_value + 3))[0] | 0x20202020) ==
|
|
437
|
-
((uint32_t *)"nked")[0]
|
|
438
|
-
#else
|
|
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' &&
|
|
442
|
-
(start_value[6] | 32) == 'd')
|
|
443
|
-
#endif
|
|
444
|
-
) {
|
|
445
|
-
if (parser->state.content_length) return -1;
|
|
446
|
-
/* simple case,only `chunked` as a value */
|
|
447
|
-
parser->state.reserved |= HTTP1_P_FLAG_CHUNKED;
|
|
448
|
-
parser->state.content_length = 0;
|
|
449
|
-
start_value += 7;
|
|
450
|
-
while (start_value < end && (*start_value == ',' || *start_value == ' '))
|
|
451
|
-
++start_value;
|
|
452
|
-
if (!(end - start_value)) return 0;
|
|
453
|
-
} else if ((end - start_value) > 7 &&
|
|
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' &&
|
|
457
|
-
(end[(-7 + 6)] | 32) == 'd')) {
|
|
458
|
-
/* simple case,`chunked` at the end of list (RFC required) */
|
|
459
|
-
if (parser->state.content_length) return -1;
|
|
460
|
-
parser->state.reserved |= HTTP1_P_FLAG_CHUNKED;
|
|
461
|
-
parser->state.content_length = 0;
|
|
462
|
-
end -= 7;
|
|
463
|
-
while (start_value < end && (end[-1] == ',' || end[-1] == ' ')) --end;
|
|
464
|
-
if (!(end - start_value)) return 0;
|
|
465
|
-
}
|
|
466
|
-
#ifdef HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER /* RFC diisallows this */
|
|
467
|
-
else if ((end - start_value) > 7 && (end - start_value) < 256) {
|
|
468
|
-
/* complex case, `the, chunked, marker, is in the middle of list */
|
|
469
|
-
uint8_t val[256];
|
|
470
|
-
size_t val_len = 0;
|
|
471
|
-
while (start_value < end && val_len < 256) {
|
|
472
|
-
if ((end - start_value) >= 7) {
|
|
473
|
-
if (
|
|
474
|
-
#if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
|
|
475
|
-
(((uint32_t *)(start_value))[0] | 0x20202020) ==
|
|
476
|
-
((uint32_t *)"chun")[0] &&
|
|
477
|
-
(((uint32_t *)(start_value + 3))[0] | 0x20202020) ==
|
|
478
|
-
((uint32_t *)"nked")[0]
|
|
479
|
-
#else
|
|
480
|
-
((start_value[0] | 32) == 'c' && (start_value[1] | 32) == 'h' &&
|
|
481
|
-
(start_value[2] | 32) == 'u' && (start_value[3] | 32) == 'n' &&
|
|
482
|
-
(start_value[4] | 32) == 'k' && (start_value[5] | 32) == 'e' &&
|
|
483
|
-
(start_value[6] | 32) == 'd')
|
|
484
|
-
#endif
|
|
485
|
-
|
|
486
|
-
) {
|
|
487
|
-
if (parser->state.content_length) return -1;
|
|
488
|
-
parser->state.reserved |= HTTP1_P_FLAG_CHUNKED;
|
|
489
|
-
parser->state.content_length = 0;
|
|
490
|
-
start_value += 7;
|
|
491
|
-
/* skip comma / white space */
|
|
492
|
-
while (start_value < end &&
|
|
493
|
-
(*start_value == ',' || *start_value == ' '))
|
|
494
|
-
++start_value;
|
|
495
|
-
continue;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
/* copy value */
|
|
499
|
-
while (start_value < end && val_len < 256 && start_value[0] != ',') {
|
|
500
|
-
val[val_len++] = *start_value;
|
|
501
|
-
++start_value;
|
|
502
|
-
}
|
|
503
|
-
/* copy comma */
|
|
504
|
-
if (start_value[0] == ',' && val_len < 256) {
|
|
505
|
-
val[val_len++] = *start_value;
|
|
506
|
-
++start_value;
|
|
507
|
-
}
|
|
508
|
-
/* skip spaces */
|
|
509
|
-
while (start_value < end && start_value[0] == ' ') {
|
|
510
|
-
++start_value;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
if (val_len < 256) {
|
|
514
|
-
while (start_value < end && val_len < 256) {
|
|
515
|
-
val[val_len++] = *start_value;
|
|
516
|
-
++start_value;
|
|
517
|
-
}
|
|
518
|
-
val[val_len] = 0;
|
|
519
|
-
}
|
|
520
|
-
/* perform callback with `val` or indicate error */
|
|
521
|
-
if (val_len == 256 || (val_len && http1_on_header(parser,
|
|
522
|
-
(char *)start,
|
|
523
|
-
(end_name - start),
|
|
524
|
-
(char *)val,
|
|
525
|
-
val_len)))
|
|
526
|
-
return -1;
|
|
527
|
-
return 0;
|
|
528
|
-
}
|
|
529
|
-
#endif /* HTTP1_ALLOW_CHUNKED_IN_MIDDLE_OF_HEADER */
|
|
530
|
-
/* perform callback */
|
|
531
|
-
if (http1_on_header(parser,
|
|
532
|
-
(char *)start,
|
|
533
|
-
(end_name - start),
|
|
534
|
-
(char *)start_value,
|
|
535
|
-
end - start_value))
|
|
536
|
-
return -1;
|
|
537
|
-
return 0;
|
|
538
|
-
}
|
|
539
|
-
inline static int http1_consume_header_top(http1_parser_s *parser,
|
|
540
|
-
uint8_t *start,
|
|
541
|
-
uint8_t *end_name,
|
|
542
|
-
uint8_t *start_value,
|
|
543
|
-
uint8_t *end) {
|
|
544
|
-
if ((end_name - start) == 14 &&
|
|
545
|
-
#if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
|
|
546
|
-
*((uint64_t *)start) == *((uint64_t *)"content-") &&
|
|
547
|
-
*((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")
|
|
548
|
-
#else
|
|
549
|
-
HEADER_NAME_IS_EQ((char *)start, "content-length", 14)
|
|
550
|
-
#endif
|
|
551
|
-
) {
|
|
552
|
-
/* handle the special `content-length` header */
|
|
553
|
-
if ((parser->state.reserved & HTTP1_P_FLAG_CHUNKED))
|
|
554
|
-
return 0; /* ignore if `chunked` */
|
|
555
|
-
long long old_clen = parser->state.content_length;
|
|
556
|
-
parser->state.content_length = http1_atol(start_value, NULL);
|
|
557
|
-
if ((parser->state.reserved & HTTP1_P_FLAG_CLENGTH) &&
|
|
558
|
-
old_clen != parser->state.content_length) {
|
|
559
|
-
/* content-length header repeated with conflict */
|
|
560
|
-
return -1;
|
|
561
|
-
}
|
|
562
|
-
parser->state.reserved |= HTTP1_P_FLAG_CLENGTH;
|
|
563
|
-
} else if ((end_name - start) == 17 && (end - start_value) >= 7 &&
|
|
564
|
-
#if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
|
|
565
|
-
*((uint64_t *)start) == *((uint64_t *)"transfer") &&
|
|
566
|
-
*((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin")
|
|
567
|
-
#else
|
|
568
|
-
HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17)
|
|
569
|
-
#endif
|
|
570
|
-
) {
|
|
571
|
-
/* handle the special `transfer-encoding: chunked` header */
|
|
572
|
-
return http1_consume_header_transfer_encoding(parser,
|
|
573
|
-
start,
|
|
574
|
-
end_name,
|
|
575
|
-
start_value,
|
|
576
|
-
end);
|
|
577
|
-
}
|
|
578
|
-
/* perform callback */
|
|
579
|
-
if (http1_on_header(parser,
|
|
580
|
-
(char *)start,
|
|
581
|
-
(end_name - start),
|
|
582
|
-
(char *)start_value,
|
|
583
|
-
end - start_value))
|
|
584
|
-
return -1;
|
|
585
|
-
return 0;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
inline static int http1_consume_header_trailer(http1_parser_s *parser,
|
|
589
|
-
uint8_t *start,
|
|
590
|
-
uint8_t *end_name,
|
|
591
|
-
uint8_t *start_value,
|
|
592
|
-
uint8_t *end) {
|
|
593
|
-
if ((end_name - start) > 1 && start[0] == 'x') {
|
|
594
|
-
/* X- headers are allowed */
|
|
595
|
-
goto white_listed;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
/* white listed trailer names */
|
|
599
|
-
const struct {
|
|
600
|
-
char *name;
|
|
601
|
-
long len;
|
|
602
|
-
} http1_trailer_white_list[] = {
|
|
603
|
-
{"server-timing", 13}, /* specific for client data... */
|
|
604
|
-
{NULL, 0}, /* end of list marker */
|
|
605
|
-
};
|
|
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 &&
|
|
608
|
-
HEADER_NAME_IS_EQ((char *)start,
|
|
609
|
-
http1_trailer_white_list[i].name,
|
|
610
|
-
http1_trailer_white_list[i].len)) {
|
|
611
|
-
/* header disallowed here */
|
|
612
|
-
goto white_listed;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
return 0;
|
|
616
|
-
white_listed:
|
|
617
|
-
/* perform callback */
|
|
618
|
-
if (http1_on_header(parser,
|
|
619
|
-
(char *)start,
|
|
620
|
-
(end_name - start),
|
|
621
|
-
(char *)start_value,
|
|
622
|
-
end - start_value))
|
|
623
|
-
return -1;
|
|
624
|
-
return 0;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
inline static int http1_consume_header(http1_parser_s *parser,
|
|
628
|
-
uint8_t *start,
|
|
629
|
-
uint8_t *end) {
|
|
630
|
-
uint8_t *end_name = start;
|
|
631
|
-
/* divide header name from data */
|
|
632
|
-
if (!seek2ch(&end_name, end, ':')) return -1;
|
|
633
|
-
if (end_name[-1] == ' ' || end_name[-1] == '\t') return -1;
|
|
634
|
-
#if HTTP_HEADERS_LOWERCASE
|
|
635
|
-
for (uint8_t *t = start; t < end_name; t++) {
|
|
636
|
-
*t = http_tolower(*t);
|
|
637
|
-
}
|
|
638
|
-
#endif
|
|
639
|
-
uint8_t *start_value = end_name + 1;
|
|
640
|
-
// clear away leading white space from value.
|
|
641
|
-
while (start_value < end &&
|
|
642
|
-
(start_value[0] == ' ' || start_value[0] == '\t')) {
|
|
643
|
-
start_value++;
|
|
644
|
-
};
|
|
645
|
-
return (parser->state.read ? http1_consume_header_trailer
|
|
646
|
-
: http1_consume_header_top)(parser,
|
|
647
|
-
start,
|
|
648
|
-
end_name,
|
|
649
|
-
start_value,
|
|
650
|
-
end);
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/* *****************************************************************************
|
|
654
|
-
HTTP/1.1 Body handling
|
|
655
|
-
***************************************************************************** */
|
|
656
|
-
|
|
657
|
-
inline static int http1_consume_body_streamed(http1_parser_s *parser,
|
|
658
|
-
void *buffer,
|
|
659
|
-
size_t length,
|
|
660
|
-
uint8_t **start) {
|
|
661
|
-
uint8_t *end = *start + parser->state.content_length - parser->state.read;
|
|
662
|
-
uint8_t *const stop = ((uint8_t *)buffer) + length;
|
|
663
|
-
if (end > stop) end = stop;
|
|
664
|
-
if (end > *start &&
|
|
665
|
-
http1_on_body_chunk(parser, (char *)(*start), end - *start))
|
|
666
|
-
return -1;
|
|
667
|
-
parser->state.read += (end - *start);
|
|
668
|
-
*start = end;
|
|
669
|
-
if (parser->state.content_length <= parser->state.read)
|
|
670
|
-
parser->state.reserved |= HTTP1_P_FLAG_COMPLETE;
|
|
671
|
-
return 0;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
inline static int http1_consume_body_chunked(http1_parser_s *parser,
|
|
675
|
-
void *buffer,
|
|
676
|
-
size_t length,
|
|
677
|
-
uint8_t **start) {
|
|
678
|
-
uint8_t *const stop = ((uint8_t *)buffer) + length;
|
|
679
|
-
uint8_t *end = *start;
|
|
680
|
-
while (*start < stop) {
|
|
681
|
-
if (parser->state.content_length == 0) {
|
|
682
|
-
if (end + 2 >= stop) return 0;
|
|
683
|
-
if ((end[0] == '\r' && end[1] == '\n')) {
|
|
684
|
-
/* remove tailing EOL that wasn't processed and retest */
|
|
685
|
-
end += 2;
|
|
686
|
-
*start = end;
|
|
687
|
-
if (end + 2 >= stop) return 0;
|
|
688
|
-
}
|
|
689
|
-
long long chunk_len = http1_atol16(end, (const uint8_t **)&end);
|
|
690
|
-
if (end + 2 > stop) /* overflowed? */
|
|
691
|
-
return 0;
|
|
692
|
-
if ((end[0] != '\r' || end[1] != '\n'))
|
|
693
|
-
return -1; /* required EOL after content length */
|
|
694
|
-
end += 2;
|
|
695
|
-
|
|
696
|
-
parser->state.content_length = 0 - chunk_len;
|
|
697
|
-
*start = end;
|
|
698
|
-
if (parser->state.content_length == 0) {
|
|
699
|
-
/* all chunked data was parsed */
|
|
700
|
-
/* update content-length */
|
|
701
|
-
parser->state.content_length = parser->state.read;
|
|
702
|
-
#ifdef HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
|
|
703
|
-
{ /* add virtual header ... ? */
|
|
704
|
-
char buf[512];
|
|
705
|
-
size_t buf_len = 512;
|
|
706
|
-
size_t tmp_len = parser->state.read;
|
|
707
|
-
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;
|
|
712
|
-
}
|
|
713
|
-
if (!(parser->state.reserved & HTTP1_P_FLAG_CLENGTH) &&
|
|
714
|
-
http1_on_header(parser,
|
|
715
|
-
"content-length",
|
|
716
|
-
14,
|
|
717
|
-
(char *)buf + buf_len,
|
|
718
|
-
511 - buf_len)) {
|
|
719
|
-
return -1;
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
#endif
|
|
723
|
-
/* FIXME: consume trailing EOL */
|
|
724
|
-
if (*start + 2 <= stop && (start[0][0] == '\r' || start[0][0] == '\n'))
|
|
725
|
-
*start += 1 + (start[0][1] == '\r' || start[0][1] == '\n');
|
|
726
|
-
else {
|
|
727
|
-
/* remove the "headers complete" and "trailer" flags */
|
|
728
|
-
parser->state.reserved =
|
|
729
|
-
HTTP1_P_FLAG_STATUS_LINE | HTTP1_P_FLAG_CLENGTH;
|
|
730
|
-
return -2;
|
|
731
|
-
}
|
|
732
|
-
/* the parsing complete flag */
|
|
733
|
-
parser->state.reserved |= HTTP1_P_FLAG_COMPLETE;
|
|
734
|
-
return 0;
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
end = *start + (0 - parser->state.content_length);
|
|
738
|
-
if (end > stop) end = stop;
|
|
739
|
-
if (end > *start &&
|
|
740
|
-
http1_on_body_chunk(parser, (char *)(*start), end - *start)) {
|
|
741
|
-
return -1;
|
|
742
|
-
}
|
|
743
|
-
parser->state.read += (end - *start);
|
|
744
|
-
parser->state.content_length += (end - *start);
|
|
745
|
-
*start = end;
|
|
746
|
-
}
|
|
747
|
-
return 0;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
inline static int http1_consume_body(http1_parser_s *parser,
|
|
751
|
-
void *buffer,
|
|
752
|
-
size_t length,
|
|
753
|
-
uint8_t **start) {
|
|
754
|
-
if (parser->state.content_length > 0 &&
|
|
755
|
-
parser->state.content_length > parser->state.read) {
|
|
756
|
-
/* normal, streamed data */
|
|
757
|
-
return http1_consume_body_streamed(parser, buffer, length, start);
|
|
758
|
-
} else if (parser->state.content_length <= 0 &&
|
|
759
|
-
(parser->state.reserved & HTTP1_P_FLAG_CHUNKED)) {
|
|
760
|
-
/* chuncked encoding */
|
|
761
|
-
return http1_consume_body_chunked(parser, buffer, length, start);
|
|
762
|
-
} else {
|
|
763
|
-
/* nothing to do - parsing complete */
|
|
764
|
-
parser->state.reserved |= HTTP1_P_FLAG_COMPLETE;
|
|
765
|
-
}
|
|
766
|
-
return 0;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
/* *****************************************************************************
|
|
770
|
-
HTTP/1.1 parsre function
|
|
771
|
-
***************************************************************************** */
|
|
772
|
-
#if DEBUG
|
|
773
|
-
#include <assert.h>
|
|
774
|
-
#define HTTP1_ASSERT assert
|
|
775
|
-
#else
|
|
776
|
-
#define HTTP1_ASSERT(...)
|
|
777
|
-
#endif
|
|
778
|
-
|
|
779
|
-
/**
|
|
780
|
-
* Returns the amount of data actually consumed by the parser.
|
|
781
|
-
*
|
|
782
|
-
* The value 0 indicates there wasn't enough data to be parsed and the same
|
|
783
|
-
* buffer (with more data) should be resubmitted.
|
|
784
|
-
*
|
|
785
|
-
* A value smaller than the buffer size indicates that EITHER a request /
|
|
786
|
-
* response was detected OR that the leftover could not be consumed because more
|
|
787
|
-
* data was required.
|
|
788
|
-
*
|
|
789
|
-
* Simply resubmit the reminder of the data to continue parsing.
|
|
790
|
-
*
|
|
791
|
-
* A request / response callback automatically stops the parsing process,
|
|
792
|
-
* allowing the user to adjust or refresh the state of the data.
|
|
793
|
-
*/
|
|
794
|
-
static size_t http1_parse(http1_parser_s *parser, void *buffer, size_t length) {
|
|
795
|
-
if (!length) return 0;
|
|
796
|
-
HTTP1_ASSERT(parser && buffer);
|
|
797
|
-
parser->state.next = NULL;
|
|
798
|
-
uint8_t *start = (uint8_t *)buffer;
|
|
799
|
-
uint8_t *end = start;
|
|
800
|
-
uint8_t *const stop = start + length;
|
|
801
|
-
uint8_t eol_len = 0;
|
|
802
|
-
#define HTTP1_CONSUMED ((size_t)((uintptr_t)start - (uintptr_t)buffer))
|
|
803
|
-
|
|
804
|
-
re_eval:
|
|
805
|
-
switch ((parser->state.reserved & 7)) {
|
|
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;
|
|
811
|
-
}
|
|
812
|
-
end = start;
|
|
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
|
|
827
|
-
goto error;
|
|
828
|
-
end = start = end + 1;
|
|
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:
|
|
845
|
-
++start;
|
|
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;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
/* are we done ? */
|
|
862
|
-
if (parser->state.reserved & HTTP1_P_FLAG_COMPLETE) {
|
|
863
|
-
parser->state.next = start;
|
|
864
|
-
if (((parser->state.reserved & HTTP1_P_FLAG_RESPONSE)
|
|
865
|
-
? http1_on_response
|
|
866
|
-
: http1_on_request)(parser))
|
|
867
|
-
goto error;
|
|
868
|
-
parser->state = (struct http1_parser_protected_read_only_state_s){0};
|
|
869
|
-
}
|
|
870
|
-
return HTTP1_CONSUMED;
|
|
871
|
-
error:
|
|
872
|
-
http1_on_error(parser);
|
|
873
|
-
parser->state = (struct http1_parser_protected_read_only_state_s){0};
|
|
874
|
-
return length;
|
|
875
|
-
#undef HTTP1_CONSUMED
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
#endif
|