iodine 0.4.8 → 0.4.10

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.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +26 -0
  4. data/README.md +18 -12
  5. data/SPEC-Websocket-Draft.md +9 -5
  6. data/bin/ws-echo +3 -0
  7. data/examples/config.ru +0 -1
  8. data/examples/echo.ru +3 -1
  9. data/examples/redis.ru +0 -1
  10. data/ext/iodine/base64.c +97 -105
  11. data/ext/iodine/defer.c +16 -1
  12. data/ext/iodine/defer.h +10 -0
  13. data/ext/iodine/evio.c +35 -13
  14. data/ext/iodine/extconf.rb +1 -1
  15. data/ext/iodine/facil.c +12 -1
  16. data/ext/iodine/facil.h +3 -1
  17. data/ext/iodine/fio2resp.c +71 -0
  18. data/ext/iodine/fio2resp.h +50 -0
  19. data/ext/iodine/fio_cli_helper.c +404 -0
  20. data/ext/iodine/fio_cli_helper.h +152 -0
  21. data/ext/iodine/fiobj.h +631 -0
  22. data/ext/iodine/fiobj_alloc.c +81 -0
  23. data/ext/iodine/fiobj_ary.c +290 -0
  24. data/ext/iodine/fiobj_generic.c +260 -0
  25. data/ext/iodine/fiobj_hash.c +447 -0
  26. data/ext/iodine/fiobj_io.c +58 -0
  27. data/ext/iodine/fiobj_json.c +779 -0
  28. data/ext/iodine/fiobj_misc.c +213 -0
  29. data/ext/iodine/fiobj_numbers.c +113 -0
  30. data/ext/iodine/fiobj_primitives.c +98 -0
  31. data/ext/iodine/fiobj_str.c +261 -0
  32. data/ext/iodine/fiobj_sym.c +213 -0
  33. data/ext/iodine/fiobj_tests.c +474 -0
  34. data/ext/iodine/fiobj_types.h +290 -0
  35. data/ext/iodine/http1.c +54 -36
  36. data/ext/iodine/http1_parser.c +143 -35
  37. data/ext/iodine/http1_parser.h +6 -3
  38. data/ext/iodine/http1_response.c +0 -1
  39. data/ext/iodine/http_response.c +1 -1
  40. data/ext/iodine/iodine.c +20 -4
  41. data/ext/iodine/iodine_protocol.c +5 -4
  42. data/ext/iodine/iodine_pubsub.c +1 -1
  43. data/ext/iodine/random.c +5 -5
  44. data/ext/iodine/sha1.c +5 -8
  45. data/ext/iodine/sha2.c +8 -11
  46. data/ext/iodine/sha2.h +3 -3
  47. data/ext/iodine/sock.c +29 -31
  48. data/ext/iodine/websocket_parser.h +428 -0
  49. data/ext/iodine/websockets.c +112 -377
  50. data/ext/iodine/xor-crypt.c +16 -12
  51. data/lib/iodine/version.rb +1 -1
  52. metadata +21 -3
  53. data/ext/iodine/empty.h +0 -26
