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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.clang-format +15 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -3
  5. data/CHANGELOG.md +132 -12
  6. data/Gemfile +5 -7
  7. data/LICENSE.txt +1 -1
  8. data/README.md +147 -671
  9. data/Rakefile +9 -16
  10. data/examples/chatroom.nru +9 -0
  11. data/examples/client +74 -0
  12. data/examples/config-hello.nru +9 -0
  13. data/examples/config-hello.ru +11 -0
  14. data/examples/config-hijack.ru +13 -0
  15. data/examples/config-shootout.ru +43 -0
  16. data/examples/config.nru +120 -0
  17. data/examples/config.rb +37 -0
  18. data/examples/config.ru +42 -53
  19. data/examples/fake +24 -0
  20. data/examples/hello_neorack +137 -0
  21. data/examples/routes.nru +29 -0
  22. data/examples/routes.ru +42 -0
  23. data/exe/iodine +218 -258
  24. data/ext/iodine/extconf.rb +27 -100
  25. data/ext/iodine/fio-stl.h +128336 -0
  26. data/ext/iodine/fio-stl.md +26396 -0
  27. data/ext/iodine/iodine.c +213 -1393
  28. data/ext/iodine/iodine.h +250 -55
  29. data/ext/iodine/iodine_arg_helper.h +329 -0
  30. data/ext/iodine/iodine_caller.h +234 -25
  31. data/ext/iodine/iodine_cli.h +434 -0
  32. data/ext/iodine/iodine_compression.h +312 -0
  33. data/ext/iodine/iodine_connection.h +3484 -34
  34. data/ext/iodine/iodine_core.h +363 -0
  35. data/ext/iodine/iodine_crypto.h +1441 -0
  36. data/ext/iodine/iodine_defer.h +225 -4
  37. data/ext/iodine/iodine_json.h +738 -4
  38. data/ext/iodine/iodine_listener.h +263 -0
  39. data/ext/iodine/iodine_log.h +297 -0
  40. data/ext/iodine/iodine_minimap.h +543 -0
  41. data/ext/iodine/iodine_mustache.h +461 -4
  42. data/ext/iodine/iodine_pubsub_eng.h +729 -0
  43. data/ext/iodine/iodine_pubsub_history.h +547 -0
  44. data/ext/iodine/iodine_pubsub_msg.h +228 -0
  45. data/ext/iodine/iodine_redis.h +322 -0
  46. data/ext/iodine/iodine_store.h +418 -16
  47. data/ext/iodine/iodine_threads.h +279 -0
  48. data/ext/iodine/iodine_tls.h +515 -6
  49. data/ext/iodine/iodine_utils.h +923 -0
  50. data/iodine.gemspec +37 -42
  51. data/lib/iodine/benchmark.rb +1690 -0
  52. data/lib/iodine/documentation.rb +3085 -0
  53. data/lib/iodine/version.rb +17 -1
  54. data/lib/iodine.rb +54 -255
  55. data/logo.svg +36 -0
  56. data/mustache.md +55 -0
  57. data/sig/iodine.rbs +4 -0
  58. metadata +58 -184
  59. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  60. data/.github/workflows/ruby.yml +0 -42
  61. data/.gitignore +0 -21
  62. data/LIMITS.md +0 -41
  63. data/SPEC-PubSub-Draft.md +0 -159
  64. data/SPEC-WebSocket-Draft.md +0 -239
  65. data/bin/console +0 -22
  66. data/bin/info.md +0 -353
  67. data/bin/mustache_bench.rb +0 -100
  68. data/bin/poc/Gemfile.lock +0 -23
  69. data/bin/poc/README.md +0 -37
  70. data/bin/poc/config.ru +0 -66
  71. data/bin/poc/gemfile +0 -1
  72. data/bin/poc/www/index.html +0 -57
  73. data/examples/async_task.ru +0 -92
  74. data/examples/bates/README.md +0 -3
  75. data/examples/bates/config.ru +0 -342
  76. data/examples/bates/david+bold.pdf +0 -0
  77. data/examples/bates/public/drop-pdf.png +0 -0
  78. data/examples/bates/public/index.html +0 -600
  79. data/examples/echo.ru +0 -59
  80. data/examples/etag.ru +0 -16
  81. data/examples/hello.ru +0 -29
  82. data/examples/pubsub_engine.ru +0 -81
  83. data/examples/rack3.ru +0 -12
  84. data/examples/redis.ru +0 -70
  85. data/examples/shootout.ru +0 -73
  86. data/examples/sub-protocols.ru +0 -90
  87. data/examples/tcp_client.rb +0 -66
  88. data/examples/x-sendfile.ru +0 -14
  89. data/ext/iodine/fio.c +0 -11995
  90. data/ext/iodine/fio.h +0 -6371
  91. data/ext/iodine/fio_cli.c +0 -431
  92. data/ext/iodine/fio_cli.h +0 -189
  93. data/ext/iodine/fio_json_parser.h +0 -688
  94. data/ext/iodine/fio_siphash.c +0 -157
  95. data/ext/iodine/fio_siphash.h +0 -37
  96. data/ext/iodine/fio_tls.h +0 -129
  97. data/ext/iodine/fio_tls_missing.c +0 -649
  98. data/ext/iodine/fio_tls_openssl.c +0 -1106
  99. data/ext/iodine/fio_tmpfile.h +0 -50
  100. data/ext/iodine/fiobj.h +0 -44
  101. data/ext/iodine/fiobj4fio.h +0 -21
  102. data/ext/iodine/fiobj_ary.c +0 -333
  103. data/ext/iodine/fiobj_ary.h +0 -139
  104. data/ext/iodine/fiobj_data.c +0 -1187
  105. data/ext/iodine/fiobj_data.h +0 -167
  106. data/ext/iodine/fiobj_hash.c +0 -409
  107. data/ext/iodine/fiobj_hash.h +0 -176
  108. data/ext/iodine/fiobj_json.c +0 -622
  109. data/ext/iodine/fiobj_json.h +0 -68
  110. data/ext/iodine/fiobj_mem.h +0 -71
  111. data/ext/iodine/fiobj_mustache.c +0 -317
  112. data/ext/iodine/fiobj_mustache.h +0 -62
  113. data/ext/iodine/fiobj_numbers.c +0 -344
  114. data/ext/iodine/fiobj_numbers.h +0 -127
  115. data/ext/iodine/fiobj_str.c +0 -433
  116. data/ext/iodine/fiobj_str.h +0 -172
  117. data/ext/iodine/fiobject.c +0 -620
  118. data/ext/iodine/fiobject.h +0 -655
  119. data/ext/iodine/hpack.h +0 -1923
  120. data/ext/iodine/http.c +0 -2743
  121. data/ext/iodine/http.h +0 -1002
  122. data/ext/iodine/http1.c +0 -910
  123. data/ext/iodine/http1.h +0 -29
  124. data/ext/iodine/http1_parser.h +0 -878
  125. data/ext/iodine/http_internal.c +0 -1279
  126. data/ext/iodine/http_internal.h +0 -237
  127. data/ext/iodine/http_mime_parser.h +0 -350
  128. data/ext/iodine/iodine_caller.c +0 -230
  129. data/ext/iodine/iodine_connection.c +0 -941
  130. data/ext/iodine/iodine_defer.c +0 -420
  131. data/ext/iodine/iodine_fiobj2rb.h +0 -120
  132. data/ext/iodine/iodine_helpers.c +0 -282
  133. data/ext/iodine/iodine_helpers.h +0 -12
  134. data/ext/iodine/iodine_http.c +0 -1181
  135. data/ext/iodine/iodine_http.h +0 -23
  136. data/ext/iodine/iodine_json.c +0 -302
  137. data/ext/iodine/iodine_mustache.c +0 -567
  138. data/ext/iodine/iodine_pubsub.c +0 -580
  139. data/ext/iodine/iodine_pubsub.h +0 -26
  140. data/ext/iodine/iodine_rack_io.c +0 -273
  141. data/ext/iodine/iodine_rack_io.h +0 -20
  142. data/ext/iodine/iodine_store.c +0 -148
  143. data/ext/iodine/iodine_tcp.c +0 -346
  144. data/ext/iodine/iodine_tcp.h +0 -13
  145. data/ext/iodine/iodine_tls.c +0 -261
  146. data/ext/iodine/mustache_parser.h +0 -1546
  147. data/ext/iodine/redis_engine.c +0 -957
  148. data/ext/iodine/redis_engine.h +0 -79
  149. data/ext/iodine/resp_parser.h +0 -317
  150. data/ext/iodine/websocket_parser.h +0 -506
  151. data/ext/iodine/websockets.c +0 -743
  152. data/ext/iodine/websockets.h +0 -185
  153. data/lib/iodine/connection.rb +0 -61
  154. data/lib/iodine/json.rb +0 -42
  155. data/lib/iodine/mustache.rb +0 -113
  156. data/lib/iodine/pubsub.rb +0 -55
  157. data/lib/iodine/rack_utils.rb +0 -43
  158. data/lib/iodine/tls.rb +0 -16
  159. data/lib/rack/handler/iodine.rb +0 -33
  160. data/logo.png +0 -0
@@ -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