iodine 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.4.7'.freeze
2
+ VERSION = '0.4.8'.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.4.7
4
+ version: 0.4.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-20 00:00:00.000000000 Z
11
+ date: 2017-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -148,12 +148,12 @@ files:
148
148
  - ext/iodine/http.h
149
149
  - ext/iodine/http1.c
150
150
  - ext/iodine/http1.h
151
+ - ext/iodine/http1_parser.c
152
+ - ext/iodine/http1_parser.h
151
153
  - ext/iodine/http1_request.c
152
154
  - ext/iodine/http1_request.h
153
155
  - ext/iodine/http1_response.c
154
156
  - ext/iodine/http1_response.h
155
- - ext/iodine/http1_simple_parser.c
156
- - ext/iodine/http1_simple_parser.h
157
157
  - ext/iodine/http_request.c
158
158
  - ext/iodine/http_request.h
159
159
  - ext/iodine/http_response.c
@@ -1,496 +0,0 @@
1
- /*
2
- copyright: Boaz segev, 2016-2017
3
- license: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
- #define _GNU_SOURCE
8
- #include "http1_simple_parser.h"
9
- #include <stdlib.h>
10
- #include <string.h>
11
- #include <strings.h>
12
-
13
- #ifdef __has_include
14
- #if __has_include(<x86intrin.h>)
15
- #include <x86intrin.h>
16
- #define HAVE_X86Intrin
17
- // see:
18
- // https://software.intel.com/en-us/node/513411
19
- // quick reference:
20
- // https://software.intel.com/sites/landingpage/IntrinsicsGuide/
21
- // pdf guide:
22
- // https://software.intel.com/sites/default/files/a6/22/18072-347603.pdf
23
- #endif
24
- #endif
25
-
26
- /* *****************************************************************************
27
- Useful macros an helpers
28
- */
29
-
30
- /*
31
- #define a2i(a) \
32
- (((a) >= '0' && a <= '9') ? ((a) - '0') : ({ \
33
- return -1; \
34
- 0; \
35
- }))
36
- */
37
-
38
- #define CHECK_END() \
39
- { \
40
- request->udata = pos; \
41
- if (pos >= end) { \
42
- return -2; \
43
- } \
44
- }
45
-
46
- #define EAT_EOL() \
47
- { \
48
- if (*pos == '\r' || *pos == 0) \
49
- *(pos++) = 0; \
50
- if (*pos == '\n' || *pos == 0) \
51
- *(pos++) = 0; \
52
- }
53
- #undef EAT_EOL
54
- #define EAT_EOL() \
55
- { \
56
- *(pos++) = 0; \
57
- *(pos++) = 0; \
58
- }
59
-
60
- static inline char *seek_to_char(char *start, char *end, char tok) {
61
- while (start < end) {
62
- if (*start == tok)
63
- return start;
64
- ++start;
65
- }
66
- return NULL;
67
- }
68
- /*
69
- #define seek_to_char(start, end, tok) memchr(start, tok, (end) - (start))
70
- */
71
-
72
- static inline char *seek_to_2eol(char *start, char *end) {
73
- char *ret = seek_to_char(start, end, '\n');
74
- return ret ? (*(ret - 1) == '\r' ? ret - 1 : ret) : NULL;
75
- // while (start < end) {
76
- // if ((*start == '\r' && *(start + 1) == '\n') || *start == '\n')
77
- // return start;
78
- // ++start;
79
- // }
80
- // return NULL;
81
- }
82
-
83
- #define HOST "host"
84
- #define CONTENT_TYPE "content-type"
85
- #define CONTENT_LENGTH "content-length"
86
- #define UPGRADE "upgrade"
87
- #define CONNECTION "connection"
88
-
89
- #if defined(HTTP_HEADERS_LOWERCASE) && HTTP_HEADERS_LOWERCASE == 1
90
- /* header is lowercase */
91
-
92
- #define to_lower(c) \
93
- if ((c) >= 'A' && (c) <= 'Z') \
94
- (c) |= 32;
95
-
96
- /* reviews the latest header and updates any required data in the request
97
- * structure. */
98
- static inline ssize_t review_header_data(http_request_s *request,
99
- http_header_s *header) {
100
- if (header->name_len == 4 &&
101
- *((uint32_t *)header->name) == *((uint32_t *)HOST)) { // exact match
102
- request->host = (void *)header->data;
103
- request->host_len = header->data_len;
104
- } else if (header->name_len == 12 &&
105
- *((uint64_t *)(header->name + 3)) ==
106
- *((uint64_t *)(CONTENT_TYPE + 3))) { // almost
107
- request->content_type = (void *)header->data;
108
- request->content_type_len = header->data_len;
109
- } else if (header->name_len == 14 &&
110
- *((uint64_t *)(header->name + 3)) ==
111
- *((uint64_t *)(CONTENT_LENGTH + 3))) { // close match
112
- // tmp still holds a pointer to the value
113
- size_t c_len = 0;
114
- char *tmp = (char *)header->data;
115
- while (*tmp) {
116
- if ((*tmp) >= '0' && (*tmp) <= '9')
117
- c_len = (c_len * 10) + ((*tmp) - '0');
118
- else
119
- return -1;
120
- ++tmp;
121
- };
122
- request->content_length = c_len;
123
- } else if (header->name_len == 7 &&
124
- *((uint64_t *)header->name) ==
125
- *((uint64_t *)UPGRADE)) { // matches also the NUL character
126
- request->upgrade = (void *)header->data;
127
- request->upgrade_len = header->data_len;
128
- } else if (header->name_len == 10 &&
129
- *((uint64_t *)header->name) ==
130
- *((uint64_t *)CONNECTION)) { // a close enough match
131
- request->connection = (void *)header->data;
132
- request->connection_len = header->data_len;
133
- }
134
- return 0;
135
- }
136
-
137
- #else
138
- /* unknown header case */
139
- #define to_lower(c)
140
-
141
- static inline ssize_t review_header_data(http_request_s *request,
142
- http_header_s *header) {
143
- if (header->name_len == 4 &&
144
- strncasecmp((char *)header->name, HOST, 4) == 0) {
145
- request->host = (void *)header->data;
146
- request->host_len = header->data_len;
147
- } else if (header->name_len == 12 &&
148
- strncasecmp((char *)header->name, CONTENT_TYPE, 12) == 0) {
149
- request->content_type = (void *)header->data;
150
- request->content_type_len = header->data_len;
151
- } else if (header->name_len == 14 &&
152
- strncasecmp((char *)header->name, CONTENT_LENGTH, 14) == 0) {
153
- // tmp still holds a pointer to the value
154
- size_t c_len = 0;
155
- char *tmp = (char *)header->data;
156
- while (*tmp) {
157
- if ((*tmp) >= '0' && (*tmp) <= '9')
158
- c_len = (c_len * 10) + ((*tmp) - '0');
159
- else
160
- return -1;
161
- ++tmp;
162
- };
163
- request->content_length = c_len;
164
- } else if (header->name_len == 7 &&
165
- strncasecmp((char *)header->name, UPGRADE, 7) == 0) {
166
- request->upgrade = (void *)header->data;
167
- request->upgrade_len = header->data_len;
168
- } else if (header->name_len == 10 &&
169
- strncasecmp((char *)header->name, CONNECTION, 10) == 0) {
170
- request->connection = (void *)header->data;
171
- request->connection_len = header->data_len;
172
- }
173
- return 0;
174
- }
175
- #endif
176
-
177
- static void no_on_header_found(http_request_s *request, http_header_s *header) {
178
- (void)request;
179
- (void)header;
180
- }
181
- /* *****************************************************************************
182
- The (public) parsing
183
- */
184
-
185
- /**
186
- Parses HTTP request headers. This allows review of the expected content
187
- length
188
- before accepting any content (server resource management).
189
-
190
- Returns the number of bytes consumed before the full request was accepted.
191
-
192
- Returns 0 if the headers were parsed and waiting on body parsing to complete.
193
- Returns -1 on fatal error (i.e. protocol error).
194
- Returns -2 when the request parsing didn't complete.
195
-
196
- Incomplete request parsing updates the content in the buffer. The same
197
- buffer
198
- and the same `http_request_s` should be returned to the parsed on the "next
199
- round", only the `len` argument is expected to grow.
200
- */
201
- ssize_t http1_parse_request_headers(
202
- void *buffer, size_t len, http_request_s *request,
203
- void (*on_header_found)(http_request_s *request, http_header_s *header)) {
204
- if (request == NULL || buffer == NULL)
205
- return -1;
206
- if (!on_header_found)
207
- on_header_found = no_on_header_found;
208
- if (request->body_str || request->body_file > 0)
209
- return 0;
210
- if (len == 0)
211
- return -2;
212
- http_header_s header;
213
- char *pos = (char *)buffer;
214
- char *end = (char *)buffer + len;
215
- char *next, *tmp;
216
- // collect method and restart parser if already collected
217
- if (request->method == NULL) {
218
- // eat empty spaces
219
- while ((*pos == '\n' || *pos == '\r') && pos < end)
220
- ++pos;
221
- CHECK_END();
222
- request->method = (char *)pos;
223
- next = seek_to_char(pos, end, ' ');
224
- if (next == NULL) {
225
- request->method = NULL;
226
- return -2;
227
- }
228
- request->method_len = (uintptr_t)next - (uintptr_t)pos;
229
- pos = next;
230
- *(pos++) = 0;
231
- CHECK_END();
232
- } else {
233
- /* use the `udata` pointer to store current position in the buffer */
234
- pos = request->udata;
235
- CHECK_END();
236
- }
237
- // collect path
238
- if (request->path == NULL) {
239
- next = seek_to_char(pos, end, ' ');
240
- if (next == NULL)
241
- return -2;
242
- request->path = (char *)pos;
243
- request->path_len = next - pos;
244
- tmp = seek_to_char(pos, next, '?');
245
- if (tmp) {
246
- request->path_len = tmp - pos;
247
- *(tmp++) = 0;
248
- request->query = (char *)tmp;
249
- request->query_len = next - tmp;
250
- }
251
- pos = next;
252
- *(pos++) = 0;
253
- CHECK_END();
254
- }
255
- // collect version
256
- if (request->version == NULL) {
257
- next = seek_to_2eol(pos, end);
258
- if (next == NULL)
259
- return -2;
260
- request->version = (char *)pos;
261
- request->version_len = (uintptr_t)next - (uintptr_t)pos;
262
- pos = next;
263
- EAT_EOL();
264
- CHECK_END();
265
- }
266
-
267
- // collect headers
268
- while (pos < end && *pos != 0) { /* NUL as term */
269
- if (request->headers_count >= HTTP1_MAX_HEADER_COUNT)
270
- return -1;
271
- next = seek_to_2eol(pos, end);
272
- if (next == NULL)
273
- return -2;
274
- if (next == pos) /*headers finished */
275
- break;
276
- #if defined(HTTP_HEADERS_LOWERCASE) && HTTP_HEADERS_LOWERCASE == 1
277
- tmp = pos;
278
- while (tmp < next && *tmp != ':') {
279
- to_lower(*tmp);
280
- ++tmp;
281
- }
282
- if (tmp == next)
283
- return -1;
284
- #else
285
- tmp = seek_to_char(pos, next, ':');
286
- if (!tmp)
287
- return -1;
288
- #endif
289
- header.name = (void *)pos;
290
- header.name_len = (uintptr_t)(tmp - pos);
291
- *(tmp++) = 0;
292
- if (*tmp == ' ')
293
- *(tmp++) = 0;
294
- header.data = (void *)tmp;
295
- header.data_len = (uintptr_t)(next - tmp);
296
- // eat EOL before content-length processing.
297
- pos = next;
298
- EAT_EOL();
299
- // print debug info
300
- // fprintf(stderr, "Got header %s (%u): %s (%u)\n", header.name,
301
- // header.name_len, header.data, header.data_len);
302
- // check special headers and assign value.
303
- review_header_data(request, &header);
304
- request->headers_count += 1;
305
- on_header_found(request, &header);
306
- // advance header position
307
- CHECK_END();
308
- }
309
- // Did we break because there's no more data or end of headers?
310
- CHECK_END();
311
- // "eat" the end of headers line and finish processing.
312
- EAT_EOL();
313
- if (request->content_length &&
314
- (end - pos) >= (ssize_t)request->content_length) {
315
- request->body_str = (void *)pos;
316
- // fprintf(stderr,
317
- // "assigning body to string. content-length %lu, buffer left: "
318
- // "%lu/%lu\n(%lu) %p:%.*s\n",
319
- // request->content_length, end - pos, len, request->content_length,
320
- // request->body_str, (int)request->content_length,
321
- // request->body_str);
322
- return (ssize_t)(pos - (char *)buffer) + request->content_length;
323
- }
324
- // we're done.
325
- return (ssize_t)(pos - (char *)buffer);
326
- }
327
-
328
- /**
329
- Parses HTTP request body content (if any).
330
-
331
- Returns the number of bytes consumed before the body consumption was complete.
332
-
333
- Returns -1 on fatal error (i.e. protocol error).
334
- Returns -2 when the request parsing didn't complete.
335
-
336
- Incomplete body parsing doesn't effect the buffer received. It is expected that
337
- the next "round" will contain fresh data in the `buffer` argument.
338
- */
339
- ssize_t http1_parse_request_body(void *buffer, size_t len,
340
- http_request_s *request) {
341
- if (request == NULL)
342
- return -1;
343
- // is body parsing needed?
344
- if (request->content_length == 0 || request->body_str)
345
- return request->content_length;
346
-
347
- if (!request->body_file) {
348
- // create a temporary file to contain the data.
349
- #ifdef P_tmpdir
350
- #if defined(__linux__) /* linux doesn't end with a divider */
351
- char template[] = P_tmpdir "/http_request_body_XXXXXXXX";
352
- #else
353
- char template[] = P_tmpdir "http_request_body_XXXXXXXX";
354
- #endif
355
- #else
356
- char template[] = "/tmp/http_request_body_XXXXXXXX";
357
- #endif
358
- request->body_file = mkstemp(template);
359
- if (request->body_file == -1)
360
- return -1;
361
- // use the `udata` field to store parser state.
362
- uintptr_t *tmp = (uintptr_t *)(&request->udata);
363
- *tmp = 0;
364
- }
365
- // make sure we have anything to read. This might be an initializing call.
366
- if (len == 0)
367
- return ((uintptr_t)(request->udata)) >= request->content_length ? 0 : (-2);
368
- // Calculate how much of the buffer should be read.
369
- ssize_t to_read =
370
- ((request->content_length - ((uintptr_t)request->udata)) < len)
371
- ? (request->content_length - ((uintptr_t)request->udata))
372
- : len;
373
- // write the data to the temporary file.
374
- if (write(request->body_file, buffer, to_read) < to_read)
375
- return -1;
376
- // update the `udata` field data with the received content length
377
- uintptr_t *tmp = (uintptr_t *)(&request->udata);
378
- *tmp += to_read; // (uintptr_t)(request->metadata.udata) += to_read;
379
-
380
- // check the state and return.
381
- if (((uintptr_t)request->udata) >= request->content_length) {
382
- lseek(request->body_file, 0, SEEK_SET);
383
- return to_read;
384
- }
385
- return -2;
386
- }
387
-
388
- #if defined(DEBUG) && DEBUG == 1
389
-
390
- // #include "http1_request.h"
391
-
392
- #include <time.h>
393
-
394
- void http1_parser_test(void) {
395
- char request_text[] = "GET /?a=b HTTP/1.1\r\n"
396
- "Host: local\r\n"
397
- "Upgrade: websocket\r\n"
398
- "Content-Length: 12\r\n"
399
- "Connection: close\r\n"
400
- "\r\n"
401
- "Hello World!\r\n";
402
- size_t request_length = sizeof(request_text) - 1;
403
- http_request_s *request = http_request_create(HTTP_V1);
404
- ssize_t ret =
405
- http1_parse_request_headers(request_text, request_length, request, NULL);
406
- if (ret == -1) {
407
- fprintf(stderr, "* Parser FAILED -1.\n");
408
- } else if (ret == -2) {
409
- fprintf(stderr, "* Parser FAILED -2.\n");
410
- } else {
411
- #define pok(true_str, false_str, result, expected) \
412
- (((result) == (expected)) ? fprintf(stderr, true_str) \
413
- : fprintf(stderr, false_str))
414
- pok("* Correct Return\n", "* WRONG Return\n", ret,
415
- sizeof(request_text) - 3);
416
- pok("* Correct Method\n", "* WRONG Method\n",
417
- strcmp(request->method, "GET"), 0);
418
- pok("* Correct Method length\n", "* WRONG Method length",
419
- request->method_len, 3);
420
- pok("* Correct path\n", "* WRONG path", strcmp(request->path, "/"), 0);
421
- pok("* Correct path length\n", "* WRONG path length", request->path_len, 1);
422
- pok("* Correct query\n", "* WRONG query", strcmp(request->query, "a=b"), 0);
423
- pok("* Correct query length\n", "* WRONG query length", request->query_len,
424
- 3);
425
- pok("* Correct host\n", "* WRONG host\n", strcmp(request->host, "local"),
426
- 0);
427
- pok("* Correct Method length\n", "* WRONG Method length\n",
428
- request->host_len, 5);
429
- pok("* Correct header count\n", "* WRONG header count\n",
430
- request->headers_count, 4);
431
- pok("* Correct content length\n", "* WRONG content length\n",
432
- request->content_length, 12);
433
- pok("* Correct body\n", "* WRONG body\n",
434
- memcmp(request->body_str, "Hello World!", request->content_length), 0);
435
- fprintf(stderr, "%.*s\n", (int)request->content_length, request->body_str);
436
- #undef pok
437
- }
438
- http_request_clear(request);
439
- clock_t start, end;
440
- start = clock();
441
- for (size_t i = 0; i < 6000000; i++) {
442
- char request_text2[] = "GET /?a=b HTTP/1.1\r\n"
443
- "Host: local\r\n"
444
- "Upgrade: websocket\r\n"
445
- "Content-Length: 12\r\n"
446
- "Connection: close\r\n"
447
- "\r\n"
448
- "Hello World!\r\n";
449
- if (http1_parse_request_headers(request_text2, request_length, request,
450
- NULL) <= 0 ||
451
- (request->upgrade_len != 9)) {
452
- fprintf(stderr, "* HTTP/1.1 simple_parser unknown error\n");
453
- return;
454
- }
455
- http_request_clear(request);
456
- }
457
- end = clock();
458
- fprintf(stderr, "7M requests in %lu cycles (%lf ms)\n", end - start,
459
- (double)(end - start) / (CLOCKS_PER_SEC / 1000));
460
- char request_text2[] = "GET /?a=b HTTP/1.1\r\n"
461
- "Host: local\r\n"
462
- "Upgrade: websocket\r\n"
463
- "Content-Length: 12\r\n"
464
- "Connection: close\r\n"
465
- "\r\n"
466
- "Hello World!\r\n";
467
- fprintf(stderr, "start\n");
468
- if (http1_parse_request_headers(request_text2, 7, request, NULL) != -2)
469
- fprintf(stderr, "Fragmented Parsing FAILED\n");
470
- fprintf(stderr, "step\n");
471
- if (http1_parse_request_headers(request_text2, 27, request, NULL) != -2)
472
- fprintf(stderr, "Fragmented Parsing FAILED\n");
473
- fprintf(stderr, "step\n");
474
- if (http1_parse_request_headers(request_text2, 38, request, NULL) != -2)
475
- fprintf(stderr, "Fragmented Parsing FAILED\n");
476
- fprintf(stderr, "step\n");
477
- if ((ret = http1_parse_request_headers(request_text2, 98, request, NULL)) !=
478
- 94)
479
- fprintf(stderr, "Fragmented Parsing (some body) FAILED\n");
480
- fprintf(stderr, "read: %lu\n", ret);
481
- if ((ret += http1_parse_request_body(request_text2 + ret,
482
- request_length - ret, request)) < 98)
483
- fprintf(stderr, "Body parsing FAILED\n");
484
- fprintf(stderr, "step\n");
485
- if (request->body_file <= 0)
486
- fprintf(stderr, "Body file FAILED\n");
487
- fprintf(stderr, "step\n");
488
- ret = read(request->body_file, request_text, request->content_length);
489
- if (ret < 0)
490
- perror("Couldn't read temporary file");
491
- fprintf(stderr, "Body:\n%.*s\n", (int)request->content_length, request_text);
492
-
493
- http_request_destroy(request);
494
- }
495
-
496
- #endif