iodine 0.7.38 → 0.7.39

Sign up to get free protection for your applications and to get access to all the features.
@@ -181,6 +181,7 @@ static void iodine_defer_performe_once(void *block, void *ignr) {
181
181
  }
182
182
 
183
183
  static void iodine_defer_run_timer(void *block) {
184
+ /* TODO, update return value to allow timer cancellation */
184
185
  IodineCaller.call((VALUE)block, call_id);
185
186
  }
186
187
 
@@ -17,28 +17,40 @@ module Iodine
17
17
  # Instances of this class are passed to the callback objects. i.e.:
18
18
  #
19
19
  # module MyConnectionCallbacks
20
+ #
20
21
  # # called when the callback object is linked with a new client
21
22
  # def on_open client
22
23
  # client.is_a?(Iodine::Connection) # => true
23
24
  # end
25
+ #
24
26
  # # called when data is available
25
27
  # def on_message client, data
26
28
  # client.is_a?(Iodine::Connection) # => true
27
29
  # end
30
+ #
28
31
  # # called when the server is shutting down, before closing the client
29
32
  # # (it's still possible to send messages to the client)
30
33
  # def on_shutdown client
31
34
  # client.is_a?(Iodine::Connection) # => true
32
35
  # end
36
+ #
33
37
  # # called when the client is closed (no longer available)
34
38
  # def on_close client
35
39
  # client.is_a?(Iodine::Connection) # => true
36
40
  # end
41
+ #
37
42
  # # called when all the previous calls to `client.write` have completed
38
43
  # # (the local buffer was drained and is now empty)
39
44
  # def on_drained client
40
45
  # client.is_a?(Iodine::Connection) # => true
41
46
  # end
47
+ #
48
+ # # called when timeout was reached, llowing a `ping` to be sent
49
+ # def ping client
50
+ # client.is_a?(Iodine::Connection) # => true
51
+ # clint.close() # close connection on timeout is the default
52
+ # end
53
+ #
42
54
  # # Allows the module to be used as a static callback object (avoiding object allocation)
43
55
  # extend self
44
56
  # end
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.7.38'.freeze
2
+ VERSION = '0.7.39'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.38
4
+ version: 0.7.39
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-12-09 00:00:00.000000000 Z
11
+ date: 2020-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -188,7 +188,6 @@ files:
188
188
  - ext/iodine/http.h
189
189
  - ext/iodine/http1.c
190
190
  - ext/iodine/http1.h
191
- - ext/iodine/http1_parser.c
192
191
  - ext/iodine/http1_parser.h
193
192
  - ext/iodine/http_internal.c
194
193
  - ext/iodine/http_internal.h
@@ -244,7 +243,7 @@ licenses:
244
243
  metadata:
245
244
  allowed_push_host: https://rubygems.org
246
245
  post_install_message: |-
247
- Thank you for installing Iodine 0.7.38.
246
+ Thank you for installing Iodine 0.7.39.
248
247
  Remember: if iodine supports your business, it's only fair to give value back (code contributions / donations).
249
248
  rdoc_options: []
250
249
  require_paths:
@@ -266,7 +265,7 @@ requirements:
266
265
  - Ruby >= 2.3.8 (Ruby EOL).
267
266
  - Ruby >= 2.5.0 recommended.
268
267
  - TLS requires OpenSSL >= 1.1.0
269
- rubygems_version: 3.0.1
268
+ rubygems_version: 3.1.2
270
269
  signing_key:
271
270
  specification_version: 4
272
271
  summary: iodine - a fast HTTP / Websocket Server with Pub/Sub support, optimized for