@@ -0,0 +1,290 @@
1
+ /*
2
+ Copyright: Boaz segev, 2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+
8
+ #ifndef H_FIOBJ_TYPES_INTERNAL_H
9
+ #define H_FIOBJ_TYPES_INTERNAL_H
10
+
11
+ #ifndef _GNU_SOURCE
12
+ #define _GNU_SOURCE
13
+ #endif
14
+
15
+ #include "fiobj.h"
16
+
17
+ #include <errno.h>
18
+ #include <math.h>
19
+ #include <signal.h>
20
+ #include <stdarg.h>
21
+ #include <string.h>
22
+ #include <sys/types.h>
23
+
24
+ /* *****************************************************************************
25
+ Atomic add / subtract
26
+ ***************************************************************************** */
27
+
28
+ /* Select the correct compiler builtin method. */
29
+ #if defined(__has_builtin)
30
+
31
+ #if __has_builtin(__atomic_exchange_n)
32
+ #define SPN_LOCK_BUILTIN(...) __atomic_exchange_n(__VA_ARGS__, __ATOMIC_ACQ_REL)
33
+ /** An atomic addition operation */
34
+ #define spn_add(...) __atomic_add_fetch(__VA_ARGS__, __ATOMIC_ACQ_REL)
35
+ /** An atomic subtraction operation */
36
+ #define spn_sub(...) __atomic_sub_fetch(__VA_ARGS__, __ATOMIC_ACQ_REL)
37
+
38
+ #elif __has_builtin(__sync_fetch_and_or)
39
+ #define SPN_LOCK_BUILTIN(...) __sync_fetch_and_or(__VA_ARGS__)
40
+ /** An atomic addition operation */
41
+ #define spn_add(...) __sync_add_and_fetch(__VA_ARGS__)
42
+ /** An atomic subtraction operation */
43
+ #define spn_sub(...) __sync_sub_and_fetch(__VA_ARGS__)
44
+
45
+ #else
46
+ #error Required builtin "__sync_swap" or "__sync_fetch_and_or" missing from compiler.
47
+ #endif /* defined(__has_builtin) */
48
+
49
+ #elif __GNUC__ > 3
50
+ #define SPN_LOCK_BUILTIN(...) __sync_fetch_and_or(__VA_ARGS__)
51
+ /** An atomic addition operation */
52
+ #define spn_add(...) __sync_add_and_fetch(__VA_ARGS__)
53
+ /** An atomic subtraction operation */
54
+ #define spn_sub(...) __sync_sub_and_fetch(__VA_ARGS__)
55
+
56
+ #else
57
+ #error Required builtin "__sync_swap" or "__sync_fetch_and_or" not found.
58
+ #endif
59
+
60
+ /* *****************************************************************************
61
+ Simple List - Used for fiobj_s * objects, but can be used for anything really.
62
+ ***************************************************************************** */
63
+
64
+ typedef struct fio_ls_s {
65
+ struct fio_ls_s *prev;
66
+ struct fio_ls_s *next;
67
+ fiobj_s *obj;
68
+ } fio_ls_s;
69
+
70
+ #define FIO_LS_INIT(name) \
71
+ { .next = &(name), .prev = &(name) }
72
+
73
+ /** Adds an object to the list's head. */
74
+ static inline __attribute__((unused)) void fio_ls_push(fio_ls_s *pos,
75
+ fiobj_s *obj) {
76
+ /* prepare item */
77
+ fio_ls_s *item = (fio_ls_s *)malloc(sizeof(*item));
78
+ if (!item)
79
+ perror("ERROR: fiobj list couldn't allocate memory"), exit(errno);
80
+ *item = (fio_ls_s){.prev = pos, .next = pos->next, .obj = obj};
81
+ /* inject item */
82
+ pos->next->prev = item;
83
+ pos->next = item;
84
+ }
85
+
86
+ /** Adds an object to the list's tail. */
87
+ static inline __attribute__((unused)) void fio_ls_unshift(fio_ls_s *pos,
88
+ fiobj_s *obj) {
89
+ pos = pos->prev;
90
+ fio_ls_push(pos, obj);
91
+ }
92
+
93
+ /** Removes an object from the list's head. */
94
+ static inline __attribute__((unused)) fiobj_s *fio_ls_pop(fio_ls_s *list) {
95
+ if (list->next == list)
96
+ return NULL;
97
+ fio_ls_s *item = list->next;
98
+ fiobj_s *ret = item->obj;
99
+ list->next = item->next;
100
+ list->next->prev = list;
101
+ free(item);
102
+ return ret;
103
+ }
104
+
105
+ /** Removes an object from the list's tail. */
106
+ static inline __attribute__((unused)) fiobj_s *fio_ls_shift(fio_ls_s *list) {
107
+ if (list->prev == list)
108
+ return NULL;
109
+ fio_ls_s *item = list->prev;
110
+ fiobj_s *ret = item->obj;
111
+ list->prev = item->prev;
112
+ list->prev->next = list;
113
+ free(item);
114
+ return ret;
115
+ }
116
+
117
+ /** Removes an object from the containing node. */
118
+ static inline __attribute__((unused)) fiobj_s *fio_ls_remove(fio_ls_s *node) {
119
+ fiobj_s *ret = node->obj;
120
+ node->next->prev = node->prev->next;
121
+ node->prev->next = node->next->prev;
122
+ free(node);
123
+ return ret;
124
+ }
125
+
126
+ /* *****************************************************************************
127
+ Memory Page Size
128
+ ***************************************************************************** */
129
+
130
+ #if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
131
+ #include <unistd.h>
132
+
133
+ size_t __attribute__((weak)) fiobj_memory_page_size(void) {
134
+ static size_t page_size = 0;
135
+ if (page_size)
136
+ return page_size;
137
+ page_size = sysconf(_SC_PAGESIZE);
138
+ if (!page_size)
139
+ page_size = 4096;
140
+ return page_size;
141
+ }
142
+ #pragma weak fiobj_memory_page_size
143
+
144
+ #else
145
+ #define fiobj_memory_page_size() 4096
146
+
147
+ #endif
148
+ /* *****************************************************************************
149
+ Object types
150
+ ***************************************************************************** */
151
+
152
+ /* Number */
153
+ typedef struct {
154
+ fiobj_type_en type;
155
+ int64_t i;
156
+ } fio_num_s;
157
+
158
+ /* Float */
159
+ typedef struct {
160
+ fiobj_type_en type;
161
+ double f;
162
+ } fio_float_s;
163
+
164
+ /* String */
165
+ typedef struct {
166
+ fiobj_type_en type;
167
+ uint64_t capa;
168
+ uint64_t len;
169
+ uint8_t is_static;
170
+ char *str;
171
+ } fio_str_s;
172
+
173
+ /* Symbol */
174
+ typedef struct {
175
+ fiobj_type_en type;
176
+ uintptr_t hash;
177
+ uint64_t len;
178
+ char str[];
179
+ } fio_sym_s;
180
+
181
+ /* IO */
182
+ typedef struct {
183
+ fiobj_type_en type;
184
+ intptr_t fd;
185
+ } fio_io_s;
186
+
187
+ /* Array */
188
+ typedef struct {
189
+ fiobj_type_en type;
190
+ uint64_t start;
191
+ uint64_t end;
192
+ uint64_t capa;
193
+ fiobj_s **arry;
194
+ } fio_ary_s;
195
+
196
+ /* *****************************************************************************
197
+ Hash types
198
+ ***************************************************************************** */
199
+ /* MUST be a power of 2 */
200
+ #define FIOBJ_HASH_MAX_MAP_SEEK (256)
201
+
202
+ typedef struct {
203
+ uintptr_t hash;
204
+ fio_ls_s *container;
205
+ } map_info_s;
206
+
207
+ typedef struct {
208
+ uintptr_t capa;
209
+ map_info_s *data;
210
+ } fio_map_s;
211
+
212
+ typedef struct {
213
+ fiobj_type_en type;
214
+ uintptr_t count;
215
+ uintptr_t mask;
216
+ fio_ls_s items;
217
+ fio_map_s map;
218
+ } fio_hash_s;
219
+
220
+ /* Hash node */
221
+ typedef struct {
222
+ fiobj_type_en type;
223
+ fiobj_s *name;
224
+ fiobj_s *obj;
225
+ } fio_couplet_s;
226
+
227
+ void fiobj_hash_rehash(fiobj_s *h);
228
+
229
+ /* *****************************************************************************
230
+ Type VTable (virtual function table)
231
+ ***************************************************************************** */
232
+ struct fiobj_vtable_s {
233
+ /* deallocate an object */
234
+ void (*free)(fiobj_s *);
235
+ /* object value as String */
236
+ fio_cstr_s (*to_str)(fiobj_s *);
237
+ /* object value as Integer */
238
+ int64_t (*to_i)(fiobj_s *);
239
+ /* object value as Float */
240
+ double (*to_f)(fiobj_s *);
241
+ /* true if object is equal. nested objects must be ignored (test container) */
242
+ int (*is_eq)(fiobj_s *self, fiobj_s *other);
243
+ /* return the number of nested object */
244
+ size_t (*count)(fiobj_s *);
245
+ /* perform a task for the object's children (-1 stops iteration)
246
+ * returns the number of items processed + `start_at`.
247
+ */
248
+ size_t (*each1)(fiobj_s *, size_t start_at,
249
+ int (*task)(fiobj_s *obj, void *arg), void *arg);
250
+ };
251
+
252
+ fio_cstr_s fiobj_noop_str(fiobj_s *obj);
253
+ int64_t fiobj_noop_i(fiobj_s *obj);
254
+ double fiobj_noop_f(fiobj_s *obj);
255
+ size_t fiobj_noop_count(fiobj_s *obj);
256
+ size_t fiobj_noop_each1(fiobj_s *obj, size_t start_at,
257
+ int (*task)(fiobj_s *obj, void *arg), void *arg);
258
+ void fiobj_simple_dealloc(fiobj_s *o);
259
+
260
+ /* *****************************************************************************
261
+ The Object type head and management
262
+ ***************************************************************************** */
263
+
264
+ typedef struct {
265
+ uint64_t ref;
266
+ struct fiobj_vtable_s *vtable;
267
+ } fiobj_head_s;
268
+
269
+ #define OBJ2HEAD(o) (((fiobj_head_s *)(o)) - 1)[0]
270
+ #define HEAD2OBJ(o) ((fiobj_s *)(((fiobj_head_s *)(o)) + 1))
271
+
272
+ #define OBJREF_ADD(o) spn_add(&OBJ2HEAD((o)).ref, 1)
273
+ #define OBJREF_REM(o) spn_sub(&OBJ2HEAD((o)).ref, 1)
274
+
275
+ #define obj2io(o) ((fio_io_s *)(o))
276
+ #define obj2ary(o) ((fio_ary_s *)(o))
277
+ #define obj2str(o) ((fio_str_s *)(o))
278
+ #define obj2sym(o) ((fio_sym_s *)(o))
279
+ #define obj2num(o) ((fio_num_s *)(o))
280
+ #define obj2hash(o) ((fio_hash_s *)(o))
281
+ #define obj2float(o) ((fio_float_s *)(o))
282
+ #define obj2couplet(o) ((fio_couplet_s *)(o))
283
+
284
+ /* *****************************************************************************
285
+ Internal API required across the board
286
+ ***************************************************************************** */
287
+ void fiobj_dealloc(fiobj_s *obj);
288
+ uint64_t fiobj_sym_hash(const void *data, size_t len);
289
+
290
+ #endif
data/ext/iodine/http1.c CHANGED
@@ -101,6 +101,43 @@ initialize:
101
101
  return http1_pool.protocol_mem;
