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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +26 -0
- data/README.md +18 -12
- data/SPEC-Websocket-Draft.md +9 -5
- data/bin/ws-echo +3 -0
- data/examples/config.ru +0 -1
- data/examples/echo.ru +3 -1
- data/examples/redis.ru +0 -1
- data/ext/iodine/base64.c +97 -105
- data/ext/iodine/defer.c +16 -1
- data/ext/iodine/defer.h +10 -0
- data/ext/iodine/evio.c +35 -13
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +12 -1
- data/ext/iodine/facil.h +3 -1
- data/ext/iodine/fio2resp.c +71 -0
- data/ext/iodine/fio2resp.h +50 -0
- data/ext/iodine/fio_cli_helper.c +404 -0
- data/ext/iodine/fio_cli_helper.h +152 -0
- data/ext/iodine/fiobj.h +631 -0
- data/ext/iodine/fiobj_alloc.c +81 -0
- data/ext/iodine/fiobj_ary.c +290 -0
- data/ext/iodine/fiobj_generic.c +260 -0
- data/ext/iodine/fiobj_hash.c +447 -0
- data/ext/iodine/fiobj_io.c +58 -0
- data/ext/iodine/fiobj_json.c +779 -0
- data/ext/iodine/fiobj_misc.c +213 -0
- data/ext/iodine/fiobj_numbers.c +113 -0
- data/ext/iodine/fiobj_primitives.c +98 -0
- data/ext/iodine/fiobj_str.c +261 -0
- data/ext/iodine/fiobj_sym.c +213 -0
- data/ext/iodine/fiobj_tests.c +474 -0
- data/ext/iodine/fiobj_types.h +290 -0
- data/ext/iodine/http1.c +54 -36
- data/ext/iodine/http1_parser.c +143 -35
- data/ext/iodine/http1_parser.h +6 -3
- data/ext/iodine/http1_response.c +0 -1
- data/ext/iodine/http_response.c +1 -1
- data/ext/iodine/iodine.c +20 -4
- data/ext/iodine/iodine_protocol.c +5 -4
- data/ext/iodine/iodine_pubsub.c +1 -1
- data/ext/iodine/random.c +5 -5
- data/ext/iodine/sha1.c +5 -8
- data/ext/iodine/sha2.c +8 -11
- data/ext/iodine/sha2.h +3 -3
- data/ext/iodine/sock.c +29 -31
- data/ext/iodine/websocket_parser.h +428 -0
- data/ext/iodine/websockets.c +112 -377
- data/ext/iodine/xor-crypt.c +16 -12
- data/lib/iodine/version.rb +1 -1
- metadata +21 -3
- 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 >
|
287
|
-
|
288
|
-
|
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) {
|
data/ext/iodine/http1_parser.c
CHANGED
@@ -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
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
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 == '\
|
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]
|
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
|
-
|
245
|
-
if (
|
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
|
-
|
254
|
-
|
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
|