@@ -1,616 +0,0 @@
1
- /*
2
- Copyright: Boaz Segev, 2017-2019
3
- License: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #ifndef __GNU_SOURCE
8
- #define __GNU_SOURCE
9
- #endif
10
-
11
- #include <http1_parser.h>
12
-
13
- #include <ctype.h>
14
- #include <stdio.h>
15
- #include <string.h>
16
-
17
- /* *****************************************************************************
18
- Seeking for characters in a string
19
- ***************************************************************************** */
20
-
21
- #ifndef ALLOW_UNALIGNED_MEMORY_ACCESS
22
- #define ALLOW_UNALIGNED_MEMORY_ACCESS 0
23
- #endif
24
-
25
- #ifndef HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
26
- #define HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING 1
27
- #endif
28
-
29
- #if FIO_MEMCHAR
30
-
31
- /**
32
- * This seems to be faster on some systems, especially for smaller distances.
33
- *
34
- * On newer systems, `memchr` should be faster.
35
- */
36
- static int seek2ch(uint8_t **buffer, register uint8_t *const limit,
37
- const uint8_t c) {
38
- if (*buffer >= limit)
39
- return 0;
40
-
41
- if (**buffer == c) {
42
- #if HTTP1_PARSER_CONVERT_EOL2NUL
43
- **buffer = 0;
44
- #endif
45
- return 1;
46
- }
47
-
48
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
49
- /* too short for this mess */
50
- if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
51
- goto finish;
52
-
53
- /* align memory */
54
- {
55
- const uint8_t *alignment =
56
- (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
57
- if (limit >= alignment) {
58
- while (*buffer < alignment) {
59
- if (**buffer == c) {
60
- #if HTTP1_PARSER_CONVERT_EOL2NUL
61
- **buffer = 0;
62
- #endif
63
- return 1;
64
- }
65
- *buffer += 1;
66
- }
67
- }
68
- }
69
- const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
70
- #else
71
- const uint8_t *limit64 = (uint8_t *)limit - 7;
72
- #endif
73
- uint64_t wanted1 = 0x0101010101010101ULL * c;
74
- for (; *buffer < limit64; *buffer += 8) {
75
- const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
76
- const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
77
- const uint64_t t1 = (eq1 & 0x8080808080808080llu);
78
- if ((t0 & t1)) {
79
- break;
80
- }
81
- }
82
- #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
83
- finish:
84
- #endif
85
- while (*buffer < limit) {
86
- if (**buffer == c) {
87
- #if HTTP1_PARSER_CONVERT_EOL2NUL
88
- **buffer = 0;
89
- #endif
90
- return 1;
91
- }
92
- (*buffer)++;
93
- }
94
- return 0;
95
- }
96
-
97
- #else
98
-
99
- /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
100
- inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
101
- /* This is library based alternative that is sometimes slower */
102
- if (*pos >= limit)
103
- return 0;
104
- if (**pos == ch) {
105
- return 1;
106
- }
107
- uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
108
- if (tmp) {
109
- *pos = tmp;
110
- #if HTTP1_PARSER_CONVERT_EOL2NUL
111
- *tmp = 0;
112
- #endif
113
- return 1;
114
- }
115
- *pos = limit;
116
- return 0;
117
- }
118
-
119
- #endif
120
-
121
- /* a helper that seeks the EOL, converts it to NUL and returns it's length */
122
- inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
123
- /* single char lookup using memchr might be better when target is far... */
124
- if (!seek2ch(pos, limit, '\n'))
125
- return 0;
126
- if ((*pos)[-1] == '\r') {
127
- #if HTTP1_PARSER_CONVERT_EOL2NUL
128
- (*pos)[-1] = 0;
129
- #endif
130
- return 2;
131
- }
132
- return 1;
133
- }
134
-
135
- /* *****************************************************************************
136
- String to Number
137
- ***************************************************************************** */
138
-
139
- /** Converts a String to a number using base 10 */
140
- static long long http1_atol(const uint8_t *buf, const uint8_t **end) {
141
- register unsigned long long i = 0;
142
- uint8_t inv = 0;
143
- while (*buf == ' ' || *buf == '\t' || *buf == '\f')
144
- ++buf;
145
- while (*buf == '-' || *buf == '+')
146
- inv ^= (*(buf++) == '-');
147
- while (i <= ((((~0ULL) >> 1) / 10)) && *buf >= '0' && *buf <= '9') {
148
- i = i * 10;
149
- i += *buf - '0';
150
- ++buf;
151
- }
152
- /* test for overflow */
153
- if (i >= (~((~0ULL) >> 1)) || (*buf >= '0' && *buf <= '9'))
154
- i = (~0ULL >> 1);
155
- if (inv)
156
- i = 0ULL - i;
157
- if (end)
158
- *end = buf;
159
- return i;
160
- }
161
-
162
- /** Converts a String to a number using base 16, overflow limited to 113bytes */
163
- static long long http1_atol16(const uint8_t *buf, const uint8_t **end) {
164
- register unsigned long long i = 0;
165
- uint8_t inv = 0;
166
- for (int limit_ = 0;
167
- (*buf == ' ' || *buf == '\t' || *buf == '\f') && limit_ < 32; ++limit_)
168
- ++buf;
169
- for (int limit_ = 0; (*buf == '-' || *buf == '+') && limit_ < 32; ++limit_)
170
- inv ^= (*(buf++) == '-');
171
- if (*buf == '0')
172
- ++buf;
173
- if ((*buf | 32) == 'x')
174
- ++buf;
175
- for (int limit_ = 0; (*buf == '0') && limit_ < 32; ++limit_)
176
- ++buf;
177
- while (!(i & (~((~(0ULL)) >> 4)))) {
178
- if (*buf >= '0' && *buf <= '9') {
179
- i <<= 4;
180
- i |= *buf - '0';
181
- } else if ((*buf | 32) >= 'a' && (*buf | 32) <= 'f') {
182
- i <<= 4;
183
- i |= (*buf | 32) - ('a' - 10);
184
- } else
185
- break;
186
- ++buf;
187
- }
188
- if (inv)
189
- i = 0ULL - i;
190
- if (end)
191
- *end = buf;
192
- return i;
193
- }
194
-
195
- /* *****************************************************************************
196
- HTTP/1.1 parsre stages
197
- ***************************************************************************** */
198
-
199
- inline static int consume_response_line(struct http1_fio_parser_args_s *args,
200
- uint8_t *start, uint8_t *end) {
201
- args->parser->state.reserved |= 128;
202
- uint8_t *tmp = start;
203
- if (!seek2ch(&tmp, end, ' '))
204
- return -1;
205
- if (args->on_http_version(args->parser, (char *)start, tmp - start))
206
- return -1;
207
- tmp = start = tmp + 1;
208
- if (!seek2ch(&tmp, end, ' '))
209
- return -1;
210
- if (args->on_status(args->parser, atol((char *)start), (char *)(tmp + 1),
211
- end - tmp))
212
- return -1;
213
- return 0;
214
- }
215
-
216
- inline static int consume_request_line(struct http1_fio_parser_args_s *args,
217
- uint8_t *start, uint8_t *end) {
218
- uint8_t *tmp = start;
219
- uint8_t *host_start = NULL;
220
- uint8_t *host_end = NULL;
221
- if (!seek2ch(&tmp, end, ' '))
222
- return -1;
223
- if (args->on_method(args->parser, (char *)start, tmp - start))
224
- return -1;
225
- tmp = start = tmp + 1;
226
- if (start[0] == 'h' && start[1] == 't' && start[2] == 't' &&
227
- start[3] == 'p') {
228
- if (start[4] == ':' && start[5] == '/' && start[6] == '/') {
229
- /* Request URI is in long form... emulate Host header instead. */
230
- tmp = host_end = host_start = (start += 7);
231
- } else if (start[4] == 's' && start[5] == ':' && start[6] == '/' &&
232
- start[7] == '/') {
233
- /* Secure request is in long form... emulate Host header instead. */
234
- tmp = host_end = host_start = (start += 8);
235
- } else
236
- goto review_path;
237
- if (!seek2ch(&tmp, end, ' '))
238
- return -1;
239
- *tmp = ' ';
240
- if (!seek2ch(&host_end, tmp, '/')) {
241
- if (args->on_path(args->parser, (char *)"/", 1))
242
- return -1;
243
- goto start_version;
244
- }
245
- host_end[0] = '/';
246
- start = host_end;
247
- }
248
- review_path:
249
- tmp = start;
250
- if (seek2ch(&tmp, end, '?')) {
251
- if (args->on_path(args->parser, (char *)start, tmp - start))
252
- return -1;
253
- tmp = start = tmp + 1;
254
- if (!seek2ch(&tmp, end, ' '))
255
- return -1;
256
- if (tmp - start > 0 &&
257
- args->on_query(args->parser, (char *)start, tmp - start))
258
- return -1;
259
- } else {
260
- tmp = start;
261
- if (!seek2ch(&tmp, end, ' '))
262
- return -1;
263
- if (args->on_path(args->parser, (char *)start, tmp - start))
264
- return -1;
265
- }
266
- start_version:
267
- start = tmp + 1;
268
- if (start + 5 >= end) /* require "HTTP/" */
269
- return -1;
270
- if (args->on_http_version(args->parser, (char *)start, end - start))
271
- return -1;
272
- /* */
273
- if (host_start && args->on_header(args->parser, (char *)"host", 4,
274
- (char *)host_start, host_end - host_start))
275
- return -1;
276
- return 0;
277
- }
278
-
279
- inline static int consume_header(struct http1_fio_parser_args_s *args,
280
- uint8_t *start, uint8_t *end) {
281
- uint8_t *end_name = start;
282
- /* divide header name from data */
283
- if (!seek2ch(&end_name, end, ':'))
284
- return -1;
285
- #if HTTP_HEADERS_LOWERCASE
286
- for (uint8_t *t = start; t < end_name; t++) {
287
- *t = tolower(*t);
288
- }
289
- #endif
290
- uint8_t *start_value = end_name + 1;
291
- if (start_value[0] == ' ') {
292
- start_value++;
293
- };
294
-
295
- if ((end_name - start) == 14 &&
296
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
297
- *((uint64_t *)start) == *((uint64_t *)"content-") &&
298
- *((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")
299
- #else
300
- HEADER_NAME_IS_EQ((char *)start, "content-length", 14)
301
- #endif
302
- ) {
303
- /* handle the special `content-length` header */
304
- args->parser->state.content_length = http1_atol(start_value, NULL);
305
- } else if ((end_name - start) == 17 && (end - start_value) >= 7 &&
306
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
307
- *((uint64_t *)start) == *((uint64_t *)"transfer") &&
308
- *((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin")
309
- #else
310
- HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17)
311
- #endif
312
- ) {
313
- /* handle the special `transfer-encoding: chunked` header? */
314
- /* this removes the `chunked` marker and "unchunks" the data */
315
- if (
316
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
317
- (((uint32_t *)(start_value))[0] | 0x20202020) ==
318
- ((uint32_t *)"chun")[0] &&
319
- (((uint32_t *)(start_value + 3))[0] | 0x20202020) ==
320
- ((uint32_t *)"nked")[0]
321
- #else
322
- ((start_value[0] | 32) == 'c' && (start_value[1] | 32) == 'h' &&
323
- (start_value[2] | 32) == 'u' && (start_value[3] | 32) == 'n' &&
324
- (start_value[4] | 32) == 'k' && (start_value[5] | 32) == 'e' &&
325
- (start_value[6] | 32) == 'd')
326
- #endif
327
- ) {
328
- /* simple case,`chunked` at the beginning */
329
- args->parser->state.reserved |= 64;
330
- start_value += 7;
331
- while (start_value < end && (*start_value == ',' || *start_value == ' '))
332
- ++start_value;
333
- if (!(end - start_value))
334
- return 0;
335
- } else if ((end - start_value) > 7 &&
336
- ((end[(-7 + 0)] | 32) == 'c' && (end[(-7 + 1)] | 32) == 'h' &&
337
- (end[(-7 + 2)] | 32) == 'u' && (end[(-7 + 3)] | 32) == 'n' &&
338
- (end[(-7 + 4)] | 32) == 'k' && (end[(-7 + 5)] | 32) == 'e' &&
339
- (end[(-7 + 6)] | 32) == 'd')) {
340
- /* simple case,`chunked` at the end of list */
341
- args->parser->state.reserved |= 64;
342
- end -= 7;
343
- while (start_value < end && (end[-1] == ',' || end[-1] == ' '))
344
- --end;
345
- if (!(end - start_value))
346
- return 0;
347
- } else if ((end - start_value) > 7 && (end - start_value) < 256) {
348
- /* complex case, `the, chunked, marker, is in the middle of list */
349
- uint8_t val[256];
350
- size_t val_len = 0;
351
- while (start_value < end) {
352
- if (
353
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED
354
- (((uint32_t *)(start_value))[0] | 0x20202020) ==
355
- ((uint32_t *)"chun")[0] &&
356
- (((uint32_t *)(start_value + 3))[0] | 0x20202020) ==
357
- ((uint32_t *)"nked")[0]
358
- #else
359
- ((start_value[0] | 32) == 'c' && (start_value[1] | 32) == 'h' &&
360
- (start_value[2] | 32) == 'u' && (start_value[3] | 32) == 'n' &&
361
- (start_value[4] | 32) == 'k' && (start_value[5] | 32) == 'e' &&
362
- (start_value[6] | 32) == 'd')
363
- #endif
364
-
365
- ) {
366
- args->parser->state.reserved |= 64;
367
- start_value += 7;
368
- /* skip comma / white space */
369
- while (start_value < end &&
370
- (*start_value == ',' || *start_value == ' '))
371
- ++start_value;
372
- break;
373
- }
374
- val[val_len++] = *start_value;
375
- ++start_value;
376
- }
377
- while (start_value < end) {
378
- val[val_len++] = *start_value;
379
- ++start_value;
380
- }
381
- /* perform callback with `val` */
382
- val[val_len] = 0;
383
- if (val_len && args->on_header(args->parser, (char *)start,
384
- (end_name - start), (char *)val, val_len))
385
- return -1;
386
- return 0;
387
- }
388
- } else if ((end_name - start) == 7 &&
389
- #if HTTP1_UNALIGNED_MEMORY_ACCESS_ENABLED && HTTP_HEADERS_LOWERCASE
390
- *((uint64_t *)start) == *((uint64_t *)"trailer")
391
- #else
392
- HEADER_NAME_IS_EQ((char *)start, "trailer", 7)
393
- #endif
394
- ) {
395
- /* chunked data with trailer... */
396
- args->parser->state.reserved |= 64;
397
- args->parser->state.reserved |= 32;
398
- // return 0; /* hide Trailer header, since we process the headers? */
399
- }
400
-
401
- /* perform callback */
402
- if (args->on_header(args->parser, (char *)start, (end_name - start),
403
- (char *)start_value, end - start_value))
404
- return -1;
405
- return 0;
406
- }
407
-
408
- /* *****************************************************************************
409
- HTTP/1.1 Body handling
410
- ***************************************************************************** */
411
-
412
- inline static int consume_body_streamed(struct http1_fio_parser_args_s *args,
413
- uint8_t **start) {
414
- uint8_t *end =
415
- *start + args->parser->state.content_length - args->parser->state.read;
416
- uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
417
- if (end > stop)
418
- end = stop;
419
- if (end > *start &&
420
- args->on_body_chunk(args->parser, (char *)(*start), end - *start))
421
- return -1;
422
- args->parser->state.read += (end - *start);
423
- *start = end;
424
- if (args->parser->state.content_length <= args->parser->state.read)
425
- args->parser->state.reserved |= 4;
426
- return 0;
427
- }
428
-
429
- inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
430
- uint8_t **start) {
431
- uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
432
- uint8_t *end = *start;
433
- while (*start < stop) {
434
- if (args->parser->state.content_length == 0) {
435
- if (end + 2 >= stop)
436
- return 0;
437
- if ((end[0] == '\r' && end[1] == '\n')) {
438
- /* remove tailing EOL that wasn't processed and retest */
439
- end += 2;
440
- *start = end;
441
- if (end + 2 >= stop)
442
- return 0;
443
- }
444
- long long chunk_len = http1_atol16(end, (const uint8_t **)&end);
445
- if (end + 2 > stop) /* overflowed? */
446
- return 0;
447
- if ((end[0] != '\r' || end[1] != '\n'))
448
- return -1; /* required EOL after content length */
449
- end += 2;
450
-
451
- args->parser->state.content_length = 0 - chunk_len;
452
- *start = end;
453
- if (args->parser->state.content_length == 0) {
454
- /* all chunked data was parsed - update content-length */
455
- args->parser->state.content_length = args->parser->state.read;
456
- /* consume trailing EOL */
457
- if (*start + 2 <= stop)
458
- *start += 2;
459
- #ifdef HTTP_ADD_CONTENT_LENGTH_HEADER_IF_MISSING
460
- { /* add virtual header ... ? */
461
- char buf[512];
462
- size_t buf_len = 512;
463
- size_t tmp_len = args->parser->state.read;
464
- buf[--buf_len] = 0;
465
- while (tmp_len) {
466
- size_t mod = tmp_len / 10;
467
- buf[--buf_len] = '0' + (tmp_len - (mod * 10));
468
- tmp_len = mod;
469
- }
470
- if (args->on_header(args->parser, "content-length", 14,
471
- (char *)buf + buf_len, 511 - buf_len))
472
- return -1;
473
- }
474
- #endif
475
- if (args->parser->state.reserved & 32) {
476
- /* remove the "headers complete" and "trailer" flags */
477
- args->parser->state.reserved &= 0xDD; /* 0xDD == ~2 & ~32 & 0xFF */
478
- return -2;
479
- }
480
- /* the parsing complete flag */
481
- args->parser->state.reserved |= 4;
482
- return 0;
483
- }
484
- }
485
- end = *start + (0 - args->parser->state.content_length);
486
- if (end > stop)
487
- end = stop;
488
- if (end > *start &&
489
- args->on_body_chunk(args->parser, (char *)(*start), end - *start)) {
490
- return -1;
491
- }
492
- args->parser->state.read += (end - *start);
493
- args->parser->state.content_length += (end - *start);
494
- *start = end;
495
- }
496
- return 0;
497
- }
498
-
499
- inline static int consume_body(struct http1_fio_parser_args_s *args,
500
- uint8_t **start) {
501
- if (args->parser->state.content_length > 0 &&
502
- args->parser->state.content_length > args->parser->state.read) {
503
- /* normal, streamed data */
504
- return consume_body_streamed(args, start);
505
- } else if (args->parser->state.content_length <= 0 &&
506
- (args->parser->state.reserved & 64)) {
507
- /* chuncked encoding */
508
- return consume_body_chunked(args, start);
509
- } else {
510
- /* nothing to do - parsing complete */
511
- args->parser->state.reserved |= 4;
512
- }
513
- return 0;
514
- }
515
-
516
- /* *****************************************************************************
517
- HTTP/1.1 parsre function
518
- ***************************************************************************** */
519
- #if DEBUG
520
- #include <assert.h>
521
- #else
522
- #define DEBUG 0
523
- #define assert(...)
524
- #endif
525
-
526
- size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
527
- assert(args->parser && args->buffer);
528
- args->parser->state.next = NULL;
529
- uint8_t *start = args->buffer;
530
- uint8_t *end = start;
531
- uint8_t *const stop = start + args->length;
532
- uint8_t eol_len = 0;
533
- #define CONSUMED ((size_t)((uintptr_t)start - (uintptr_t)args->buffer))
534
- // fprintf(stderr, "** resuming with at %p with %.*s...(%lu)\n", args->buffer,
535
- // 4,
536
- // start, args->length);
537
- re_eval:
538
- switch ((args->parser->state.reserved & 15)) {
539
-
540
- /* request / response line */
541
- case 0:
542
- /* clear out any leadinng white space */
543
- while ((start < stop) &&
544
- (*start == '\r' || *start == '\n' || *start == ' ' || *start == 0)) {
545
- ++start;
546
- }
547
- end = start;
548
- /* make sure the whole line is available*/
549
- if (!(eol_len = seek2eol(&end, stop)))
550
- return CONSUMED;
551
-
552
- if (start[0] == 'H' && start[1] == 'T' && start[2] == 'T' &&
553
- start[3] == 'P') {
554
- /* HTTP response */
555
- if (consume_response_line(args, start, end - eol_len + 1))
556
- goto error;
557
- } else if (tolower(start[0]) >= 'a' && tolower(start[0]) <= 'z') {
558
- /* HTTP request */
559
- if (consume_request_line(args, start, end - eol_len + 1))
560
- goto error;
561
- }
562
- end = start = end + 1;
563
- args->parser->state.reserved |= 1;
564
-
565
- /* fallthrough */
566
- /* headers */
567
- case 1:
568
- do {
569
- if (start >= stop)
570
- return CONSUMED; /* buffer ended on header line */
571
- if (*start == '\r' || *start == '\n') {
572
- goto finished_headers; /* empty line, end of headers */
573
- }
574
- if (!(eol_len = seek2eol(&end, stop)))
575
- return CONSUMED;
576
- if (consume_header(args, start, end - eol_len + 1))
577
- goto error;
578
- end = start = end + 1;
579
- } while ((args->parser->state.reserved & 2) == 0);
580
- finished_headers:
581
- ++start;
582
- if (*start == '\n')
583
- ++start;
584
- end = start;
585
- args->parser->state.reserved |= 2;
586
- /* fallthrough */
587
- /* request body */
588
- case 3: { /* 2 | 1 == 3 */
589
- int t3 = consume_body(args, &start);
590
- switch (t3) {
591
- case -1:
592
- goto error;
593
- case -2:
594
- goto re_eval;
595
- }
596
- break;
597
- }
598
- }
599
- /* are we done ? */
600
- if (args->parser->state.reserved & 4) {
601
- args->parser->state.next = start;
602
- if (((args->parser->state.reserved & 128) ? args->on_response
603
- : args->on_request)(args->parser))
604
- goto error;
605
- args->parser->state =
606
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
607
- }
608
- return CONSUMED;
609
- error:
610
- args->on_error(args->parser);
611
- args->parser->state =
612
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
613
- return args->length;
614
- }
615
-
616
- #undef CONSUMED