iodine 0.1.21 → 0.2.0

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.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
@@ -0,0 +1,56 @@
1
+ #ifndef HTTP1_H
2
+ #define HTTP1_H
3
+
4
+ #include "http.h"
5
+
6
+ #ifndef HTTP1_MAX_HEADER_SIZE
7
+ /** Sets the maximum allowed size of a requests header section
8
+ (all cookies, headers and other data that isn't the request's "body").
9
+
10
+ Defaults to ~8Kb headers per request */
11
+ #define HTTP1_MAX_HEADER_SIZE (8 * 1024) /* ~ 8kb */
12
+ #endif
13
+
14
+ #ifndef HTTP1_MAX_HEADER_COUNT
15
+ /** Sets the maximum allowed headers per request (obligatory).
16
+
17
+ Defaults to 64 headers per request */
18
+ #define HTTP1_MAX_HEADER_COUNT (64)
19
+ #endif
20
+
21
+ #ifndef HTTP1_POOL_SIZE
22
+ /** Defines the pre-allocated memory for incoming concurrent connections.
23
+
24
+ Any concurrent HTTP1 connections over this amount will be dynamically allocated.
25
+ */
26
+ #define HTTP1_POOL_SIZE (64) /* should be ~0.5Mb with default values*/
27
+ #endif
28
+
29
+ extern char* HTTP1;
30
+
31
+ /**
32
+ Allocates memory for an HTTP/1.1 protocol.
33
+
34
+ The protocol self destructs when the `on_close` callback is called.
35
+
36
+ To free the protocol manually, it's possible to call it's `on_close` callback.
37
+ */
38
+ protocol_s* http1_alloc(intptr_t fd, http_settings_s* settings);
39
+
40
+ /**
41
+ Listens for incoming HTTP/1.1 connections on the specified posrt and address,
42
+ implementing the requested settings.
43
+ */
44
+ int http1_listen(const char* port,
45
+ const char* address,
46
+ http_settings_s settings);
47
+
48
+ #define http1_listen(port, address, ...) \
49
+ http1_listen((port), (address), (http_settings_s){__VA_ARGS__})
50
+
51
+ #endif
52
+
53
+ /** Should be defined by `http.h` ... if it was removed, use a lower value. */
54
+ #ifndef HTTP_BUSY_UNLESS_HAS_FDS
55
+ #define HTTP_BUSY_UNLESS_HAS_FDS 8
56
+ #endif
@@ -0,0 +1,473 @@
1
+ #include "http1_simple_parser.h"
2
+ #include <strings.h>
3
+
4
+ #ifdef __has_include
5
+ #if __has_include(<x86intrin.h>)
6
+ #include <x86intrin.h>
7
+ #define HAVE_X86Intrin
8
+ // see:
9
+ // https://software.intel.com/en-us/node/513411
10
+ // quick reference:
11
+ // https://software.intel.com/sites/landingpage/IntrinsicsGuide/
12
+ // pdf guide:
13
+ // https://software.intel.com/sites/default/files/a6/22/18072-347603.pdf
14
+ #endif
15
+ #endif
16
+
17
+ /* *****************************************************************************
18
+ Useful macros an helpers
19
+ */
20
+
21
+ #define a2i(a) \
22
+ (((a) >= '0' && a <= '9') ? ((a) - '0') : ({ \
23
+ return -1; \
24
+ 0; \
25
+ }))
26
+
27
+ #define CHECK_END() \
28
+ { \
29
+ request->metadata.next = pos; \
30
+ if (pos >= end) { \
31
+ return -2; \
32
+ } \
33
+ }
34
+
35
+ #define EAT_EOL() \
36
+ { \
37
+ if (*pos == '\r' || *pos == 0) \
38
+ *(pos++) = 0; \
39
+ if (*pos == '\n' || *pos == 0) \
40
+ *(pos++) = 0; \
41
+ }
42
+
43
+ static inline char *seek_to_char(char *start, char *end, char tok) {
44
+ while (start < end) {
45
+ if (*start == tok)
46
+ return start;
47
+ ++start;
48
+ }
49
+ return NULL;
50
+ }
51
+ /*
52
+ #define seek_to_char(start, end, tok) memchr(start, tok, (end) - (start))
53
+ */
54
+
55
+ static inline char *seek_to_2eol(char *start, char *end) {
56
+ char *ret = seek_to_char(start, end, '\n');
57
+ return ret ? (*(ret - 1) == '\r' ? ret - 1 : ret) : NULL;
58
+ // while (start < end) {
59
+ // if ((*start == '\r' && *(start + 1) == '\n') || *start == '\n')
60
+ // return start;
61
+ // ++start;
62
+ // }
63
+ // return NULL;
64
+ }
65
+
66
+ #define HOST "host"
67
+ #define CONTENT_TYPE "content-type"
68
+ #define CONTENT_LENGTH "content-length"
69
+ #define UPGRADE "upgrade"
70
+ #define CONNECTION "connection"
71
+
72
+ #if defined(HTTP_HEADERS_LOWERCASE) && HTTP_HEADERS_LOWERCASE == 1
73
+ /* header is lowercase */
74
+
75
+ #define to_lower(c) \
76
+ if ((c) >= 'A' && (c) <= 'Z') \
77
+ (c) |= 32;
78
+
79
+ /* reviews the latest header and updates any required data in the request
80
+ * structure. */
81
+ static inline ssize_t review_header_data(http_request_s *request, char *tmp) {
82
+ // if (request->headers[request->headers_count].name_length == 4 &&
83
+ // strncmp(request->headers[request->headers_count].name, HOST, 4) == 0)
84
+ // {
85
+ // request->host = (void*)tmp;
86
+ // request->host_len =
87
+ // request->headers[request->headers_count].value_length;
88
+ // } else
89
+ if (request->headers[request->headers_count].name_length == 4 &&
90
+ *((uint32_t *)request->headers[request->headers_count].name) ==
91
+ *((uint32_t *)HOST)) { // exact match
92
+ request->host = (void *)tmp;
93
+ request->host_len = request->headers[request->headers_count].value_length;
94
+ } else if (request->headers[request->headers_count].name_length == 12 &&
95
+ *((uint64_t *)(request->headers[request->headers_count].name +
96
+ 3)) ==
97
+ *((uint64_t *)(CONTENT_TYPE + 3))) { // almost
98
+ request->content_type = (void *)tmp;
99
+ request->content_type_len =
100
+ request->headers[request->headers_count].value_length;
101
+ } else if (request->headers[request->headers_count].name_length == 14 &&
102
+ *((uint64_t *)(request->headers[request->headers_count].name +
103
+ 3)) ==
104
+ *((uint64_t *)(CONTENT_LENGTH + 3))) { // close match
105
+ // tmp still holds a pointer to the value
106
+ size_t c_len = 0;
107
+ while (*tmp) {
108
+ c_len = (c_len * 10) + a2i(*tmp);
109
+ ++tmp;
110
+ };
111
+ request->content_length = c_len;
112
+ } else if (request->headers[request->headers_count].name_length == 7 &&
113
+ *((uint64_t *)request->headers[request->headers_count].name) ==
114
+ *((uint64_t *)UPGRADE)) { // matches also the NULL character
115
+ request->upgrade = (void *)tmp;
116
+ request->upgrade_len =
117
+ request->headers[request->headers_count].value_length;
118
+ } else if (request->headers[request->headers_count].name_length == 10 &&
119
+ *((uint64_t *)request->headers[request->headers_count].name) ==
120
+ *((uint64_t *)CONNECTION)) { // a close enough match
121
+ request->connection = (void *)tmp;
122
+ request->connection_len =
123
+ request->headers[request->headers_count].value_length;
124
+ }
125
+ return 0;
126
+ }
127
+
128
+ #else
129
+ /* unknown header case */
130
+
131
+ static inline ssize_t review_header_data(http_request_s *request,
132
+ uint8_t *tmp) {
133
+ if (request->headers[request->headers_count].name_length == 4 &&
134
+ strncasecmp(request->headers[request->headers_count].name, HOST, 4) ==
135
+ 0) {
136
+ request->host = (void *)tmp;
137
+ request->host_len = request->headers[request->headers_count].value_length;
138
+ } else if (request->headers[request->headers_count].name_length == 12 &&
139
+ strncasecmp(request->headers[request->headers_count].name,
140
+ CONTENT_TYPE, 12) == 0) {
141
+ request->content_type = (void *)tmp;
142
+ request->content_type_len =
143
+ request->headers[request->headers_count].value_length;
144
+ } else if (request->headers[request->headers_count].name_length == 14 &&
145
+ strncasecmp(request->headers[request->headers_count].name,
146
+ CONTENT_LENGTH, 14) == 0) {
147
+ // tmp still holds a pointer to the value
148
+ size_t c_len = 0;
149
+ while (*tmp) {
150
+ c_len = (c_len * 10) + a2i(*tmp);
151
+ ++tmp;
152
+ };
153
+ request->content_length = c_len;
154
+ } else if (request->headers[request->headers_count].name_length == 7 &&
155
+ strncasecmp(request->headers[request->headers_count].name, UPGRADE,
156
+ 7) == 0) {
157
+ request->upgrade = (void *)tmp;
158
+ request->upgrade_len =
159
+ request->headers[request->headers_count].value_length;
160
+ } else if (request->headers[request->headers_count].name_length == 10 &&
161
+ strncasecmp(request->headers[request->headers_count].name,
162
+ CONNECTION, 10) == 0) {
163
+ request->connection = (void *)tmp;
164
+ request->connection_len =
165
+ request->headers[request->headers_count].value_length;
166
+ }
167
+ return 0;
168
+ }
169
+ #endif
170
+
171
+ /* *****************************************************************************
172
+ The (public) parsing
173
+ */
174
+
175
+ /**
176
+ Parses HTTP request headers. This allows review of the expected content
177
+ length
178
+ before accepting any content (server resource management).
179
+
180
+ Returns the number of bytes consumed before the full request was accepted.
181
+
182
+ Returns 0 if the headers were parsed and waiting on body parsing to complete.
183
+ Returns -1 on fatal error (i.e. protocol error).
184
+ Returns -2 when the request parsing didn't complete.
185
+
186
+ Incomplete request parsing updates the content in the buffer. The same
187
+ buffer
188
+ and the same `http_request_s` should be returned to the parsed on the "next
189
+ round", only the `len` argument is expected to grow.
190
+ */
191
+ ssize_t http1_parse_request_headers(void *buffer, size_t len,
192
+ http_request_s *request) {
193
+ if (request == NULL || buffer == NULL || request->metadata.max_headers == 0)
194
+ return -1;
195
+ if (request->body_str || request->body_file > 0)
196
+ return 0;
197
+ if (len == 0)
198
+ return -2;
199
+ char *pos = buffer;
200
+ char *end = buffer + len;
201
+ char *next, *tmp;
202
+ // collect method and restart parser if already collected
203
+ if (request->method == NULL) {
204
+ // eat empty spaces
205
+ while ((*pos == '\n' || *pos == '\r') && pos < end)
206
+ ++pos;
207
+ request->method = (char *)pos;
208
+ next = seek_to_char(pos, end, ' ');
209
+ if (next == NULL)
210
+ return -1; /* there should be a limit to all fragmentations. */
211
+ request->method_len = (uintptr_t)next - (uintptr_t)pos;
212
+ pos = next;
213
+ *(pos++) = 0;
214
+ CHECK_END();
215
+ } else {
216
+ /* use the `next` pointer to store current position in the buffer */
217
+ pos = request->metadata.next;
218
+ CHECK_END();
219
+ }
220
+ // collect path
221
+ if (request->path == NULL) {
222
+ next = seek_to_char(pos, end, ' ');
223
+ if (next == NULL)
224
+ return -2;
225
+ request->path = (char *)pos;
226
+ request->path_len = next - pos;
227
+ tmp = seek_to_char(pos, next, '?');
228
+ if (tmp) {
229
+ request->path_len = tmp - pos;
230
+ *(tmp++) = 0;
231
+ request->query = (char *)tmp;
232
+ request->query_len = next - tmp;
233
+ }
234
+ pos = next;
235
+ *(pos++) = 0;
236
+ CHECK_END();
237
+ }
238
+ // collect version
239
+ if (request->version == NULL) {
240
+ next = seek_to_2eol(pos, end);
241
+ if (next == NULL)
242
+ return -2;
243
+ request->version = (char *)pos;
244
+ request->version_len = (uintptr_t)next - (uintptr_t)pos;
245
+ pos = next;
246
+ EAT_EOL();
247
+ CHECK_END();
248
+ }
249
+ // collect headers
250
+ while (pos < end && *pos != '\n' && *pos != '\r' &&
251
+ *pos != 0) { /* NUL as term? */
252
+ if (request->headers_count >= request->metadata.max_headers)
253
+ return -1;
254
+ next = seek_to_2eol(pos, end);
255
+ if (next == NULL)
256
+ return -2;
257
+ #if defined(HTTP_HEADERS_LOWERCASE) && HTTP_HEADERS_LOWERCASE == 1
258
+ tmp = pos;
259
+ while (tmp < next && *tmp != ':') {
260
+ to_lower(*tmp);
261
+ ++tmp;
262
+ }
263
+ if (tmp == next)
264
+ return -1;
265
+ #else
266
+ tmp = seek_to_char(pos, next, ':');
267
+ if (!tmp)
268
+ return -1;
269
+ #endif
270
+ request->headers[request->headers_count].name = (void *)pos;
271
+ request->headers[request->headers_count].name_length = tmp - pos;
272
+ *(tmp++) = 0;
273
+ if (*tmp == ' ')
274
+ *(tmp++) = 0;
275
+ request->headers[request->headers_count].value = (char *)tmp;
276
+ request->headers[request->headers_count].value_length = next - tmp;
277
+ // eat EOL before content-length processing.
278
+ pos = next;
279
+ EAT_EOL();
280
+ // print debug info
281
+ // fprintf(stderr, "Got header %s (%u): %s (%u)\n",
282
+ // request->headers[request->headers_count].name,
283
+ // request->headers[request->headers_count].name_length,
284
+ // request->headers[request->headers_count].value,
285
+ // request->headers[request->headers_count].value_length);
286
+ // check special headers and assign value.
287
+ review_header_data(request, tmp);
288
+ // advance header position
289
+ request->headers_count += 1;
290
+ CHECK_END();
291
+ }
292
+ // check if the body is contained within the buffer
293
+ EAT_EOL();
294
+ if (request->content_length && (end - pos) >= request->content_length) {
295
+ request->body_str = (void *)pos;
296
+ // fprintf(stderr,
297
+ // "assigning body to string. content-length %lu, buffer left: "
298
+ // "%lu/%lu\n(%lu) %p:%.*s\n",
299
+ // request->content_length, end - pos, len, request->content_length,
300
+ // request->body_str, (int)request->content_length,
301
+ // request->body_str);
302
+ return (ssize_t)(pos - (char *)buffer) + request->content_length;
303
+ }
304
+
305
+ // we're done.
306
+ return pos - (char *)buffer;
307
+ }
308
+
309
+ /**
310
+ Parses HTTP request body content (if any).
311
+
312
+ Returns the number of bytes consumed before the body consumption was complete.
313
+
314
+ Returns -1 on fatal error (i.e. protocol error).
315
+ Returns -2 when the request parsing didn't complete.
316
+
317
+ Incomplete body parsing doesn't effect the buffer received. It is expected that
318
+ the next "round" will contain fresh data in the `buffer` argument.
319
+ */
320
+ ssize_t http1_parse_request_body(void *buffer, size_t len,
321
+ http_request_s *request) {
322
+ if (request == NULL)
323
+ return -1;
324
+ // is body parsing needed?
325
+ if (request->content_length == 0 || request->body_str)
326
+ return request->content_length;
327
+
328
+ if (!request->body_file) {
329
+ // create a temporary file to contain the data.
330
+ #ifdef P_tmpdir
331
+ #if defined(__linux__) /* linux doesn't end with a divider */
332
+ char template[] = P_tmpdir "/http_request_body_XXXXXXXX";
333
+ #else
334
+ char template[] = P_tmpdir "http_request_body_XXXXXXXX";
335
+ #endif
336
+ #else
337
+ char template[] = "/tmp/http_request_body_XXXXXXXX";
338
+ #endif
339
+ request->body_file = mkstemp(template);
340
+ if (request->body_file == -1)
341
+ return -1;
342
+ // use the `next` field to store parser state.
343
+ uintptr_t *tmp = (uintptr_t *)(&request->metadata.next);
344
+ *tmp = 0;
345
+ }
346
+ // make sure we have anything to read. This might be an initializing call.
347
+ if (len == 0)
348
+ return ((uintptr_t)(request->metadata.next)) >= request->content_length
349
+ ? 0
350
+ : (-2);
351
+ // Calculate how much of the buffer should be read.
352
+ ssize_t to_read =
353
+ ((request->content_length - ((uintptr_t)request->metadata.next)) < len)
354
+ ? (request->content_length - ((uintptr_t)request->metadata.next))
355
+ : len;
356
+ // write the data to the temporary file.
357
+ if (write(request->body_file, buffer, to_read) < to_read)
358
+ return -1;
359
+ // update the `next` field data with the received content length
360
+ uintptr_t *tmp = (uintptr_t *)(&request->metadata.next);
361
+ *tmp += to_read; // request->metadata.next += to_read;
362
+
363
+ // check the state and return.
364
+ if (((uintptr_t)request->metadata.next) >= request->content_length) {
365
+ lseek(request->body_file, 0, SEEK_SET);
366
+ return to_read;
367
+ }
368
+ return -2;
369
+ }
370
+
371
+ #if defined(DEBUG) && DEBUG == 1
372
+
373
+ #include <time.h>
374
+
375
+ void http_parser_test(void) {
376
+ char request_text[] = "GET /?a=b HTTP/1.1\r\n"
377
+ "Host: local\r\n"
378
+ "Upgrade: websocket\r\n"
379
+ "Content-Length: 12\r\n"
380
+ "Connection: close\r\n"
381
+ "\r\n"
382
+ "Hello World!\r\n";
383
+ size_t request_length = sizeof(request_text) - 1;
384
+ uint8_t request_mem[HTTP_REQUEST_SIZE(24)] = {};
385
+ http_request_s *request = (void *)request_mem;
386
+ *request = (http_request_s){.metadata.max_headers = 24};
387
+ ssize_t ret =
388
+ http1_parse_request_headers(request_text, request_length, request);
389
+ if (ret == -1) {
390
+ fprintf(stderr, "* Parser FAILED -1.\n");
391
+ } else if (ret == -2) {
392
+ fprintf(stderr, "* Parser FAILED -2.\n");
393
+ } else {
394
+ #define pok(true_str, false_str, result, expected) \
395
+ (((result) == (expected)) ? fprintf(stderr, true_str) \
396
+ : fprintf(stderr, false_str))
397
+ pok("* Correct Return\n", "* WRONG Return\n", ret,
398
+ sizeof(request_text) - 3);
399
+ pok("* Correct Method\n", "* WRONG Method\n",
400
+ strcmp(request->method, "GET"), 0);
401
+ pok("* Correct Method length\n", "* WRONG Method length",
402
+ request->method_len, 3);
403
+ pok("* Correct path\n", "* WRONG path", strcmp(request->path, "/"), 0);
404
+ pok("* Correct path length\n", "* WRONG path length", request->path_len, 1);
405
+ pok("* Correct query\n", "* WRONG query", strcmp(request->query, "a=b"), 0);
406
+ pok("* Correct query length\n", "* WRONG query length", request->query_len,
407
+ 3);
408
+ pok("* Correct host\n", "* WRONG host\n", strcmp(request->host, "local"),
409
+ 0);
410
+ pok("* Correct Method length\n", "* WRONG Method length\n",
411
+ request->host_len, 5);
412
+ pok("* Correct header count\n", "* WRONG header count\n",
413
+ request->headers_count, 4);
414
+ pok("* Correct content length\n", "* WRONG content length\n",
415
+ request->content_length, 12);
416
+ pok("* Correct body\n", "* WRONG body\n",
417
+ memcmp(request->body_str, "Hello World!", request->content_length), 0);
418
+ fprintf(stderr, "%.*s\n", (int)request->content_length, request->body_str);
419
+ #undef pok
420
+ }
421
+ http_request_clear(request);
422
+ clock_t start, end;
423
+ start = clock();
424
+ for (size_t i = 0; i < 6000000; i++) {
425
+ char request_text2[] = "GET /?a=b HTTP/1.1\r\n"
426
+ "Host: local\r\n"
427
+ "Upgrade: websocket\r\n"
428
+ "Content-Length: 12\r\n"
429
+ "Connection: close\r\n"
430
+ "\r\n"
431
+ "Hello World!\r\n";
432
+ http1_parse_request_headers(request_text2, request_length, request);
433
+ http_request_clear(request);
434
+ }
435
+ end = clock();
436
+ fprintf(stderr, "7M requests in %lu cycles (%lf ms)\n", end - start,
437
+ (double)(end - start) / (CLOCKS_PER_SEC / 1000));
438
+ char request_text2[] = "GET /?a=b HTTP/1.1\r\n"
439
+ "Host: local\r\n"
440
+ "Upgrade: websocket\r\n"
441
+ "Content-Length: 12\r\n"
442
+ "Connection: close\r\n"
443
+ "\r\n"
444
+ "Hello World!\r\n";
445
+ fprintf(stderr, "start\n");
446
+ if (http1_parse_request_headers(request_text2, 7, request) != -2)
447
+ fprintf(stderr, "Fragmented Parsing FAILED\n");
448
+ fprintf(stderr, "step\n");
449
+ if (http1_parse_request_headers(request_text2, 27, request) != -2)
450
+ fprintf(stderr, "Fragmented Parsing FAILED\n");
451
+ fprintf(stderr, "step\n");
452
+ if (http1_parse_request_headers(request_text2, 38, request) != -2)
453
+ fprintf(stderr, "Fragmented Parsing FAILED\n");
454
+ fprintf(stderr, "step\n");
455
+ if ((ret = http1_parse_request_headers(request_text2, 98, request)) != 94)
456
+ fprintf(stderr, "Fragmented Parsing (some body) FAILED\n");
457
+ fprintf(stderr, "read: %lu\n", ret);
458
+ if ((ret += http1_parse_request_body(request_text2 + ret,
459
+ request_length - ret, request)) < 98)
460
+ fprintf(stderr, "Body parsing FAILED\n");
461
+ fprintf(stderr, "step\n");
462
+ if (request->body_file <= 0)
463
+ fprintf(stderr, "Body file FAILED\n");
464
+ fprintf(stderr, "step\n");
465
+ ret = read(request->body_file, request_text, request->content_length);
466
+ if (ret < 0)
467
+ perror("Couldn't read temporary file");
468
+ fprintf(stderr, "Body:\n%.*s\n", (int)request->content_length, request_text);
469
+
470
+ http_request_clear(request);
471
+ }
472
+
473
+ #endif