iodine 0.4.19 → 0.5.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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/http1.h
CHANGED
@@ -1,52 +1,29 @@
|
|
1
1
|
/*
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
Feel free to copy, use and enjoy according to the license provided.
|
2
|
+
Copyright: Boaz Segev, 2017
|
3
|
+
License: MIT
|
6
4
|
*/
|
7
5
|
#ifndef H_HTTP1_H
|
8
6
|
#define H_HTTP1_H
|
7
|
+
|
9
8
|
#include "http.h"
|
10
9
|
|
11
|
-
#ifndef
|
10
|
+
#ifndef HTTP1_READ_BUFFER
|
12
11
|
/**
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
Defaults to ~16Kb headers per request.
|
19
|
-
*/
|
20
|
-
#define HTTP1_MAX_HEADER_SIZE (16 * 1024) /* ~16kb */
|
21
|
-
#endif
|
22
|
-
|
23
|
-
#ifndef HTTP1_MAX_HEADER_COUNT
|
24
|
-
/** Sets the maximum allowed headers per request (obligatory).
|
25
|
-
|
26
|
-
Defaults to 64 headers per request */
|
27
|
-
#define HTTP1_MAX_HEADER_COUNT (64)
|
12
|
+
* The size of a single `read` command, it sets the limit for an HTTP/1.1
|
13
|
+
* header line.
|
14
|
+
*/
|
15
|
+
#define HTTP1_READ_BUFFER (8 * 1024) /* ~8kb */
|
28
16
|
#endif
|
29
17
|
|
30
|
-
|
31
|
-
|
18
|
+
/** Creates an HTTP1 protocol object and handles any unread data in the buffer
|
19
|
+
* (if any). */
|
20
|
+
protocol_s *http1_new(uintptr_t uuid, http_settings_s *settings,
|
21
|
+
void *unread_data, size_t unread_length);
|
32
22
|
|
33
|
-
|
34
|
-
|
35
|
-
#define HTTP1_POOL_SIZE (64) /* should be ~0.5Mb with default values*/
|
36
|
-
#endif
|
37
|
-
|
38
|
-
/** The HTTP/1.1 protocol identifier. */
|
39
|
-
extern char *HTTP1_Protocol_String;
|
40
|
-
|
41
|
-
/* *****************************************************************************
|
42
|
-
HTTP listening helpers
|
43
|
-
*/
|
44
|
-
|
45
|
-
/**
|
46
|
-
Allocates memory for an upgradable HTTP/1.1 protocol.
|
23
|
+
/** Manually destroys the HTTP1 protocol object. */
|
24
|
+
void http1_destroy(protocol_s *);
|
47
25
|
|
48
|
-
|
49
|
-
|
50
|
-
protocol_s *http1_on_open(intptr_t fd, http_settings_s *settings);
|
26
|
+
/** returns the HTTP/1.1 protocol's VTable. */
|
27
|
+
void * http1_vtable(void);
|
51
28
|
|
52
29
|
#endif
|
data/ext/iodine/http1_parser.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
Copyright: Boaz
|
2
|
+
Copyright: Boaz Segev, 2017-2018
|
3
3
|
License: MIT
|
4
4
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
@@ -13,58 +13,72 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
13
13
|
#include <stdio.h>
|
14
14
|
#include <string.h>
|
15
15
|
|
16
|
-
#define PREFER_MEMCHAR 0
|
17
|
-
|
18
16
|
/* *****************************************************************************
|
19
17
|
Seeking for characters in a string
|
20
18
|
***************************************************************************** */
|
21
19
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
|
26
|
-
/* This is library based alternative that is sometimes slower */
|
27
|
-
if (*pos >= limit || **pos == ch) {
|
28
|
-
return 0;
|
29
|
-
}
|
30
|
-
uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
|
31
|
-
if (tmp) {
|
32
|
-
*pos = tmp;
|
33
|
-
*tmp = 0;
|
34
|
-
return 1;
|
35
|
-
}
|
36
|
-
*pos = limit;
|
37
|
-
return 0;
|
38
|
-
}
|
20
|
+
#ifndef ALLOW_UNALIGNED_MEMORY_ACCESS
|
21
|
+
#define ALLOW_UNALIGNED_MEMORY_ACCESS 0
|
22
|
+
#endif
|
39
23
|
|
40
|
-
#
|
24
|
+
#if FIO_MEMCHAR
|
41
25
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
26
|
+
/**
|
27
|
+
* This seems to be faster on some systems, especially for smaller distances.
|
28
|
+
*
|
29
|
+
* On newer systems, `memchr` should be faster.
|
30
|
+
*/
|
31
|
+
static inline int seek2ch(uint8_t **buffer, register uint8_t *const limit,
|
32
|
+
const uint8_t c) {
|
46
33
|
if (**buffer == c) {
|
34
|
+
#if HTTP1_PARSER_CONVERT_EOL2NUL
|
47
35
|
**buffer = 0;
|
36
|
+
#endif
|
48
37
|
return 1;
|
49
38
|
}
|
50
39
|
|
51
|
-
|
52
|
-
|
53
|
-
|
40
|
+
#if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
|
41
|
+
/* too short for this mess */
|
42
|
+
if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
|
43
|
+
goto finish;
|
54
44
|
|
55
|
-
|
56
|
-
|
57
|
-
const
|
58
|
-
|
45
|
+
/* align memory */
|
46
|
+
{
|
47
|
+
const uint8_t *alignment =
|
48
|
+
(uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
|
49
|
+
if (limit >= alignment) {
|
50
|
+
while (*buffer < alignment) {
|
51
|
+
if (**buffer == c) {
|
52
|
+
#if HTTP1_PARSER_CONVERT_EOL2NUL
|
53
|
+
**buffer = 0;
|
54
|
+
#endif
|
55
|
+
return 1;
|
56
|
+
}
|
57
|
+
*buffer += 1;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
|
62
|
+
#else
|
63
|
+
const uint8_t *limit64 = (uint8_t *)limit - 7;
|
64
|
+
#endif
|
65
|
+
uint64_t wanted1 = 0x0101010101010101ULL * c;
|
66
|
+
for (; *buffer < limit64; *buffer += 8) {
|
67
|
+
const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
|
68
|
+
const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
|
69
|
+
const uint64_t t1 = (eq1 & 0x8080808080808080llu);
|
59
70
|
if ((t0 & t1)) {
|
60
71
|
break;
|
61
72
|
}
|
62
73
|
}
|
63
|
-
|
64
|
-
|
74
|
+
#if !defined(__x86_64__)
|
75
|
+
finish:
|
76
|
+
#endif
|
65
77
|
while (*buffer < limit) {
|
66
78
|
if (**buffer == c) {
|
79
|
+
#if HTTP1_PARSER_CONVERT_EOL2NUL
|
67
80
|
**buffer = 0;
|
81
|
+
#endif
|
68
82
|
return 1;
|
69
83
|
}
|
70
84
|
(*buffer)++;
|
@@ -72,6 +86,26 @@ static inline uint8_t seek2ch(uint8_t **buffer, const uint8_t *const limit,
|
|
72
86
|
return 0;
|
73
87
|
}
|
74
88
|
|
89
|
+
#else
|
90
|
+
|
91
|
+
/* a helper that seeks any char, converts it to NUL and returns 1 if found. */
|
92
|
+
inline static uint8_t seek2ch(uint8_t **pos, uint8_t *const limit, uint8_t ch) {
|
93
|
+
/* This is library based alternative that is sometimes slower */
|
94
|
+
if (*pos >= limit || **pos == ch) {
|
95
|
+
return 0;
|
96
|
+
}
|
97
|
+
uint8_t *tmp = memchr(*pos, ch, limit - (*pos));
|
98
|
+
if (tmp) {
|
99
|
+
*pos = tmp;
|
100
|
+
#if HTTP1_PARSER_CONVERT_EOL2NUL
|
101
|
+
*tmp = 0;
|
102
|
+
#endif
|
103
|
+
return 1;
|
104
|
+
}
|
105
|
+
*pos = limit;
|
106
|
+
return 0;
|
107
|
+
}
|
108
|
+
|
75
109
|
#endif
|
76
110
|
|
77
111
|
/* a helper that seeks the EOL, converts it to NUL and returns it's length */
|
@@ -80,7 +114,9 @@ inline static uint8_t seek2eol(uint8_t **pos, uint8_t *const limit) {
|
|
80
114
|
if (!seek2ch(pos, limit, '\n'))
|
81
115
|
return 0;
|
82
116
|
if ((*pos)[-1] == '\r') {
|
117
|
+
#if HTTP1_PARSER_CONVERT_EOL2NUL
|
83
118
|
(*pos)[-1] = 0;
|
119
|
+
#endif
|
84
120
|
return 2;
|
85
121
|
}
|
86
122
|
return 1;
|
@@ -110,11 +146,37 @@ inline static int consume_response_line(struct http1_fio_parser_args_s *args,
|
|
110
146
|
inline static int consume_request_line(struct http1_fio_parser_args_s *args,
|
111
147
|
uint8_t *start, uint8_t *end) {
|
112
148
|
uint8_t *tmp = start;
|
149
|
+
uint8_t *host_start = NULL;
|
150
|
+
uint8_t *host_end = NULL;
|
113
151
|
if (!seek2ch(&tmp, end, ' '))
|
114
152
|
return -1;
|
115
153
|
if (args->on_method(args->parser, (char *)start, tmp - start))
|
116
154
|
return -1;
|
117
155
|
tmp = start = tmp + 1;
|
156
|
+
if (start[0] == 'h' && start[1] == 't' && start[2] == 't' &&
|
157
|
+
start[3] == 'p') {
|
158
|
+
if (start[4] == ':' && start[5] == '/' && start[6] == '/') {
|
159
|
+
/* Request URI is in long form... emulate Host header instead. */
|
160
|
+
tmp = host_end = host_start = (start += 7);
|
161
|
+
} else if (start[4] == 's' && start[5] == ':' && start[6] == '/' &&
|
162
|
+
start[7] == '/') {
|
163
|
+
/* Secure request is in long form... emulate Host header instead. */
|
164
|
+
tmp = host_end = host_start = (start += 8);
|
165
|
+
} else
|
166
|
+
goto review_path;
|
167
|
+
if (!seek2ch(&tmp, end, ' '))
|
168
|
+
return -1;
|
169
|
+
*tmp = ' ';
|
170
|
+
if (!seek2ch(&host_end, tmp, '/')) {
|
171
|
+
if (args->on_path(args->parser, "/", 1))
|
172
|
+
return -1;
|
173
|
+
goto start_version;
|
174
|
+
}
|
175
|
+
host_end[0] = '/';
|
176
|
+
start = host_end;
|
177
|
+
}
|
178
|
+
review_path:
|
179
|
+
tmp = start;
|
118
180
|
if (seek2ch(&tmp, end, '?')) {
|
119
181
|
if (args->on_path(args->parser, (char *)start, tmp - start))
|
120
182
|
return -1;
|
@@ -131,62 +193,65 @@ inline static int consume_request_line(struct http1_fio_parser_args_s *args,
|
|
131
193
|
if (args->on_path(args->parser, (char *)start, tmp - start))
|
132
194
|
return -1;
|
133
195
|
}
|
196
|
+
start_version:
|
134
197
|
start = tmp + 1;
|
135
|
-
if (start +
|
198
|
+
if (start + 5 >= end) /* require "HTTP/" */
|
136
199
|
return -1;
|
137
200
|
if (args->on_http_version(args->parser, (char *)start, end - start))
|
138
201
|
return -1;
|
202
|
+
/* */
|
203
|
+
if (host_start && args->on_header(args->parser, "host", 4, (char *)host_start,
|
204
|
+
host_end - host_start))
|
205
|
+
return -1;
|
139
206
|
return 0;
|
140
207
|
}
|
141
208
|
|
142
209
|
inline static int consume_header(struct http1_fio_parser_args_s *args,
|
143
210
|
uint8_t *start, uint8_t *end) {
|
144
|
-
uint8_t
|
145
|
-
uint8_t *tmp = start;
|
211
|
+
uint8_t *end_name = start;
|
146
212
|
/* divide header name from data */
|
147
|
-
if (!seek2ch(&
|
213
|
+
if (!seek2ch(&end_name, end, ':'))
|
148
214
|
return -1;
|
149
215
|
#if HTTP_HEADERS_LOWERCASE
|
150
|
-
for (uint8_t *
|
151
|
-
*
|
216
|
+
for (uint8_t *t = start; t < end_name; t++) {
|
217
|
+
*t = tolower(*t);
|
152
218
|
}
|
153
219
|
#endif
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
tmp++;
|
158
|
-
t2++;
|
220
|
+
uint8_t *start_value = end_name + 1;
|
221
|
+
if (start_value[0] == ' ') {
|
222
|
+
start_value++;
|
159
223
|
};
|
160
|
-
#if HTTP_HEADERS_LOWERCASE
|
161
|
-
|
224
|
+
#if ALLOW_UNALIGNED_MEMORY_ACCESS && HTTP_HEADERS_LOWERCASE
|
225
|
+
/* enable this section to test unaligned memory access */
|
226
|
+
if ((end_name - start) == 14 &&
|
162
227
|
*((uint64_t *)start) == *((uint64_t *)"content-") &&
|
163
228
|
*((uint64_t *)(start + 6)) == *((uint64_t *)"t-length")) {
|
164
229
|
/* handle the special `content-length` header */
|
165
|
-
args->parser->state.content_length = atol((char *)
|
166
|
-
} else if ((
|
230
|
+
args->parser->state.content_length = atol((char *)start_value);
|
231
|
+
} else if ((end_name - start) == 17 &&
|
167
232
|
*((uint64_t *)start) == *((uint64_t *)"transfer") &&
|
168
233
|
*((uint64_t *)(start + 8)) == *((uint64_t *)"-encodin") &&
|
169
|
-
*((uint32_t *)
|
170
|
-
*((uint32_t *)(
|
234
|
+
*((uint32_t *)start_value) == *((uint32_t *)"chun") &&
|
235
|
+
*((uint32_t *)(start_value + 3)) == *((uint32_t *)"nked")) {
|
171
236
|
/* handle the special `transfer-encoding: chunked` header */
|
172
237
|
args->parser->state.reserved |= 64;
|
173
|
-
} else if ((
|
238
|
+
} else if ((end_name - start) == 7 &&
|
174
239
|
*((uint64_t *)start) == *((uint64_t *)"trailer")) {
|
175
240
|
/* chunked data with trailer... */
|
176
241
|
args->parser->state.reserved |= 64;
|
177
242
|
args->parser->state.reserved |= 32;
|
178
243
|
}
|
179
244
|
#else
|
180
|
-
if ((
|
245
|
+
if ((end_name - start) == 14 &&
|
181
246
|
HEADER_NAME_IS_EQ((char *)start, "content-length", 14)) {
|
182
247
|
/* handle the special `content-length` header */
|
183
|
-
args->parser->state.content_length = atol((char *)
|
184
|
-
} else if ((
|
248
|
+
args->parser->state.content_length = atol((char *)start_value);
|
249
|
+
} else if ((end_name - start) == 17 &&
|
185
250
|
HEADER_NAME_IS_EQ((char *)start, "transfer-encoding", 17) &&
|
186
|
-
memcmp(
|
251
|
+
memcmp(start_value, "chunked", 7)) {
|
187
252
|
/* handle the special `transfer-encoding: chunked` header */
|
188
253
|
args->parser->state.reserved |= 64;
|
189
|
-
} else if ((
|
254
|
+
} else if ((end_name - start) == 7 &&
|
190
255
|
HEADER_NAME_IS_EQ((char *)start, "trailer", 7)) {
|
191
256
|
/* chunked data with trailer... */
|
192
257
|
args->parser->state.reserved |= 64;
|
@@ -194,8 +259,8 @@ inline static int consume_header(struct http1_fio_parser_args_s *args,
|
|
194
259
|
}
|
195
260
|
#endif
|
196
261
|
/* perform callback */
|
197
|
-
if (args->on_header(args->parser, (char *)start, (
|
198
|
-
(char *)
|
262
|
+
if (args->on_header(args->parser, (char *)start, (end_name - start),
|
263
|
+
(char *)start_value, end - start_value))
|
199
264
|
return -1;
|
200
265
|
return 0;
|
201
266
|
}
|
@@ -228,14 +293,16 @@ inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
|
|
228
293
|
while (*start < stop) {
|
229
294
|
if (args->parser->state.content_length == 0) {
|
230
295
|
size_t eol_len;
|
296
|
+
/* consume seperator */
|
297
|
+
while (*start < stop && (**start == '\n' || **start == '\r'))
|
298
|
+
++(*start);
|
231
299
|
/* collect chunked length */
|
232
300
|
if (!(eol_len = seek2eol(&end, stop))) {
|
233
301
|
/* requires length data to continue */
|
234
302
|
return 0;
|
235
303
|
}
|
236
304
|
/* an empty EOL is possible in mid stream processing */
|
237
|
-
if (*start + eol_len
|
238
|
-
!seek2eol(&end, stop)) {
|
305
|
+
if (*start + eol_len > end && (*start = end) && !seek2eol(&end, stop)) {
|
239
306
|
return 0;
|
240
307
|
}
|
241
308
|
args->parser->state.content_length = 0 - strtol((char *)*start, NULL, 16);
|
@@ -266,8 +333,6 @@ inline static int consume_body_chunked(struct http1_fio_parser_args_s *args,
|
|
266
333
|
args->parser->state.read += (end - *start);
|
267
334
|
args->parser->state.content_length += (end - *start);
|
268
335
|
*start = end;
|
269
|
-
if (args->parser->state.content_length == 0 && seek2eol(start, stop))
|
270
|
-
(*start)++;
|
271
336
|
}
|
272
337
|
return 0;
|
273
338
|
}
|
@@ -300,9 +365,8 @@ HTTP/1.1 parsre function
|
|
300
365
|
#endif
|
301
366
|
|
302
367
|
size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args) {
|
303
|
-
|
304
|
-
|
305
|
-
}
|
368
|
+
assert(args->parser && args->buffer);
|
369
|
+
args->parser->state.next = NULL;
|
306
370
|
uint8_t *start = args->buffer;
|
307
371
|
uint8_t *end = start;
|
308
372
|
uint8_t *const stop = start + args->length;
|
@@ -325,7 +389,8 @@ re_eval:
|
|
325
389
|
if (!(eol_len = seek2eol(&end, stop)))
|
326
390
|
return CONSUMED;
|
327
391
|
|
328
|
-
if (
|
392
|
+
if (start[0] == 'H' && start[1] == 'T' && start[2] == 'T' &&
|
393
|
+
start[3] == 'P') {
|
329
394
|
/* HTTP response */
|
330
395
|
if (consume_response_line(args, start, end - eol_len + 1))
|
331
396
|
goto error;
|
@@ -341,32 +406,39 @@ re_eval:
|
|
341
406
|
/* headers */
|
342
407
|
case 1:
|
343
408
|
do {
|
409
|
+
if (start + 1 >= stop)
|
410
|
+
return CONSUMED; /* buffer ended on header line */
|
411
|
+
if (*start == '\r' || *start == '\n') {
|
412
|
+
goto finished_headers; /* empty line, end of headers */
|
413
|
+
}
|
344
414
|
if (!(eol_len = seek2eol(&end, stop)))
|
345
415
|
return CONSUMED;
|
346
|
-
/* test for header ending */
|
347
|
-
if (*start == 0)
|
348
|
-
goto finished_headers; /* break the do..while loop, not the switch
|
349
|
-
statement */
|
350
416
|
if (consume_header(args, start, end - eol_len + 1))
|
351
417
|
goto error;
|
352
418
|
end = start = end + 1;
|
353
419
|
} while ((args->parser->state.reserved & 2) == 0);
|
354
420
|
finished_headers:
|
355
|
-
|
421
|
+
++start;
|
422
|
+
if (*start == '\n')
|
423
|
+
++start;
|
424
|
+
end = start;
|
356
425
|
args->parser->state.reserved |= 2;
|
357
426
|
/* fallthrough */
|
358
427
|
/* request body */
|
359
428
|
case 3: { /* 2 | 1 == 3 */
|
360
429
|
int t3 = consume_body(args, &start);
|
361
|
-
|
430
|
+
switch (t3) {
|
431
|
+
case -1:
|
362
432
|
goto error;
|
363
|
-
|
433
|
+
case -2:
|
364
434
|
goto re_eval;
|
435
|
+
}
|
365
436
|
break;
|
366
437
|
}
|
367
438
|
}
|
368
439
|
/* are we done ? */
|
369
440
|
if (args->parser->state.reserved & 4) {
|
441
|
+
args->parser->state.next = start;
|
370
442
|
if (((args->parser->state.reserved & 128) ? args->on_response
|
371
443
|
: args->on_request)(args->parser))
|
372
444
|
goto error;
|
data/ext/iodine/http1_parser.h
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#ifndef H_HTTP1_PARSER_H
|
2
2
|
/*
|
3
|
-
Copyright: Boaz
|
3
|
+
Copyright: Boaz Segev, 2017-2018
|
4
4
|
License: MIT
|
5
5
|
|
6
6
|
Feel free to copy, use and enjoy according to the license provided.
|
@@ -21,11 +21,19 @@ to maintain and that could be used for an HTTP/1.x client as well.
|
|
21
21
|
#include <sys/types.h>
|
22
22
|
|
23
23
|
#ifndef HTTP_HEADERS_LOWERCASE
|
24
|
-
/**
|
25
|
-
*
|
24
|
+
/**
|
25
|
+
* when defined, HTTP headers will be converted to lowercase and header
|
26
|
+
* searches will be case sensitive.
|
27
|
+
*
|
28
|
+
* this is required by facil.io and helps with HTTP/2 compatibility.
|
29
|
+
*/
|
26
30
|
#define HTTP_HEADERS_LOWERCASE 1
|
27
31
|
#endif
|
28
32
|
|
33
|
+
#ifndef HTTP1_PARSER_CONVERT_EOL2NUL
|
34
|
+
#define HTTP1_PARSER_CONVERT_EOL2NUL 0
|
35
|
+
#endif
|
36
|
+
|
29
37
|
#if HTTP_HEADERS_LOWERCASE
|
30
38
|
|
31
39
|
#define HEADER_NAME_IS_EQ(var_name, const_name, len) \
|
@@ -41,7 +49,8 @@ typedef struct http1_parser_s {
|
|
41
49
|
struct http1_parser_protected_read_only_state_s {
|
42
50
|
ssize_t content_length; /* negative values indicate chuncked data state */
|
43
51
|
ssize_t read; /* total number of bytes read so far (body only) */
|
44
|
-
uint8_t
|
52
|
+
uint8_t *next; /* the known position for the end of request/response */
|
53
|
+
uint8_t reserved; /* for internal use */
|
45
54
|
} state;
|
46
55
|
} http1_parser_s;
|
47
56
|
|
@@ -104,6 +113,8 @@ size_t http1_fio_parser_fn(struct http1_fio_parser_args_s *args);
|
|
104
113
|
|
105
114
|
static inline __attribute__((unused)) size_t
|
106
115
|
http1_fio_parser(struct http1_fio_parser_args_s args) {
|
116
|
+
if (!args.length)
|
117
|
+
return 0;
|
107
118
|
return http1_fio_parser_fn(&args);
|
108
119
|
}
|
109
120
|
#if __STDC_VERSION__ >= 199901L
|