102
102
  }
103
103
 
104
+ /* *****************************************************************************
105
+ HTTP/1.1 error responses
106
+ ***************************************************************************** */
107
+
108
+ static void err_bad_request(http1_protocol_s *pr) {
109
+ http_response_s *response = http_response_create(&pr->request.request);
110
+ if (pr->settings->log_static)
111
+ http_response_log_start(response);
112
+ response->status = 400;
113
+ if (!pr->settings->public_folder ||
114
+ http_response_sendfile2(response, &pr->request.request,
115
+ pr->settings->public_folder,
116
+ pr->settings->public_folder_length, "400.html", 8,
117
+ pr->settings->log_static)) {
118
+ response->should_close = 1;
119
+ http_response_write_body(response, "Bad Request", 11);
120
+ http_response_finish(response);
121
+ }
122
+ sock_close(pr->request.request.fd);
123
+ pr->request.buffer_pos = 0;
124
+ }
125
+
126
+ static void err_too_big(http1_protocol_s *pr) {
127
+ http_response_s *response = http_response_create(&pr->request.request);
128
+ if (pr->settings->log_static)
129
+ http_response_log_start(response);
130
+ response->status = 431;
131
+ if (!pr->settings->public_folder ||
132
+ http_response_sendfile2(response, &pr->request.request,
133
+ pr->settings->public_folder,
134
+ pr->settings->public_folder_length, "431.html", 8,
135
+ pr->settings->log_static)) {
136
+ http_response_write_body(response, "Request Header Fields Too Large", 31);
137
+ http_response_finish(response);
138
+ }
139
+ }
140
+
104
141
  /* *****************************************************************************
105
142
  HTTP/1.1 parsre callbacks
106
143
  ***************************************************************************** */
