iodine 0.7.60 → 0.8.0
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.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -21
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -148
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
data/ext/iodine/http1.c
DELETED
|
@@ -1,910 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright: Boaz Segev, 2017-2019
|
|
3
|
-
License: MIT
|
|
4
|
-
*/
|
|
5
|
-
#include <fio.h>
|
|
6
|
-
|
|
7
|
-
#include <http1.h>
|
|
8
|
-
#include <http1_parser.h>
|
|
9
|
-
#include <http_internal.h>
|
|
10
|
-
#include <websockets.h>
|
|
11
|
-
|
|
12
|
-
#include <fiobj.h>
|
|
13
|
-
|
|
14
|
-
#include <assert.h>
|
|
15
|
-
#include <stddef.h>
|
|
16
|
-
|
|
17
|
-
/* *****************************************************************************
|
|
18
|
-
The HTTP/1.1 Protocol Object
|
|
19
|
-
***************************************************************************** */
|
|
20
|
-
|
|
21
|
-
typedef struct http1pr_s {
|
|
22
|
-
http_fio_protocol_s p;
|
|
23
|
-
http1_parser_s parser;
|
|
24
|
-
http_s request;
|
|
25
|
-
uintptr_t buf_len;
|
|
26
|
-
uintptr_t max_header_size;
|
|
27
|
-
uintptr_t header_size;
|
|
28
|
-
uint8_t close;
|
|
29
|
-
uint8_t is_client;
|
|
30
|
-
uint8_t stop;
|
|
31
|
-
uint8_t buf[];
|
|
32
|
-
} http1pr_s;
|
|
33
|
-
|
|
34
|
-
struct http_vtable_s HTTP1_VTABLE; /* initialized later on */
|
|
35
|
-
|
|
36
|
-
/* *****************************************************************************
|
|
37
|
-
Internal Helpers
|
|
38
|
-
***************************************************************************** */
|
|
39
|
-
|
|
40
|
-
#define parser2http(x) \
|
|
41
|
-
((http1pr_s *)((uintptr_t)(x) - (uintptr_t)(&((http1pr_s *)0)->parser)))
|
|
42
|
-
|
|
43
|
-
inline static void h1_reset(http1pr_s *p) { p->header_size = 0; }
|
|
44
|
-
|
|
45
|
-
#define http1_pr2handle(pr) (((http1pr_s *)(pr))->request)
|
|
46
|
-
#define handle2pr(h) ((http1pr_s *)h->private_data.flag)
|
|
47
|
-
|
|
48
|
-
static fio_str_info_s http1pr_status2str(uintptr_t status);
|
|
49
|
-
|
|
50
|
-
/* cleanup an HTTP/1.1 handler object */
|
|
51
|
-
static inline void http1_after_finish(http_s *h) {
|
|
52
|
-
http1pr_s *p = handle2pr(h);
|
|
53
|
-
p->stop = p->stop & (~1UL);
|
|
54
|
-
if (h != &p->request) {
|
|
55
|
-
http_s_destroy(h, 0);
|
|
56
|
-
fio_free(h);
|
|
57
|
-
} else {
|
|
58
|
-
http_s_clear(h, p->p.settings->log);
|
|
59
|
-
}
|
|
60
|
-
if (p->close)
|
|
61
|
-
fio_close(p->p.uuid);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/* *****************************************************************************
|
|
65
|
-
HTTP Request / Response (Virtual) Functions
|
|
66
|
-
***************************************************************************** */
|
|
67
|
-
struct header_writer_s {
|
|
68
|
-
FIOBJ dest;
|
|
69
|
-
FIOBJ name;
|
|
70
|
-
FIOBJ value;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
static int write_header(FIOBJ o, void *w_) {
|
|
74
|
-
struct header_writer_s *w = w_;
|
|
75
|
-
if (!o)
|
|
76
|
-
return 0;
|
|
77
|
-
if (fiobj_hash_key_in_loop()) {
|
|
78
|
-
w->name = fiobj_hash_key_in_loop();
|
|
79
|
-
}
|
|
80
|
-
if (FIOBJ_TYPE_IS(o, FIOBJ_T_ARRAY)) {
|
|
81
|
-
fiobj_each1(o, 0, write_header, w);
|
|
82
|
-
return 0;
|
|
83
|
-
}
|
|
84
|
-
fio_str_info_s name = fiobj_obj2cstr(w->name);
|
|
85
|
-
fio_str_info_s str = fiobj_obj2cstr(o);
|
|
86
|
-
if (!str.data)
|
|
87
|
-
return 0;
|
|
88
|
-
// fiobj_str_capa_assert(w->dest,
|
|
89
|
-
// fiobj_obj2cstr(w->dest).len + name.len + str.len +
|
|
90
|
-
// 5);
|
|
91
|
-
fiobj_str_write(w->dest, name.data, name.len);
|
|
92
|
-
fiobj_str_write(w->dest, ":", 1);
|
|
93
|
-
fiobj_str_write(w->dest, str.data, str.len);
|
|
94
|
-
fiobj_str_write(w->dest, "\r\n", 2);
|
|
95
|
-
return 0;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
static FIOBJ headers2str(http_s *h, uintptr_t padding) {
|
|
99
|
-
if (!h->method && !!h->status_str)
|
|
100
|
-
return FIOBJ_INVALID;
|
|
101
|
-
|
|
102
|
-
static uintptr_t connection_hash;
|
|
103
|
-
if (!connection_hash)
|
|
104
|
-
connection_hash = fiobj_hash_string("connection", 10);
|
|
105
|
-
|
|
106
|
-
struct header_writer_s w;
|
|
107
|
-
{
|
|
108
|
-
const uintptr_t header_length_guess =
|
|
109
|
-
fiobj_hash_count(h->private_data.out_headers) * 64;
|
|
110
|
-
w.dest = fiobj_str_buf(header_length_guess + padding);
|
|
111
|
-
}
|
|
112
|
-
http1pr_s *p = handle2pr(h);
|
|
113
|
-
|
|
114
|
-
if (p->is_client == 0) {
|
|
115
|
-
fio_str_info_s t = http1pr_status2str(h->status);
|
|
116
|
-
fiobj_str_write(w.dest, t.data, t.len);
|
|
117
|
-
FIOBJ tmp = fiobj_hash_get2(h->private_data.out_headers, connection_hash);
|
|
118
|
-
if (tmp) {
|
|
119
|
-
t = fiobj_obj2cstr(tmp);
|
|
120
|
-
if (t.data[0] == 'c' || t.data[0] == 'C')
|
|
121
|
-
p->close = 1;
|
|
122
|
-
} else {
|
|
123
|
-
tmp = fiobj_hash_get2(h->headers, connection_hash);
|
|
124
|
-
if (tmp) {
|
|
125
|
-
t = fiobj_obj2cstr(tmp);
|
|
126
|
-
if (!t.data || !t.len || t.data[0] == 'k' || t.data[0] == 'K')
|
|
127
|
-
fiobj_str_write(w.dest, "connection:keep-alive\r\n", 23);
|
|
128
|
-
else {
|
|
129
|
-
fiobj_str_write(w.dest, "connection:close\r\n", 18);
|
|
130
|
-
p->close = 1;
|
|
131
|
-
}
|
|
132
|
-
} else {
|
|
133
|
-
t = fiobj_obj2cstr(h->version);
|
|
134
|
-
if (!p->close && t.len > 7 && t.data && t.data[5] == '1' &&
|
|
135
|
-
t.data[6] == '.' && t.data[7] == '1')
|
|
136
|
-
fiobj_str_write(w.dest, "connection:keep-alive\r\n", 23);
|
|
137
|
-
else {
|
|
138
|
-
fiobj_str_write(w.dest, "connection:close\r\n", 18);
|
|
139
|
-
p->close = 1;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
} else {
|
|
144
|
-
if (h->method) {
|
|
145
|
-
fiobj_str_join(w.dest, h->method);
|
|
146
|
-
fiobj_str_write(w.dest, " ", 1);
|
|
147
|
-
} else {
|
|
148
|
-
fiobj_str_write(w.dest, "GET ", 4);
|
|
149
|
-
}
|
|
150
|
-
fiobj_str_join(w.dest, h->path);
|
|
151
|
-
if (h->query) {
|
|
152
|
-
fiobj_str_write(w.dest, "?", 1);
|
|
153
|
-
fiobj_str_join(w.dest, h->query);
|
|
154
|
-
}
|
|
155
|
-
fiobj_str_write(w.dest, " HTTP/1.1\r\n", 11);
|
|
156
|
-
/* make sure we have a host header? */
|
|
157
|
-
static uint64_t host_hash;
|
|
158
|
-
if (!host_hash)
|
|
159
|
-
host_hash = fiobj_hash_string("host", 4);
|
|
160
|
-
FIOBJ tmp;
|
|
161
|
-
if (!fiobj_hash_get2(h->private_data.out_headers, host_hash) &&
|
|
162
|
-
(tmp = fiobj_hash_get2(h->headers, host_hash))) {
|
|
163
|
-
fiobj_str_write(w.dest, "host:", 5);
|
|
164
|
-
fiobj_str_join(w.dest, tmp);
|
|
165
|
-
fiobj_str_write(w.dest, "\r\n", 2);
|
|
166
|
-
}
|
|
167
|
-
if (!fiobj_hash_get2(h->private_data.out_headers, connection_hash))
|
|
168
|
-
fiobj_str_write(w.dest, "connection:keep-alive\r\n", 23);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
fiobj_each1(h->private_data.out_headers, 0, write_header, &w);
|
|
172
|
-
fiobj_str_write(w.dest, "\r\n", 2);
|
|
173
|
-
return w.dest;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/** Should send existing headers and data */
|
|
177
|
-
static int http1_send_body(http_s *h, void *data, uintptr_t length) {
|
|
178
|
-
|
|
179
|
-
FIOBJ packet = headers2str(h, length);
|
|
180
|
-
if (!packet) {
|
|
181
|
-
http1_after_finish(h);
|
|
182
|
-
return -1;
|
|
183
|
-
}
|
|
184
|
-
fiobj_str_write(packet, data, length);
|
|
185
|
-
fiobj_send_free((handle2pr(h)->p.uuid), packet);
|
|
186
|
-
http1_after_finish(h);
|
|
187
|
-
return 0;
|
|
188
|
-
}
|
|
189
|
-
/** Should send existing headers and file */
|
|
190
|
-
static int http1_sendfile(http_s *h, int fd, uintptr_t length,
|
|
191
|
-
uintptr_t offset) {
|
|
192
|
-
FIOBJ packet = headers2str(h, 0);
|
|
193
|
-
if (!packet) {
|
|
194
|
-
close(fd);
|
|
195
|
-
http1_after_finish(h);
|
|
196
|
-
return -1;
|
|
197
|
-
}
|
|
198
|
-
if (length < HTTP_MAX_HEADER_LENGTH) {
|
|
199
|
-
/* optimize away small files */
|
|
200
|
-
fio_str_info_s s = fiobj_obj2cstr(packet);
|
|
201
|
-
fiobj_str_capa_assert(packet, s.len + length);
|
|
202
|
-
s = fiobj_obj2cstr(packet);
|
|
203
|
-
intptr_t i = pread(fd, s.data + s.len, length, offset);
|
|
204
|
-
if (i < 0) {
|
|
205
|
-
close(fd);
|
|
206
|
-
fiobj_send_free((handle2pr(h)->p.uuid), packet);
|
|
207
|
-
fio_close((handle2pr(h)->p.uuid));
|
|
208
|
-
return -1;
|
|
209
|
-
}
|
|
210
|
-
close(fd);
|
|
211
|
-
fiobj_str_resize(packet, s.len + i);
|
|
212
|
-
fiobj_send_free((handle2pr(h)->p.uuid), packet);
|
|
213
|
-
http1_after_finish(h);
|
|
214
|
-
return 0;
|
|
215
|
-
}
|
|
216
|
-
fiobj_send_free((handle2pr(h)->p.uuid), packet);
|
|
217
|
-
fio_sendfile((handle2pr(h)->p.uuid), fd, offset, length);
|
|
218
|
-
http1_after_finish(h);
|
|
219
|
-
return 0;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** Should send existing headers or complete streaming */
|
|
223
|
-
static void htt1p_finish(http_s *h) {
|
|
224
|
-
FIOBJ packet = headers2str(h, 0);
|
|
225
|
-
if (packet)
|
|
226
|
-
fiobj_send_free((handle2pr(h)->p.uuid), packet);
|
|
227
|
-
else {
|
|
228
|
-
// fprintf(stderr, "WARNING: invalid call to `htt1p_finish`\n");
|
|
229
|
-
}
|
|
230
|
-
http1_after_finish(h);
|
|
231
|
-
}
|
|
232
|
-
/** Push for data - unsupported. */
|
|
233
|
-
static int http1_push_data(http_s *h, void *data, uintptr_t length,
|
|
234
|
-
FIOBJ mime_type) {
|
|
235
|
-
return -1;
|
|
236
|
-
(void)h;
|
|
237
|
-
(void)data;
|
|
238
|
-
(void)length;
|
|
239
|
-
(void)mime_type;
|
|
240
|
-
}
|
|
241
|
-
/** Push for files - unsupported. */
|
|
242
|
-
static int http1_push_file(http_s *h, FIOBJ filename, FIOBJ mime_type) {
|
|
243
|
-
return -1;
|
|
244
|
-
(void)h;
|
|
245
|
-
(void)filename;
|
|
246
|
-
(void)mime_type;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Called befor a pause task,
|
|
251
|
-
*/
|
|
252
|
-
static void http1_on_pause(http_s *h, http_fio_protocol_s *pr) {
|
|
253
|
-
((http1pr_s *)pr)->stop = 1;
|
|
254
|
-
fio_suspend(pr->uuid);
|
|
255
|
-
(void)h;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* called after the resume task had completed.
|
|
260
|
-
*/
|
|
261
|
-
static void http1_on_resume(http_s *h, http_fio_protocol_s *pr) {
|
|
262
|
-
if (!((http1pr_s *)pr)->stop) {
|
|
263
|
-
fio_force_event(pr->uuid, FIO_EVENT_ON_DATA);
|
|
264
|
-
}
|
|
265
|
-
(void)h;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
static intptr_t http1_hijack(http_s *h, fio_str_info_s *leftover) {
|
|
269
|
-
if (leftover) {
|
|
270
|
-
intptr_t len =
|
|
271
|
-
handle2pr(h)->buf_len -
|
|
272
|
-
(intptr_t)(handle2pr(h)->parser.state.next - handle2pr(h)->buf);
|
|
273
|
-
if (len) {
|
|
274
|
-
*leftover = (fio_str_info_s){
|
|
275
|
-
.len = len, .data = (char *)handle2pr(h)->parser.state.next};
|
|
276
|
-
} else {
|
|
277
|
-
*leftover = (fio_str_info_s){.len = 0, .data = NULL};
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
handle2pr(h)->stop = 3;
|
|
282
|
-
intptr_t uuid = handle2pr(h)->p.uuid;
|
|
283
|
-
fio_attach(uuid, NULL);
|
|
284
|
-
return uuid;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/* *****************************************************************************
|
|
288
|
-
Websockets Upgrading
|
|
289
|
-
***************************************************************************** */
|
|
290
|
-
|
|
291
|
-
static void http1_websocket_client_on_upgrade(http_s *h, char *proto,
|
|
292
|
-
size_t len) {
|
|
293
|
-
http1pr_s *p = handle2pr(h);
|
|
294
|
-
websocket_settings_s *args = h->udata;
|
|
295
|
-
const intptr_t uuid = handle2pr(h)->p.uuid;
|
|
296
|
-
http_settings_s *set = handle2pr(h)->p.settings;
|
|
297
|
-
set->udata = NULL;
|
|
298
|
-
http_finish(h);
|
|
299
|
-
p->stop = 1;
|
|
300
|
-
websocket_attach(uuid, set, args, p->parser.state.next,
|
|
301
|
-
p->buf_len - (intptr_t)(p->parser.state.next - p->buf));
|
|
302
|
-
fio_free(args);
|
|
303
|
-
(void)proto;
|
|
304
|
-
(void)len;
|
|
305
|
-
}
|
|
306
|
-
static void http1_websocket_client_on_failed(http_s *h) {
|
|
307
|
-
websocket_settings_s *s = h->udata;
|
|
308
|
-
if (s->on_close)
|
|
309
|
-
s->on_close(0, s->udata);
|
|
310
|
-
fio_free(h->udata);
|
|
311
|
-
h->udata = http_settings(h)->udata = NULL;
|
|
312
|
-
}
|
|
313
|
-
static void http1_websocket_client_on_hangup(http_settings_s *settings) {
|
|
314
|
-
websocket_settings_s *s = settings->udata;
|
|
315
|
-
if (s) {
|
|
316
|
-
if (s->on_close)
|
|
317
|
-
s->on_close(0, s->udata);
|
|
318
|
-
fio_free(settings->udata);
|
|
319
|
-
settings->udata = NULL;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
static int http1_http2websocket_server(http_s *h, websocket_settings_s *args) {
|
|
324
|
-
// A static data used for all websocket connections.
|
|
325
|
-
static char ws_key_accpt_str[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
326
|
-
static uintptr_t sec_version = 0;
|
|
327
|
-
static uintptr_t sec_key = 0;
|
|
328
|
-
if (!sec_version)
|
|
329
|
-
sec_version = fiobj_hash_string("sec-websocket-version", 21);
|
|
330
|
-
if (!sec_key)
|
|
331
|
-
sec_key = fiobj_hash_string("sec-websocket-key", 17);
|
|
332
|
-
|
|
333
|
-
FIOBJ tmp = fiobj_hash_get2(h->headers, sec_version);
|
|
334
|
-
if (!tmp)
|
|
335
|
-
goto bad_request;
|
|
336
|
-
fio_str_info_s stmp = fiobj_obj2cstr(tmp);
|
|
337
|
-
if (stmp.len != 2 || stmp.data[0] != '1' || stmp.data[1] != '3')
|
|
338
|
-
goto bad_request;
|
|
339
|
-
|
|
340
|
-
tmp = fiobj_hash_get2(h->headers, sec_key);
|
|
341
|
-
if (!tmp)
|
|
342
|
-
goto bad_request;
|
|
343
|
-
stmp = fiobj_obj2cstr(tmp);
|
|
344
|
-
|
|
345
|
-
fio_sha1_s sha1 = fio_sha1_init();
|
|
346
|
-
fio_sha1_write(&sha1, stmp.data, stmp.len);
|
|
347
|
-
fio_sha1_write(&sha1, ws_key_accpt_str, sizeof(ws_key_accpt_str) - 1);
|
|
348
|
-
tmp = fiobj_str_buf(32);
|
|
349
|
-
stmp = fiobj_obj2cstr(tmp);
|
|
350
|
-
fiobj_str_resize(tmp,
|
|
351
|
-
fio_base64_encode(stmp.data, fio_sha1_result(&sha1), 20));
|
|
352
|
-
http_set_header(h, HTTP_HEADER_CONNECTION, fiobj_dup(HTTP_HVALUE_WS_UPGRADE));
|
|
353
|
-
http_set_header(h, HTTP_HEADER_UPGRADE, fiobj_dup(HTTP_HVALUE_WEBSOCKET));
|
|
354
|
-
http_set_header(h, HTTP_HEADER_WS_SEC_KEY, tmp);
|
|
355
|
-
h->status = 101;
|
|
356
|
-
http1pr_s *pr = handle2pr(h);
|
|
357
|
-
const intptr_t uuid = handle2pr(h)->p.uuid;
|
|
358
|
-
http_settings_s *set = handle2pr(h)->p.settings;
|
|
359
|
-
http_finish(h);
|
|
360
|
-
pr->stop = 1;
|
|
361
|
-
websocket_attach(uuid, set, args, pr->parser.state.next,
|
|
362
|
-
pr->buf_len - (intptr_t)(pr->parser.state.next - pr->buf));
|
|
363
|
-
return 0;
|
|
364
|
-
bad_request:
|
|
365
|
-
http_send_error(h, 400);
|
|
366
|
-
if (args->on_close)
|
|
367
|
-
args->on_close(0, args->udata);
|
|
368
|
-
return -1;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
static int http1_http2websocket_client(http_s *h, websocket_settings_s *args) {
|
|
372
|
-
http1pr_s *p = handle2pr(h);
|
|
373
|
-
/* We're done with the HTTP stage, so we call the `on_finish` */
|
|
374
|
-
if (p->p.settings->on_finish)
|
|
375
|
-
p->p.settings->on_finish(p->p.settings);
|
|
376
|
-
/* Copy the Websocket setting arguments to the HTTP settings `udata` */
|
|
377
|
-
p->p.settings->udata = fio_malloc(sizeof(*args));
|
|
378
|
-
FIO_ASSERT_ALLOC(p->p.settings->udata);
|
|
379
|
-
((websocket_settings_s *)(p->p.settings->udata))[0] = *args;
|
|
380
|
-
/* Set callbacks */
|
|
381
|
-
p->p.settings->on_finish = http1_websocket_client_on_hangup; /* unknown */
|
|
382
|
-
p->p.settings->on_upgrade = http1_websocket_client_on_upgrade; /* sucess */
|
|
383
|
-
p->p.settings->on_response = http1_websocket_client_on_failed; /* failed */
|
|
384
|
-
p->p.settings->on_request = http1_websocket_client_on_failed; /* failed */
|
|
385
|
-
/* Set headers */
|
|
386
|
-
http_set_header(h, HTTP_HEADER_CONNECTION, fiobj_dup(HTTP_HVALUE_WS_UPGRADE));
|
|
387
|
-
http_set_header(h, HTTP_HEADER_UPGRADE, fiobj_dup(HTTP_HVALUE_WEBSOCKET));
|
|
388
|
-
http_set_header(h, HTTP_HVALUE_WS_SEC_VERSION,
|
|
389
|
-
fiobj_dup(HTTP_HVALUE_WS_VERSION));
|
|
390
|
-
|
|
391
|
-
/* we don't set the Origin header since we're not a browser... should we? */
|
|
392
|
-
// http_set_header(
|
|
393
|
-
// h, HTTP_HEADER_ORIGIN,
|
|
394
|
-
// fiobj_dup(fiobj_hash_get2(h->private_data.out_headers,
|
|
395
|
-
// fiobj_obj2hash(HTTP_HEADER_HOST))));
|
|
396
|
-
|
|
397
|
-
/* create nonce */
|
|
398
|
-
uint64_t key[2]; /* 16 bytes */
|
|
399
|
-
key[0] = (uintptr_t)h ^ (uint64_t)fio_last_tick().tv_sec;
|
|
400
|
-
key[1] = (uintptr_t)args->udata ^ (uint64_t)fio_last_tick().tv_nsec;
|
|
401
|
-
FIOBJ encoded = fiobj_str_buf(26); /* we need 24 really. */
|
|
402
|
-
fio_str_info_s tmp = fiobj_obj2cstr(encoded);
|
|
403
|
-
tmp.len = fio_base64_encode(tmp.data, (char *)key, 16);
|
|
404
|
-
fiobj_str_resize(encoded, tmp.len);
|
|
405
|
-
http_set_header(h, HTTP_HEADER_WS_SEC_CLIENT_KEY, encoded);
|
|
406
|
-
http_finish(h);
|
|
407
|
-
return 0;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
static int http1_http2websocket(http_s *h, websocket_settings_s *args) {
|
|
411
|
-
assert(h);
|
|
412
|
-
http1pr_s *p = handle2pr(h);
|
|
413
|
-
|
|
414
|
-
if (p->is_client == 0) {
|
|
415
|
-
return http1_http2websocket_server(h, args);
|
|
416
|
-
}
|
|
417
|
-
return http1_http2websocket_client(h, args);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/* *****************************************************************************
|
|
421
|
-
EventSource Support (SSE)
|
|
422
|
-
***************************************************************************** */
|
|
423
|
-
|
|
424
|
-
#undef http_upgrade2sse
|
|
425
|
-
|
|
426
|
-
typedef struct {
|
|
427
|
-
fio_protocol_s p;
|
|
428
|
-
http_sse_internal_s *sse;
|
|
429
|
-
} http1_sse_fio_protocol_s;
|
|
430
|
-
|
|
431
|
-
static void http1_sse_on_ready(intptr_t uuid, fio_protocol_s *p_) {
|
|
432
|
-
http1_sse_fio_protocol_s *p = (http1_sse_fio_protocol_s *)p_;
|
|
433
|
-
if (p->sse->sse.on_ready)
|
|
434
|
-
p->sse->sse.on_ready(&p->sse->sse);
|
|
435
|
-
(void)uuid;
|
|
436
|
-
}
|
|
437
|
-
static uint8_t http1_sse_on_shutdown(intptr_t uuid, fio_protocol_s *p_) {
|
|
438
|
-
http1_sse_fio_protocol_s *p = (http1_sse_fio_protocol_s *)p_;
|
|
439
|
-
if (p->sse->sse.on_shutdown)
|
|
440
|
-
p->sse->sse.on_shutdown(&p->sse->sse);
|
|
441
|
-
return 0;
|
|
442
|
-
(void)uuid;
|
|
443
|
-
}
|
|
444
|
-
static void http1_sse_on_close(intptr_t uuid, fio_protocol_s *p_) {
|
|
445
|
-
http1_sse_fio_protocol_s *p = (http1_sse_fio_protocol_s *)p_;
|
|
446
|
-
http_sse_destroy(p->sse);
|
|
447
|
-
fio_free(p);
|
|
448
|
-
(void)uuid;
|
|
449
|
-
}
|
|
450
|
-
static void http1_sse_ping(intptr_t uuid, fio_protocol_s *p_) {
|
|
451
|
-
fio_write2(uuid, .data.buffer = ": ping\n\n", .length = 8,
|
|
452
|
-
.after.dealloc = FIO_DEALLOC_NOOP);
|
|
453
|
-
(void)p_;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* Upgrades an HTTP connection to an EventSource (SSE) connection.
|
|
458
|
-
*
|
|
459
|
-
* Thie `http_s` handle will be invalid after this call.
|
|
460
|
-
*
|
|
461
|
-
* On HTTP/1.1 connections, this will preclude future requests using the same
|
|
462
|
-
* connection.
|
|
463
|
-
*/
|
|
464
|
-
static int http1_upgrade2sse(http_s *h, http_sse_s *sse) {
|
|
465
|
-
const intptr_t uuid = handle2pr(h)->p.uuid;
|
|
466
|
-
/* send response */
|
|
467
|
-
h->status = 200;
|
|
468
|
-
http_set_header(h, HTTP_HEADER_CONTENT_TYPE, fiobj_dup(HTTP_HVALUE_SSE_MIME));
|
|
469
|
-
http_set_header(h, HTTP_HEADER_CACHE_CONTROL,
|
|
470
|
-
fiobj_dup(HTTP_HVALUE_NO_CACHE));
|
|
471
|
-
http_set_header(h, HTTP_HEADER_CONTENT_ENCODING,
|
|
472
|
-
fiobj_str_new("identity", 8));
|
|
473
|
-
handle2pr(h)->stop = 1;
|
|
474
|
-
htt1p_finish(h); /* avoid the enforced content length in http_finish */
|
|
475
|
-
|
|
476
|
-
/* switch protocol to SSE */
|
|
477
|
-
http1_sse_fio_protocol_s *sse_pr = fio_malloc(sizeof(*sse_pr));
|
|
478
|
-
if (!sse_pr)
|
|
479
|
-
goto failed;
|
|
480
|
-
*sse_pr = (http1_sse_fio_protocol_s){
|
|
481
|
-
.p =
|
|
482
|
-
{
|
|
483
|
-
.on_ready = http1_sse_on_ready,
|
|
484
|
-
.on_shutdown = http1_sse_on_shutdown,
|
|
485
|
-
.on_close = http1_sse_on_close,
|
|
486
|
-
.ping = http1_sse_ping,
|
|
487
|
-
},
|
|
488
|
-
.sse = fio_malloc(sizeof(*(sse_pr->sse))),
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
if (!sse_pr->sse)
|
|
492
|
-
goto failed;
|
|
493
|
-
|
|
494
|
-
http_sse_init(sse_pr->sse, uuid, &HTTP1_VTABLE, sse);
|
|
495
|
-
fio_timeout_set(uuid, handle2pr(h)->p.settings->ws_timeout);
|
|
496
|
-
if (sse->on_open)
|
|
497
|
-
sse->on_open(&sse_pr->sse->sse);
|
|
498
|
-
fio_attach(uuid, &sse_pr->p);
|
|
499
|
-
return 0;
|
|
500
|
-
|
|
501
|
-
failed:
|
|
502
|
-
fio_close(handle2pr(h)->p.uuid);
|
|
503
|
-
if (sse->on_close)
|
|
504
|
-
sse->on_close(sse);
|
|
505
|
-
return -1;
|
|
506
|
-
(void)sse;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
#undef http_sse_write
|
|
510
|
-
/**
|
|
511
|
-
* Writes data to an EventSource (SSE) connection.
|
|
512
|
-
*
|
|
513
|
-
* See the {struct http_sse_write_args} for possible named arguments.
|
|
514
|
-
*/
|
|
515
|
-
static int http1_sse_write(http_sse_s *sse, FIOBJ str) {
|
|
516
|
-
return fiobj_send_free(((http_sse_internal_s *)sse)->uuid, str);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Closes an EventSource (SSE) connection.
|
|
521
|
-
*/
|
|
522
|
-
static int http1_sse_close(http_sse_s *sse) {
|
|
523
|
-
fio_close(((http_sse_internal_s *)sse)->uuid);
|
|
524
|
-
return 0;
|
|
525
|
-
}
|
|
526
|
-
/* *****************************************************************************
|
|
527
|
-
Virtual Table Decleration
|
|
528
|
-
***************************************************************************** */
|
|
529
|
-
|
|
530
|
-
struct http_vtable_s HTTP1_VTABLE = {
|
|
531
|
-
.http_send_body = http1_send_body,
|
|
532
|
-
.http_sendfile = http1_sendfile,
|
|
533
|
-
.http_finish = htt1p_finish,
|
|
534
|
-
.http_push_data = http1_push_data,
|
|
535
|
-
.http_push_file = http1_push_file,
|
|
536
|
-
.http_on_pause = http1_on_pause,
|
|
537
|
-
.http_on_resume = http1_on_resume,
|
|
538
|
-
.http_hijack = http1_hijack,
|
|
539
|
-
.http2websocket = http1_http2websocket,
|
|
540
|
-
.http_upgrade2sse = http1_upgrade2sse,
|
|
541
|
-
.http_sse_write = http1_sse_write,
|
|
542
|
-
.http_sse_close = http1_sse_close,
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
void *http1_vtable(void) { return (void *)&HTTP1_VTABLE; }
|
|
546
|
-
|
|
547
|
-
/* *****************************************************************************
|
|
548
|
-
Parser Callbacks
|
|
549
|
-
***************************************************************************** */
|
|
550
|
-
|
|
551
|
-
/** called when a request was received. */
|
|
552
|
-
static int http1_on_request(http1_parser_s *parser) {
|
|
553
|
-
http1pr_s *p = parser2http(parser);
|
|
554
|
-
http_on_request_handler______internal(&http1_pr2handle(p), p->p.settings);
|
|
555
|
-
if (p->request.method && !p->stop)
|
|
556
|
-
http_finish(&p->request);
|
|
557
|
-
h1_reset(p);
|
|
558
|
-
return fio_is_closed(p->p.uuid);
|
|
559
|
-
}
|
|
560
|
-
/** called when a response was received. */
|
|
561
|
-
static int http1_on_response(http1_parser_s *parser) {
|
|
562
|
-
http1pr_s *p = parser2http(parser);
|
|
563
|
-
http_on_response_handler______internal(&http1_pr2handle(p), p->p.settings);
|
|
564
|
-
if (p->request.status_str && !p->stop)
|
|
565
|
-
http_finish(&p->request);
|
|
566
|
-
h1_reset(p);
|
|
567
|
-
return fio_is_closed(p->p.uuid);
|
|
568
|
-
}
|
|
569
|
-
/** called when a request method is parsed. */
|
|
570
|
-
static int http1_on_method(http1_parser_s *parser, char *method,
|
|
571
|
-
size_t method_len) {
|
|
572
|
-
http1_pr2handle(parser2http(parser)).method =
|
|
573
|
-
fiobj_str_new(method, method_len);
|
|
574
|
-
parser2http(parser)->header_size += method_len;
|
|
575
|
-
return 0;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/** called when a response status is parsed. the status_str is the string
|
|
579
|
-
* without the prefixed numerical status indicator.*/
|
|
580
|
-
static int http1_on_status(http1_parser_s *parser, size_t status,
|
|
581
|
-
char *status_str, size_t len) {
|
|
582
|
-
http1_pr2handle(parser2http(parser)).status_str =
|
|
583
|
-
fiobj_str_new(status_str, len);
|
|
584
|
-
http1_pr2handle(parser2http(parser)).status = status;
|
|
585
|
-
parser2http(parser)->header_size += len;
|
|
586
|
-
return 0;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/** called when a request path (excluding query) is parsed. */
|
|
590
|
-
static int http1_on_path(http1_parser_s *parser, char *path, size_t len) {
|
|
591
|
-
http1_pr2handle(parser2http(parser)).path = fiobj_str_new(path, len);
|
|
592
|
-
parser2http(parser)->header_size += len;
|
|
593
|
-
return 0;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/** called when a request path (excluding query) is parsed. */
|
|
597
|
-
static int http1_on_query(http1_parser_s *parser, char *query, size_t len) {
|
|
598
|
-
http1_pr2handle(parser2http(parser)).query = fiobj_str_new(query, len);
|
|
599
|
-
parser2http(parser)->header_size += len;
|
|
600
|
-
return 0;
|
|
601
|
-
}
|
|
602
|
-
/** called when a the HTTP/1.x version is parsed. */
|
|
603
|
-
static int http1_on_version(http1_parser_s *parser, char *version, size_t len) {
|
|
604
|
-
http1_pr2handle(parser2http(parser)).version = fiobj_str_new(version, len);
|
|
605
|
-
parser2http(parser)->header_size += len;
|
|
606
|
-
/* start counting - occurs on the first line of both requests and responses */
|
|
607
|
-
#if FIO_HTTP_EXACT_LOGGING
|
|
608
|
-
clock_gettime(CLOCK_REALTIME,
|
|
609
|
-
&http1_pr2handle(parser2http(parser)).received_at);
|
|
610
|
-
#else
|
|
611
|
-
http1_pr2handle(parser2http(parser)).received_at = fio_last_tick();
|
|
612
|
-
#endif
|
|
613
|
-
return 0;
|
|
614
|
-
}
|
|
615
|
-
/** called when a header is parsed. */
|
|
616
|
-
static int http1_on_header(http1_parser_s *parser, char *name, size_t name_len,
|
|
617
|
-
char *data, size_t data_len) {
|
|
618
|
-
FIOBJ sym;
|
|
619
|
-
FIOBJ obj;
|
|
620
|
-
if (!http1_pr2handle(parser2http(parser)).headers) {
|
|
621
|
-
FIO_LOG_ERROR("(http1 parse ordering error) missing HashMap for header "
|
|
622
|
-
"%s: %s",
|
|
623
|
-
name, data);
|
|
624
|
-
http_send_error2(500, parser2http(parser)->p.uuid,
|
|
625
|
-
parser2http(parser)->p.settings);
|
|
626
|
-
return -1;
|
|
627
|
-
}
|
|
628
|
-
parser2http(parser)->header_size += name_len + data_len;
|
|
629
|
-
if (parser2http(parser)->header_size >=
|
|
630
|
-
parser2http(parser)->max_header_size ||
|
|
631
|
-
fiobj_hash_count(http1_pr2handle(parser2http(parser)).headers) >
|
|
632
|
-
HTTP_MAX_HEADER_COUNT) {
|
|
633
|
-
if (parser2http(parser)->p.settings->log) {
|
|
634
|
-
FIO_LOG_WARNING("(HTTP) security alert - header flood detected.");
|
|
635
|
-
}
|
|
636
|
-
http_send_error(&http1_pr2handle(parser2http(parser)), 413);
|
|
637
|
-
return -1;
|
|
638
|
-
}
|
|
639
|
-
sym = fiobj_str_new(name, name_len);
|
|
640
|
-
obj = fiobj_str_new(data, data_len);
|
|
641
|
-
set_header_add(http1_pr2handle(parser2http(parser)).headers, sym, obj);
|
|
642
|
-
fiobj_free(sym);
|
|
643
|
-
return 0;
|
|
644
|
-
}
|
|
645
|
-
/** called when a body chunk is parsed. */
|
|
646
|
-
static int http1_on_body_chunk(http1_parser_s *parser, char *data,
|
|
647
|
-
size_t data_len) {
|
|
648
|
-
if (parser->state.content_length >
|
|
649
|
-
(ssize_t)parser2http(parser)->p.settings->max_body_size ||
|
|
650
|
-
parser->state.read >
|
|
651
|
-
(ssize_t)parser2http(parser)->p.settings->max_body_size) {
|
|
652
|
-
http_send_error(&http1_pr2handle(parser2http(parser)), 413);
|
|
653
|
-
return -1; /* test every time, in case of chunked data */
|
|
654
|
-
}
|
|
655
|
-
if (!parser->state.read) {
|
|
656
|
-
if (parser->state.content_length > 0 &&
|
|
657
|
-
parser->state.content_length <= HTTP_MAX_HEADER_LENGTH) {
|
|
658
|
-
http1_pr2handle(parser2http(parser)).body = fiobj_data_newstr();
|
|
659
|
-
} else {
|
|
660
|
-
http1_pr2handle(parser2http(parser)).body = fiobj_data_newtmpfile();
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
fiobj_data_write(http1_pr2handle(parser2http(parser)).body, data, data_len);
|
|
664
|
-
return 0;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/** called when a protocol error occurred. */
|
|
668
|
-
static int http1_on_error(http1_parser_s *parser) {
|
|
669
|
-
if (parser2http(parser)->close)
|
|
670
|
-
return -1;
|
|
671
|
-
FIO_LOG_DEBUG("HTTP parser error.");
|
|
672
|
-
fio_close(parser2http(parser)->p.uuid);
|
|
673
|
-
return -1;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/* *****************************************************************************
|
|
677
|
-
Connection Callbacks
|
|
678
|
-
***************************************************************************** */
|
|
679
|
-
|
|
680
|
-
static inline void http1_consume_data(intptr_t uuid, http1pr_s *p) {
|
|
681
|
-
if (fio_pending(uuid) > 4) {
|
|
682
|
-
goto throttle;
|
|
683
|
-
}
|
|
684
|
-
ssize_t i = 0;
|
|
685
|
-
size_t org_len = p->buf_len;
|
|
686
|
-
int pipeline_limit = 8;
|
|
687
|
-
if (!p->buf_len)
|
|
688
|
-
return;
|
|
689
|
-
do {
|
|
690
|
-
i = http1_parse(&p->parser, p->buf + (org_len - p->buf_len), p->buf_len);
|
|
691
|
-
p->buf_len -= i;
|
|
692
|
-
--pipeline_limit;
|
|
693
|
-
} while (i && p->buf_len && pipeline_limit && !p->stop);
|
|
694
|
-
|
|
695
|
-
if (p->buf_len && org_len != p->buf_len) {
|
|
696
|
-
memmove(p->buf, p->buf + (org_len - p->buf_len), p->buf_len);
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
if (p->buf_len == HTTP_MAX_HEADER_LENGTH) {
|
|
700
|
-
/* no room to read... parser not consuming data */
|
|
701
|
-
if (p->request.method)
|
|
702
|
-
http_send_error(&p->request, 413);
|
|
703
|
-
else {
|
|
704
|
-
p->request.method = fiobj_str_tmp();
|
|
705
|
-
http_send_error(&p->request, 413);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
if (!pipeline_limit) {
|
|
710
|
-
fio_force_event(uuid, FIO_EVENT_ON_DATA);
|
|
711
|
-
}
|
|
712
|
-
return;
|
|
713
|
-
|
|
714
|
-
throttle:
|
|
715
|
-
/* throttle busy clients (slowloris) */
|
|
716
|
-
p->stop |= 4;
|
|
717
|
-
fio_suspend(uuid);
|
|
718
|
-
FIO_LOG_DEBUG("(HTTP/1,1) throttling client at %.*s",
|
|
719
|
-
(int)fio_peer_addr(uuid).len, fio_peer_addr(uuid).data);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
/** called when a data is available, but will not run concurrently */
|
|
723
|
-
static void http1_on_data(intptr_t uuid, fio_protocol_s *protocol) {
|
|
724
|
-
http1pr_s *p = (http1pr_s *)protocol;
|
|
725
|
-
if (p->stop) {
|
|
726
|
-
fio_suspend(uuid);
|
|
727
|
-
return;
|
|
728
|
-
}
|
|
729
|
-
ssize_t i = 0;
|
|
730
|
-
if (HTTP_MAX_HEADER_LENGTH - p->buf_len)
|
|
731
|
-
i = fio_read(uuid, p->buf + p->buf_len,
|
|
732
|
-
HTTP_MAX_HEADER_LENGTH - p->buf_len);
|
|
733
|
-
if (i > 0) {
|
|
734
|
-
p->buf_len += i;
|
|
735
|
-
}
|
|
736
|
-
http1_consume_data(uuid, p);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
/** called when the connection was closed, but will not run concurrently */
|
|
740
|
-
static void http1_on_close(intptr_t uuid, fio_protocol_s *protocol) {
|
|
741
|
-
http1_destroy(protocol);
|
|
742
|
-
(void)uuid;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
/** called when the connection was closed, but will not run concurrently */
|
|
746
|
-
static void http1_on_ready(intptr_t uuid, fio_protocol_s *protocol) {
|
|
747
|
-
/* resume slow clients from suspension */
|
|
748
|
-
http1pr_s *p = (http1pr_s *)protocol;
|
|
749
|
-
if (p->stop & 4) {
|
|
750
|
-
p->stop ^= 4; /* flip back the bit, so it's zero */
|
|
751
|
-
fio_force_event(uuid, FIO_EVENT_ON_DATA);
|
|
752
|
-
}
|
|
753
|
-
(void)protocol;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
/** called when a data is available for the first time */
|
|
757
|
-
static void http1_on_data_first_time(intptr_t uuid, fio_protocol_s *protocol) {
|
|
758
|
-
http1pr_s *p = (http1pr_s *)protocol;
|
|
759
|
-
ssize_t i;
|
|
760
|
-
|
|
761
|
-
i = fio_read(uuid, p->buf + p->buf_len, HTTP_MAX_HEADER_LENGTH - p->buf_len);
|
|
762
|
-
|
|
763
|
-
if (i <= 0)
|
|
764
|
-
return;
|
|
765
|
-
p->buf_len += i;
|
|
766
|
-
|
|
767
|
-
/* ensure future reads skip this first time HTTP/2.0 test */
|
|
768
|
-
p->p.protocol.on_data = http1_on_data;
|
|
769
|
-
if (i >= 24 && !memcmp(p->buf, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", 24)) {
|
|
770
|
-
FIO_LOG_WARNING("client claimed unsupported HTTP/2 prior knowledge.");
|
|
771
|
-
fio_close(uuid);
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
/* Finish handling the same way as the normal `on_data` */
|
|
776
|
-
http1_consume_data(uuid, p);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
/* *****************************************************************************
|
|
780
|
-
Public API
|
|
781
|
-
***************************************************************************** */
|
|
782
|
-
|
|
783
|
-
/** Creates an HTTP1 protocol object and handles any unread data in the buffer
|
|
784
|
-
* (if any). */
|
|
785
|
-
fio_protocol_s *http1_new(uintptr_t uuid, http_settings_s *settings,
|
|
786
|
-
void *unread_data, size_t unread_length) {
|
|
787
|
-
if (unread_data && unread_length > HTTP_MAX_HEADER_LENGTH)
|
|
788
|
-
return NULL;
|
|
789
|
-
http1pr_s *p = fio_malloc(sizeof(*p) + HTTP_MAX_HEADER_LENGTH);
|
|
790
|
-
// FIO_LOG_DEBUG("Allocated HTTP/1.1 protocol at. %p", (void *)p);
|
|
791
|
-
FIO_ASSERT_ALLOC(p);
|
|
792
|
-
*p = (http1pr_s){
|
|
793
|
-
.p.protocol =
|
|
794
|
-
{
|
|
795
|
-
.on_data = http1_on_data_first_time,
|
|
796
|
-
.on_close = http1_on_close,
|
|
797
|
-
.on_ready = http1_on_ready,
|
|
798
|
-
},
|
|
799
|
-
.p.uuid = uuid,
|
|
800
|
-
.p.settings = settings,
|
|
801
|
-
.max_header_size = settings->max_header_size,
|
|
802
|
-
.is_client = settings->is_client,
|
|
803
|
-
};
|
|
804
|
-
http_s_new(&p->request, &p->p, &HTTP1_VTABLE);
|
|
805
|
-
if (unread_data && unread_length <= HTTP_MAX_HEADER_LENGTH) {
|
|
806
|
-
memcpy(p->buf, unread_data, unread_length);
|
|
807
|
-
p->buf_len = unread_length;
|
|
808
|
-
}
|
|
809
|
-
fio_attach(uuid, &p->p.protocol);
|
|
810
|
-
if (unread_data && unread_length <= HTTP_MAX_HEADER_LENGTH) {
|
|
811
|
-
fio_force_event(uuid, FIO_EVENT_ON_DATA);
|
|
812
|
-
}
|
|
813
|
-
return &p->p.protocol;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
/** Manually destroys the HTTP1 protocol object. */
|
|
817
|
-
void http1_destroy(fio_protocol_s *pr) {
|
|
818
|
-
http1pr_s *p = (http1pr_s *)pr;
|
|
819
|
-
http1_pr2handle(p).status = 0;
|
|
820
|
-
http_s_destroy(&http1_pr2handle(p), 0);
|
|
821
|
-
fio_free(p); // occasional Windows crash bug
|
|
822
|
-
// FIO_LOG_DEBUG("Deallocated HTTP/1.1 protocol at. %p", (void *)p);
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
/* *****************************************************************************
|
|
826
|
-
Protocol Data
|
|
827
|
-
***************************************************************************** */
|
|
828
|
-
|
|
829
|
-
// clang-format off
|
|
830
|
-
#define HTTP_SET_STATUS_STR(status, str) [((status)-100)] = { .data = (char*)("HTTP/1.1 " #status " " str "\r\n"), .len = (sizeof("HTTP/1.1 " #status " " str "\r\n") - 1) }
|
|
831
|
-
// #undef HTTP_SET_STATUS_STR
|
|
832
|
-
// clang-format on
|
|
833
|
-
|
|
834
|
-
static fio_str_info_s http1pr_status2str(uintptr_t status) {
|
|
835
|
-
static fio_str_info_s status2str[] = {
|
|
836
|
-
HTTP_SET_STATUS_STR(100, "Continue"),
|
|
837
|
-
HTTP_SET_STATUS_STR(101, "Switching Protocols"),
|
|
838
|
-
HTTP_SET_STATUS_STR(102, "Processing"),
|
|
839
|
-
HTTP_SET_STATUS_STR(103, "Early Hints"),
|
|
840
|
-
HTTP_SET_STATUS_STR(200, "OK"),
|
|
841
|
-
HTTP_SET_STATUS_STR(201, "Created"),
|
|
842
|
-
HTTP_SET_STATUS_STR(202, "Accepted"),
|
|
843
|
-
HTTP_SET_STATUS_STR(203, "Non-Authoritative Information"),
|
|
844
|
-
HTTP_SET_STATUS_STR(204, "No Content"),
|
|
845
|
-
HTTP_SET_STATUS_STR(205, "Reset Content"),
|
|
846
|
-
HTTP_SET_STATUS_STR(206, "Partial Content"),
|
|
847
|
-
HTTP_SET_STATUS_STR(207, "Multi-Status"),
|
|
848
|
-
HTTP_SET_STATUS_STR(208, "Already Reported"),
|
|
849
|
-
HTTP_SET_STATUS_STR(226, "IM Used"),
|
|
850
|
-
HTTP_SET_STATUS_STR(300, "Multiple Choices"),
|
|
851
|
-
HTTP_SET_STATUS_STR(301, "Moved Permanently"),
|
|
852
|
-
HTTP_SET_STATUS_STR(302, "Found"),
|
|
853
|
-
HTTP_SET_STATUS_STR(303, "See Other"),
|
|
854
|
-
HTTP_SET_STATUS_STR(304, "Not Modified"),
|
|
855
|
-
HTTP_SET_STATUS_STR(305, "Use Proxy"),
|
|
856
|
-
HTTP_SET_STATUS_STR(306, "(Unused), "),
|
|
857
|
-
HTTP_SET_STATUS_STR(307, "Temporary Redirect"),
|
|
858
|
-
HTTP_SET_STATUS_STR(308, "Permanent Redirect"),
|
|
859
|
-
HTTP_SET_STATUS_STR(400, "Bad Request"),
|
|
860
|
-
HTTP_SET_STATUS_STR(403, "Forbidden"),
|
|
861
|
-
HTTP_SET_STATUS_STR(404, "Not Found"),
|
|
862
|
-
HTTP_SET_STATUS_STR(401, "Unauthorized"),
|
|
863
|
-
HTTP_SET_STATUS_STR(402, "Payment Required"),
|
|
864
|
-
HTTP_SET_STATUS_STR(405, "Method Not Allowed"),
|
|
865
|
-
HTTP_SET_STATUS_STR(406, "Not Acceptable"),
|
|
866
|
-
HTTP_SET_STATUS_STR(407, "Proxy Authentication Required"),
|
|
867
|
-
HTTP_SET_STATUS_STR(408, "Request Timeout"),
|
|
868
|
-
HTTP_SET_STATUS_STR(409, "Conflict"),
|
|
869
|
-
HTTP_SET_STATUS_STR(410, "Gone"),
|
|
870
|
-
HTTP_SET_STATUS_STR(411, "Length Required"),
|
|
871
|
-
HTTP_SET_STATUS_STR(412, "Precondition Failed"),
|
|
872
|
-
HTTP_SET_STATUS_STR(413, "Payload Too Large"),
|
|
873
|
-
HTTP_SET_STATUS_STR(414, "URI Too Long"),
|
|
874
|
-
HTTP_SET_STATUS_STR(415, "Unsupported Media Type"),
|
|
875
|
-
HTTP_SET_STATUS_STR(416, "Range Not Satisfiable"),
|
|
876
|
-
HTTP_SET_STATUS_STR(417, "Expectation Failed"),
|
|
877
|
-
HTTP_SET_STATUS_STR(421, "Misdirected Request"),
|
|
878
|
-
HTTP_SET_STATUS_STR(422, "Unprocessable Entity"),
|
|
879
|
-
HTTP_SET_STATUS_STR(423, "Locked"),
|
|
880
|
-
HTTP_SET_STATUS_STR(424, "Failed Dependency"),
|
|
881
|
-
HTTP_SET_STATUS_STR(425, "Unassigned"),
|
|
882
|
-
HTTP_SET_STATUS_STR(426, "Upgrade Required"),
|
|
883
|
-
HTTP_SET_STATUS_STR(427, "Unassigned"),
|
|
884
|
-
HTTP_SET_STATUS_STR(428, "Precondition Required"),
|
|
885
|
-
HTTP_SET_STATUS_STR(429, "Too Many Requests"),
|
|
886
|
-
HTTP_SET_STATUS_STR(430, "Unassigned"),
|
|
887
|
-
HTTP_SET_STATUS_STR(431, "Request Header Fields Too Large"),
|
|
888
|
-
HTTP_SET_STATUS_STR(500, "Internal Server Error"),
|
|
889
|
-
HTTP_SET_STATUS_STR(501, "Not Implemented"),
|
|
890
|
-
HTTP_SET_STATUS_STR(502, "Bad Gateway"),
|
|
891
|
-
HTTP_SET_STATUS_STR(503, "Service Unavailable"),
|
|
892
|
-
HTTP_SET_STATUS_STR(504, "Gateway Timeout"),
|
|
893
|
-
HTTP_SET_STATUS_STR(505, "HTTP Version Not Supported"),
|
|
894
|
-
HTTP_SET_STATUS_STR(506, "Variant Also Negotiates"),
|
|
895
|
-
HTTP_SET_STATUS_STR(507, "Insufficient Storage"),
|
|
896
|
-
HTTP_SET_STATUS_STR(508, "Loop Detected"),
|
|
897
|
-
HTTP_SET_STATUS_STR(509, "Unassigned"),
|
|
898
|
-
HTTP_SET_STATUS_STR(510, "Not Extended"),
|
|
899
|
-
HTTP_SET_STATUS_STR(511, "Network Authentication Required"),
|
|
900
|
-
};
|
|
901
|
-
fio_str_info_s ret = (fio_str_info_s){.len = 0, .data = NULL};
|
|
902
|
-
if (status >= 100 &&
|
|
903
|
-
(status - 100) < sizeof(status2str) / sizeof(status2str[0]))
|
|
904
|
-
ret = status2str[status - 100];
|
|
905
|
-
if (!ret.data) {
|
|
906
|
-
ret = status2str[400];
|
|
907
|
-
}
|
|
908
|
-
return ret;
|
|
909
|
-
}
|
|
910
|
-
#undef HTTP_SET_STATUS_STR
|