@@ -115,6 +152,7 @@ static int http1_on_request(http1_parser_s *parser) {
115
152
  if (pr->request.request.host == NULL) {
116
153
  goto bad_request;
117
154
  }
155
+ pr->request.request.content_length = parser->state.content_length;
118
156
  http_settings_s *settings = pr->settings;
119
157
  pr->request.request.settings = settings;
120
158
  // make sure udata to NULL, making it available for the user
@@ -131,23 +169,7 @@ static int http1_on_request(http1_parser_s *parser) {
131
169
  return 0;
132
170
  bad_request:
133
171
  /* handle generally bad requests */
134
- {
135
- http_response_s *response = http_response_create(&pr->request.request);
136
- if (pr->settings->log_static)
137
- http_response_log_start(response);
138
- response->status = 400;
139
- if (!pr->settings->public_folder ||
140
- http_response_sendfile2(response, &pr->request.request,
141
- pr->settings->public_folder,
142
- pr->settings->public_folder_length, "400.html",
143
- 8, pr->settings->log_static)) {
144
- response->should_close = 1;
145
- http_response_write_body(response, "Bad Request", 11);
146
- http_response_finish(response);
147
- }
148
- sock_close(pr->request.request.fd);
149
- pr->request.buffer_pos = 0;
150
- }
172
+ err_bad_request(pr);
151
173
  return -1;
152
174
  }
153
175
  /** called when a response was received. */
@@ -215,6 +237,8 @@ static int http1_on_header(http1_parser_s *parser, char *name, size_t name_len,
215
237
  http1_protocol_s *pr = parser->udata;
216
238
  if (!pr || pr->request.header_pos >= HTTP1_MAX_HEADER_COUNT - 1)
217
239
  goto too_big;
240
+ if (parser->state.read)
241
+ goto too_big; /* refuse trailer header data, it isn't in buffer */
218
242
 
219
243
  /** test for special headers that should be easily accessible **/
220
244
  #if HTTP_HEADERS_LOWERCASE
@@ -259,20 +283,7 @@ static int http1_on_header(http1_parser_s *parser, char *name, size_t name_len,
259
283
  return 0;
260
284
  too_big:
261
285
  /* handle oversized headers */
262
- {
263
- http_response_s *response = http_response_create(&pr->request.request);
264
- if (pr->settings->log_static)
265
- http_response_log_start(response);
266
- response->status = 431;
267
- if (!pr->settings->public_folder ||
268
- http_response_sendfile2(response, &pr->request.request,
269
- pr->settings->public_folder,
270
- pr->settings->public_folder_length, "431.html",
271
- 8, pr->settings->log_static)) {
272
- http_response_write_body(response, "Request Header Fields Too Large", 31);
273
- http_response_finish(response);
274
- }
275
- }
286
+ err_too_big(pr);
276
287
  return -1;
277
288
  }
278
289
 
@@ -282,12 +293,13 @@ static int http1_on_body_chunk(http1_parser_s *parser, char *data,
282
293
  http1_protocol_s *pr = parser->udata;
283
294
  if (!pr)
284
295
  return -1;
296
+ if (parser->state.content_length > (ssize_t)pr->settings->max_body_size ||
297
+ parser->state.read > (ssize_t)pr->settings->max_body_size)
298
+ return -1; /* test every time, in case of chunked data */
285
299
  if (!parser->state.read) {
286
- if (parser->state.content_length > pr->settings->max_body_size)
287
- return -1;
288
- pr->request.request.content_length = parser->state.content_length;
289
- if (pr->request.buffer_pos + parser->state.content_length <
290
- HTTP1_MAX_HEADER_SIZE) {
300
+ if (parser->state.content_length > 0 &&
301
+ (pr->request.buffer_pos + parser->state.content_length <
302
+ HTTP1_MAX_HEADER_SIZE)) {
291
303
  pr->request.request.body_str = data;
292
304
  } else {
293
305
  // create a temporary file to contain the data.
@@ -348,6 +360,12 @@ static void http1_on_data(intptr_t uuid, http1_protocol_s *pr) {
348
360
  tmp = 0;
349
361
  buffer = request->buffer + request->buffer_pos - pr->len;
350
362
  } else {
363
+ if (pr->len) {
364
+ /* protocol error, we shouldn't have letfovers during file processing */
365
+ err_bad_request(pr);
366
+ http1_on_error(&pr->parser);
367
+ return;
368
+ }
351
369
  buffer = buff;
352
370
  tmp = sock_read(uuid, buffer, HTTP_BODY_CHUNK_SIZE);
353
371
  if (tmp > 0) {
@@ -163,12 +163,34 @@ inline static int consume_header(struct http1_fio_parser_args_s *args,
163
163
  *((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")) {
164
164
  /* handle the special `content-length` header */
165
165
  args->parser->state.content_length = atol((char *)tmp);
166
+ } else if ((tmp - start) - t2 == 17 &&
167
+ *((uint64_t *)start) == *((uint64_t *)"transfer") &&
168
+ *((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin") &&
169
+ *((uint32_t *)tmp) == *((uint32_t *)"chun") &&
170
+ *((uint32_t *)(tmp + 3)) == *((uint32_t *)"nked")) {
171
+ /* handle the special `transfer-encoding: chunked` header */
172
+ args->parser->state.reserved |= 64;
173
+ } else if ((tmp - start) - t2 == 7 &&
174
+ *((uint64_t *)start) == *((uint64_t *)"trailer")) {
175
+ /* chunked data with trailer... */
176
+ args->parser->state.reserved |= 64;
177
+ args->parser->state.reserved |= 32;
166
178
  }
167
179
  #else
168
180
  if ((tmp - start) - t2 == 14 &&
169
181
  HEADER_NAME_IS_EQ((char *)start, "content-length", 14)) {
170
182
  /* handle the special `content-length` header */
171
183
  args->parser->state.content_length = atol((char *)tmp);
184
+ } else if ((tmp - start) - t2 == 17 &&
185
+ HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17) &&
186
+ memcmp(tmp, "chunked", 7)) {
187
+ /* handle the special `transfer-encoding: chunked` header */
188
+ args->parser->state.reserved |= 64;
189
+ } else if ((tmp - start) - t2 == 7 &&
190
+ HEADER_NAME_IS_EQ((char *)start, "trailer", 7)) {
191
+ /* chunked data with trailer... */
192
+ args->parser->state.reserved |= 64;
193
+ args->parser->state.reserved |= 32;
172
194
  }
173
195
  #endif
174
196
  /* perform callback */
@@ -178,25 +200,124 @@ inline static int consume_header(struct http1_fio_parser_args_s *args,
178
200
  return 0;
179
201
  }
180
202
 
203
+ /* *****************************************************************************
204
+ HTTP/1.1 Body handling
205
+ ***************************************************************************** */
206
+
207
+ inline static int consume_body_streamed(struct http1_fio_parser_args_s *args,
208
+ uint8_t **start) {
209
+ uint8_t *end =
210
+ *start + args->parser->state.content_length - args->parser->state.read;
211
+ uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
212
+ if (end > stop)
213
+ end = stop;
214
+ if (end > *start &&
215
+ args->on_body_chunk(args->parser, (char *)(*start), end - *start))
216
+ return -1;
217
+ args->parser->state.read += (end - *start);
218
+ *start = end;
219
+ if (args->parser->state.content_length <= args->parser->state.read)
220
+ args->parser->state.reserved |= 4;
221
+ return 0;
222
+ }
223
+
224
+ inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
225
+ uint8_t **start) {
226
+ uint8_t *const stop = ((uint8_t *)args->buffer) + args->length;
227
+ uint8_t *end = *start;
228
+ while (*start < stop) {
229
+ if (args->parser->state.content_length == 0) {
230
+ size_t eol_len;
231
+ /* collect chunked length */
232
+ if (!(eol_len = seek2eol(&end, stop))) {
233
+ /* requires length data to continue */
234
+ return 0;
235
+ }
236
+ /* an empty EOL is possible in mid stream processing */
237
+ if (*start + eol_len - 1 >= end && (*start = end) &&
238
+ !seek2eol(&end, stop)) {
239
+ return 0;
240
+ }
241
+ args->parser->state.content_length = 0 - strtol((char *)*start, NULL, 16);
242
+ *start = end = end + 1;
243
+ if (args->parser->state.content_length == 0) {
244
+ /* all chunked data was parsed */
245
+ args->parser->state.content_length = args->parser->state.read;
246
+ /* consume trailing EOL */
247
+ if (seek2eol(start, stop))
248
+ (*start)++;
249
+ if (args->parser->state.reserved & 32) {
250
+ /* remove the "headers complete" and "trailer" flags */
251
+ args->parser->state.reserved &= 0xDD; /* 0xDD == ~2 & ~32 & 0xFF */
252
+ return -2;
253
+ }
254
+ /* the parsing complete flag */
255
+ args->parser->state.reserved |= 4;
256
+ return 0;
257
+ }
258
+ }
259
+ end = *start + (0 - args->parser->state.content_length);
260
+ if (end > stop)
261
+ end = stop;
262
+ if (end > *start &&
263
+ args->on_body_chunk(args->parser, (char *)(*start), end - *start)) {
264
+ return -1;
265
+ }
266
+ args->parser->state.read += (end - *start);
267
+ args->parser->state.content_length += (end - *start);
268
+ *start = end;
269
+ if (args->parser->state.content_length == 0 && seek2eol(start, stop))
270
+ (*start)++;
271
+ }
272
+ return 0;
273
+ }
274
+
275
+ inline static int consume_body(struct http1_fio_parser_args_s *args,
276
+ uint8_t **start) {
277
+ if (args->parser->state.content_length > 0 &&
278
+ args->parser->state.content_length > args->parser->state.read) {
279
+ /* normal, streamed data */
280
+ return consume_body_streamed(args, start);
281
+ } else if (args->parser->state.content_length <= 0 &&
282
+ (args->parser->state.reserved & 64)) {
283
+ /* chuncked encoding */
284
+ return consume_body_chunked(args, start);
285
+ } else {
286
+ /* nothing to do - parsing complete */
287
+ args->parser->state.reserved |= 4;
288
+ }
289
+ return 0;
290
+ }
291
+
181
292
  /* *****************************************************************************
182
293
  HTTP/1.1 parsre function
183
294
  ***************************************************************************** */
295
+ #ifdef DEBUG
296
+ #include <assert.h>
297
+ #else
298
+ #define DEBUG 0
299
+ #define assert(...)
300
+ #endif
184
301
 
185
302
  size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
303
+ if (DEBUG) {
304
+ assert(args->parser && args->buffer);
305
+ }
186
306
  uint8_t *start = args->buffer;
187
307
  uint8_t *end = start;
188
308
  uint8_t *const stop = start + args->length;
189
309
  uint8_t eol_len = 0;
190
310
  #define CONSUMED ((size_t)((uintptr_t)start - (uintptr_t)args->buffer))
191
- // fprintf(stderr, "** resuming with at %p with %.*s...(%lu)\n", args->buffer,
192
- // 4,
193
- // start, args->length);
194
- switch ((args->parser->state.reserved & 31)) {
311
+ // fprintf(stderr, "** resuming with at %p with %.*s...(%lu)\n", args->buffer,
312
+ // 4,
313
+ // start, args->length);
314
+ re_eval:
315
+ switch ((args->parser->state.reserved & 15)) {
195
316
 
196
317
  /* request / response line */
197
318
  case 0:
198
319
  /* clear out any leadinng white space */
199
- while (*start == '\r' || *start == '\r' || *start == ' ' || *start == 0) {
320
+ while (*start == '\r' || *start == '\n' || *start == ' ' || *start == 0) {
200
321
  start++;
201
322
  }
202
323
  end = start;
@@ -204,11 +325,11 @@ size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
204
325
  if (!(eol_len = seek2eol(&end, stop)))
205
326
  return CONSUMED;
206
327
 
207
- if (start[0] >= '1' && start[0] <= '9') {
328
+ if (((uint32_t *)start)[0] == ((uint32_t *)"HTTP")[0]) {
208
329
  /* HTTP response */
209
330
  if (consume_response_line(args, start, end - eol_len + 1))
210
331
  goto error;
211
- } else {
332
+ } else if (tolower(start[0]) >= 'a' && tolower(start[0]) <= 'z') {
212
333
  /* HTTP request */
213
334
  if (consume_request_line(args, start, end - eol_len + 1))
214
335
  goto error;
@@ -233,44 +354,31 @@ size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
233
354
  finished_headers:
234
355
  end = start = end + 1;
235
356
  args->parser->state.reserved |= 2;
236
- if (args->parser->state.content_length == 0)
237
- goto finish;
238
- if (end >= stop)
239
- return args->length;
240
-
241
357
  /* fallthrough */
242
358
  /* request body */
243
- case 3: /* 2 | 1 == 3 */
244
- end = start + args->parser->state.content_length - args->parser->state.read;
245
- if (end > stop)
246
- end = stop;
247
- if (end == start)
248
- return CONSUMED;
249
- // fprintf(stderr, "Consuming body at (%lu/%lu):%.*s\n", end - start,
250
- // args->parser->state.content_length, (int)(end - start), start);
251
- if (args->on_body_chunk(args->parser, (char *)start, end - start))
359
+ case 3: { /* 2 | 1 == 3 */
360
+ int t3 = consume_body(args, &start);
361
+ if (t3 == -1)
252
362
  goto error;
253
- args->parser->state.read += (end - start);
254
- start = end;
255
- if (args->parser->state.content_length <= args->parser->state.read)
256
- goto finish;
257
- return CONSUMED;
363
+ if (t3 == -2)
364
+ goto re_eval;
258
365
  break;
259
366
  }
260
-
367
+ }
368
+ /* are we done ? */
369
+ if (args->parser->state.reserved & 4) {
370
+ if (((args->parser->state.reserved & 128) ? args->on_response
371
+ : args->on_request)(args->parser))
372
+ goto error;
373
+ args->parser->state =
374
+ (struct http1_parser_protected_read_only_state_s){0, 0, 0};
375
+ }
376
+ return CONSUMED;
261
377
  error:
262
378
  args->on_error(args->parser);
263
379
  args->parser->state =
264
380
  (struct http1_parser_protected_read_only_state_s){0, 0, 0};
265
381
  return args->length;
266
-
267
- finish:
268
- if (((args->parser->state.reserved & 128) ? args->on_response
269
- : args->on_request)(args->parser))
270
- goto error;
271
- args->parser->state =
272
- (struct http1_parser_protected_read_only_state_s){0, 0, 0};
273
- return CONSUMED;
274
382
  }
275
383
 
276
384
  #undef CONSUMED