rage-iodine 1.7.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/workflows/ruby.yml +42 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +1098 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/LIMITS.md +41 -0
- data/README.md +782 -0
- data/Rakefile +23 -0
- data/SPEC-PubSub-Draft.md +159 -0
- data/SPEC-WebSocket-Draft.md +239 -0
- data/bin/console +22 -0
- data/bin/info.md +353 -0
- data/bin/mustache_bench.rb +100 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/examples/async_task.ru +92 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/examples/config.ru +59 -0
- data/examples/echo.ru +59 -0
- data/examples/etag.ru +16 -0
- data/examples/hello.ru +29 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/rack3.ru +12 -0
- data/examples/redis.ru +70 -0
- data/examples/shootout.ru +73 -0
- data/examples/sub-protocols.ru +90 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +280 -0
- data/ext/iodine/extconf.rb +110 -0
- data/ext/iodine/fio.c +12096 -0
- data/ext/iodine/fio.h +6390 -0
- data/ext/iodine/fio_cli.c +431 -0
- data/ext/iodine/fio_cli.h +189 -0
- data/ext/iodine/fio_json_parser.h +687 -0
- data/ext/iodine/fio_siphash.c +157 -0
- data/ext/iodine/fio_siphash.h +37 -0
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +649 -0
- data/ext/iodine/fio_tls_openssl.c +1056 -0
- data/ext/iodine/fio_tmpfile.h +50 -0
- data/ext/iodine/fiobj.h +44 -0
- data/ext/iodine/fiobj4fio.h +21 -0
- data/ext/iodine/fiobj_ary.c +333 -0
- data/ext/iodine/fiobj_ary.h +139 -0
- data/ext/iodine/fiobj_data.c +1185 -0
- data/ext/iodine/fiobj_data.h +167 -0
- data/ext/iodine/fiobj_hash.c +409 -0
- data/ext/iodine/fiobj_hash.h +176 -0
- data/ext/iodine/fiobj_json.c +622 -0
- data/ext/iodine/fiobj_json.h +68 -0
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +317 -0
- data/ext/iodine/fiobj_mustache.h +62 -0
- data/ext/iodine/fiobj_numbers.c +344 -0
- data/ext/iodine/fiobj_numbers.h +127 -0
- data/ext/iodine/fiobj_str.c +433 -0
- data/ext/iodine/fiobj_str.h +172 -0
- data/ext/iodine/fiobject.c +620 -0
- data/ext/iodine/fiobject.h +654 -0
- data/ext/iodine/hpack.h +1923 -0
- data/ext/iodine/http.c +2736 -0
- data/ext/iodine/http.h +1019 -0
- data/ext/iodine/http1.c +825 -0
- data/ext/iodine/http1.h +29 -0
- data/ext/iodine/http1_parser.h +1835 -0
- data/ext/iodine/http_internal.c +1279 -0
- data/ext/iodine/http_internal.h +248 -0
- data/ext/iodine/http_mime_parser.h +350 -0
- data/ext/iodine/iodine.c +1433 -0
- data/ext/iodine/iodine.h +64 -0
- data/ext/iodine/iodine_caller.c +218 -0
- data/ext/iodine/iodine_caller.h +27 -0
- data/ext/iodine/iodine_connection.c +941 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +420 -0
- data/ext/iodine/iodine_defer.h +6 -0
- data/ext/iodine/iodine_fiobj2rb.h +120 -0
- data/ext/iodine/iodine_helpers.c +282 -0
- data/ext/iodine/iodine_helpers.h +12 -0
- data/ext/iodine/iodine_http.c +1280 -0
- data/ext/iodine/iodine_http.h +23 -0
- data/ext/iodine/iodine_json.c +302 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_mustache.c +567 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +580 -0
- data/ext/iodine/iodine_pubsub.h +26 -0
- data/ext/iodine/iodine_rack_io.c +273 -0
- data/ext/iodine/iodine_rack_io.h +20 -0
- data/ext/iodine/iodine_store.c +142 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +346 -0
- data/ext/iodine/iodine_tcp.h +13 -0
- data/ext/iodine/iodine_tls.c +261 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1546 -0
- data/ext/iodine/redis_engine.c +957 -0
- data/ext/iodine/redis_engine.h +79 -0
- data/ext/iodine/resp_parser.h +317 -0
- data/ext/iodine/scheduler.c +173 -0
- data/ext/iodine/scheduler.h +6 -0
- data/ext/iodine/websocket_parser.h +506 -0
- data/ext/iodine/websockets.c +752 -0
- data/ext/iodine/websockets.h +185 -0
- data/iodine.gemspec +50 -0
- data/lib/iodine/connection.rb +61 -0
- data/lib/iodine/json.rb +42 -0
- data/lib/iodine/mustache.rb +113 -0
- data/lib/iodine/pubsub.rb +55 -0
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +3 -0
- data/lib/iodine.rb +274 -0
- data/lib/rack/handler/iodine.rb +33 -0
- data/logo.png +0 -0
- metadata +284 -0
@@ -0,0 +1,1280 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz segev, 2016-2017
|
3
|
+
License: MIT
|
4
|
+
|
5
|
+
Feel free to copy, use and enjoy according to the license provided.
|
6
|
+
*/
|
7
|
+
#include "iodine.h"
|
8
|
+
|
9
|
+
#include "http.h"
|
10
|
+
#include "iodine_caller.h"
|
11
|
+
#include "iodine_store.h"
|
12
|
+
|
13
|
+
#include <ruby/encoding.h>
|
14
|
+
#include <ruby/io.h>
|
15
|
+
#include <stdio.h>
|
16
|
+
// #include "iodine_websockets.h"
|
17
|
+
|
18
|
+
#ifndef __MINGW32__
|
19
|
+
#include <arpa/inet.h>
|
20
|
+
#endif
|
21
|
+
#include <ctype.h>
|
22
|
+
#include <stdlib.h>
|
23
|
+
#include <string.h>
|
24
|
+
#ifndef __MINGW32__
|
25
|
+
#include <sys/socket.h>
|
26
|
+
#endif
|
27
|
+
|
28
|
+
/* *****************************************************************************
|
29
|
+
Available Globals
|
30
|
+
***************************************************************************** */
|
31
|
+
|
32
|
+
typedef struct {
|
33
|
+
VALUE app;
|
34
|
+
VALUE env;
|
35
|
+
} iodine_http_settings_s;
|
36
|
+
|
37
|
+
/* these three are used also by iodin_rack_io.c */
|
38
|
+
VALUE IODINE_R_INPUT_DEFAULT;
|
39
|
+
VALUE IODINE_R_INPUT;
|
40
|
+
VALUE IODINE_R_HIJACK;
|
41
|
+
VALUE IODINE_R_HIJACK_IO;
|
42
|
+
VALUE IODINE_R_HIJACK_CB;
|
43
|
+
|
44
|
+
static VALUE RACK_UPGRADE;
|
45
|
+
static VALUE RACK_UPGRADE_Q;
|
46
|
+
static VALUE RACK_UPGRADE_SSE;
|
47
|
+
static VALUE RACK_UPGRADE_WEBSOCKET;
|
48
|
+
static VALUE UPGRADE_TCP;
|
49
|
+
|
50
|
+
static VALUE HTTP_ACCEPT;
|
51
|
+
static VALUE HTTP_USER_AGENT;
|
52
|
+
static VALUE HTTP_ACCEPT_ENCODING;
|
53
|
+
static VALUE HTTP_ACCEPT_LANGUAGE;
|
54
|
+
static VALUE HTTP_CONNECTION;
|
55
|
+
static VALUE HTTP_HOST;
|
56
|
+
|
57
|
+
static VALUE hijack_func_sym;
|
58
|
+
static ID close_method_id;
|
59
|
+
static ID each_method_id;
|
60
|
+
static ID attach_method_id;
|
61
|
+
static ID iodine_call_proc_id;
|
62
|
+
static ID fiber_result_var_id;
|
63
|
+
static VALUE http_wait_directive;
|
64
|
+
|
65
|
+
static VALUE env_template_no_upgrade;
|
66
|
+
static VALUE env_template_websockets;
|
67
|
+
static VALUE env_template_sse;
|
68
|
+
|
69
|
+
static rb_encoding *IodineUTF8Encoding;
|
70
|
+
static rb_encoding *IodineBinaryEncoding;
|
71
|
+
|
72
|
+
static uint8_t support_xsendfile = 0;
|
73
|
+
|
74
|
+
#define rack_declare(rack_name) static VALUE rack_name
|
75
|
+
|
76
|
+
#define rack_set(rack_name, str) \
|
77
|
+
(rack_name) = rb_enc_str_new((str), strlen((str)), IodineBinaryEncoding); \
|
78
|
+
rb_global_variable(&(rack_name)); \
|
79
|
+
rb_obj_freeze(rack_name);
|
80
|
+
#define rack_set_sym(rack_name, sym) \
|
81
|
+
(rack_name) = rb_id2sym(rb_intern2((sym), strlen((sym)))); \
|
82
|
+
rb_global_variable(&(rack_name));
|
83
|
+
|
84
|
+
#define rack_autoset(rack_name) rack_set((rack_name), #rack_name)
|
85
|
+
|
86
|
+
// static uint8_t IODINE_IS_DEVELOPMENT_MODE = 0;
|
87
|
+
|
88
|
+
rack_declare(HTTP_SCHEME);
|
89
|
+
rack_declare(HTTPS_SCHEME);
|
90
|
+
rack_declare(QUERY_ESTRING);
|
91
|
+
rack_declare(REQUEST_METHOD);
|
92
|
+
rack_declare(PATH_INFO);
|
93
|
+
rack_declare(QUERY_STRING);
|
94
|
+
rack_declare(QUERY_ESTRING);
|
95
|
+
rack_declare(SERVER_NAME);
|
96
|
+
rack_declare(SERVER_PORT);
|
97
|
+
rack_declare(SERVER_PROTOCOL);
|
98
|
+
rack_declare(HTTP_VERSION);
|
99
|
+
rack_declare(REMOTE_ADDR);
|
100
|
+
rack_declare(CONTENT_LENGTH);
|
101
|
+
rack_declare(CONTENT_TYPE);
|
102
|
+
rack_declare(R_URL_SCHEME); // rack.url_scheme
|
103
|
+
rack_declare(R_INPUT); // rack.input
|
104
|
+
rack_declare(XSENDFILE); // for X-Sendfile support
|
105
|
+
rack_declare(XSENDFILE_TYPE); // for X-Sendfile support
|
106
|
+
rack_declare(XSENDFILE_TYPE_HEADER); // for X-Sendfile support
|
107
|
+
rack_declare(CONTENT_LENGTH_HEADER); // for X-Sendfile support
|
108
|
+
rack_declare(IODINE_REQUEST_ID);
|
109
|
+
|
110
|
+
/* used internally to handle requests */
|
111
|
+
typedef struct {
|
112
|
+
http_s *h;
|
113
|
+
FIOBJ body;
|
114
|
+
enum iodine_http_response_type_enum {
|
115
|
+
IODINE_HTTP_NONE,
|
116
|
+
IODINE_HTTP_SENDBODY,
|
117
|
+
IODINE_HTTP_XSENDFILE,
|
118
|
+
IODINE_HTTP_EMPTY,
|
119
|
+
IODINE_HTTP_ERROR,
|
120
|
+
IODINE_HTTP_WAIT,
|
121
|
+
} type;
|
122
|
+
enum iodine_upgrade_type_enum {
|
123
|
+
IODINE_UPGRADE_NONE = 0,
|
124
|
+
IODINE_UPGRADE_WEBSOCKET,
|
125
|
+
IODINE_UPGRADE_SSE,
|
126
|
+
} upgrade;
|
127
|
+
} iodine_http_request_handle_s;
|
128
|
+
|
129
|
+
/* *****************************************************************************
|
130
|
+
WebSocket support
|
131
|
+
***************************************************************************** */
|
132
|
+
|
133
|
+
typedef struct {
|
134
|
+
char *data;
|
135
|
+
size_t size;
|
136
|
+
uint8_t is_text;
|
137
|
+
VALUE io;
|
138
|
+
} iodine_msg2ruby_s;
|
139
|
+
|
140
|
+
static void *iodine_ws_fire_message(void *msg_) {
|
141
|
+
iodine_msg2ruby_s *msg = msg_;
|
142
|
+
VALUE data = rb_enc_str_new(
|
143
|
+
msg->data, msg->size,
|
144
|
+
(msg->is_text ? rb_utf8_encoding() : rb_ascii8bit_encoding()));
|
145
|
+
iodine_connection_fire_event(msg->io, IODINE_CONNECTION_ON_MESSAGE, data);
|
146
|
+
return NULL;
|
147
|
+
}
|
148
|
+
|
149
|
+
static void iodine_ws_on_message(ws_s *ws, fio_str_info_s data,
|
150
|
+
uint8_t is_text) {
|
151
|
+
iodine_msg2ruby_s msg = {
|
152
|
+
.data = data.data,
|
153
|
+
.size = data.len,
|
154
|
+
.is_text = is_text,
|
155
|
+
.io = (VALUE)websocket_udata_get(ws),
|
156
|
+
};
|
157
|
+
IodineCaller.enterGVL(iodine_ws_fire_message, &msg);
|
158
|
+
}
|
159
|
+
/**
|
160
|
+
* The (optional) on_open callback will be called once the websocket
|
161
|
+
* connection is established and before is is registered with `facil`, so no
|
162
|
+
* `on_message` events are raised before `on_open` returns.
|
163
|
+
*/
|
164
|
+
static void iodine_ws_on_open(ws_s *ws) {
|
165
|
+
VALUE h = (VALUE)websocket_udata_get(ws);
|
166
|
+
iodine_connection_s *c = iodine_connection_CData(h);
|
167
|
+
c->arg = ws;
|
168
|
+
c->uuid = websocket_uuid(ws);
|
169
|
+
iodine_connection_fire_event(h, IODINE_CONNECTION_ON_OPEN, Qnil);
|
170
|
+
}
|
171
|
+
/**
|
172
|
+
* The (optional) on_ready callback will be after a the underlying socket's
|
173
|
+
* buffer changes it's state from full to empty.
|
174
|
+
*
|
175
|
+
* If the socket's buffer is never used, the callback is never called.
|
176
|
+
*/
|
177
|
+
static void iodine_ws_on_ready(ws_s *ws) {
|
178
|
+
iodine_connection_fire_event((VALUE)websocket_udata_get(ws),
|
179
|
+
IODINE_CONNECTION_ON_DRAINED, Qnil);
|
180
|
+
}
|
181
|
+
/**
|
182
|
+
* The (optional) on_shutdown callback will be called if a websocket
|
183
|
+
* connection is still open while the server is shutting down (called before
|
184
|
+
* `on_close`).
|
185
|
+
*/
|
186
|
+
static void iodine_ws_on_shutdown(ws_s *ws) {
|
187
|
+
iodine_connection_fire_event((VALUE)websocket_udata_get(ws),
|
188
|
+
IODINE_CONNECTION_ON_SHUTDOWN, Qnil);
|
189
|
+
}
|
190
|
+
/**
|
191
|
+
* The (optional) on_close callback will be called once a websocket connection
|
192
|
+
* is terminated or failed to be established.
|
193
|
+
*
|
194
|
+
* The `uuid` is the connection's unique ID that can identify the Websocket. A
|
195
|
+
* value of `uuid == 0` indicates the Websocket connection wasn't established
|
196
|
+
* (an error occured).
|
197
|
+
*
|
198
|
+
* The `udata` is the user data as set during the upgrade or using the
|
199
|
+
* `websocket_udata_set` function.
|
200
|
+
*/
|
201
|
+
static void iodine_ws_on_close(intptr_t uuid, void *udata) {
|
202
|
+
iodine_connection_fire_event((VALUE)udata, IODINE_CONNECTION_ON_CLOSE, Qnil);
|
203
|
+
(void)uuid;
|
204
|
+
}
|
205
|
+
|
206
|
+
static void iodine_ws_attach(http_s *h, VALUE handler, VALUE env) {
|
207
|
+
VALUE io =
|
208
|
+
iodine_connection_new(.type = IODINE_CONNECTION_WEBSOCKET, .arg = NULL,
|
209
|
+
.handler = handler, .env = env, .uuid = 0);
|
210
|
+
if (io == Qnil)
|
211
|
+
return;
|
212
|
+
|
213
|
+
http_upgrade2ws(h, .on_message = iodine_ws_on_message,
|
214
|
+
.on_open = iodine_ws_on_open, .on_ready = iodine_ws_on_ready,
|
215
|
+
.on_shutdown = iodine_ws_on_shutdown,
|
216
|
+
.on_close = iodine_ws_on_close, .udata = (void *)io);
|
217
|
+
}
|
218
|
+
|
219
|
+
/* *****************************************************************************
|
220
|
+
SSE support
|
221
|
+
***************************************************************************** */
|
222
|
+
|
223
|
+
static void iodine_sse_on_ready(http_sse_s *sse) {
|
224
|
+
iodine_connection_fire_event((VALUE)sse->udata, IODINE_CONNECTION_ON_DRAINED,
|
225
|
+
Qnil);
|
226
|
+
}
|
227
|
+
|
228
|
+
static void iodine_sse_on_shutdown(http_sse_s *sse) {
|
229
|
+
iodine_connection_fire_event((VALUE)sse->udata, IODINE_CONNECTION_ON_SHUTDOWN,
|
230
|
+
Qnil);
|
231
|
+
}
|
232
|
+
static void iodine_sse_on_close(http_sse_s *sse) {
|
233
|
+
iodine_connection_fire_event((VALUE)sse->udata, IODINE_CONNECTION_ON_CLOSE,
|
234
|
+
Qnil);
|
235
|
+
}
|
236
|
+
|
237
|
+
static void iodine_sse_on_open(http_sse_s *sse) {
|
238
|
+
VALUE h = (VALUE)sse->udata;
|
239
|
+
iodine_connection_s *c = iodine_connection_CData(h);
|
240
|
+
c->arg = sse;
|
241
|
+
c->uuid = http_sse2uuid(sse);
|
242
|
+
iodine_connection_fire_event(h, IODINE_CONNECTION_ON_OPEN, Qnil);
|
243
|
+
sse->on_ready = iodine_sse_on_ready;
|
244
|
+
fio_force_event(c->uuid, FIO_EVENT_ON_READY);
|
245
|
+
}
|
246
|
+
|
247
|
+
static void iodine_sse_attach(http_s *h, VALUE handler, VALUE env) {
|
248
|
+
VALUE io = iodine_connection_new(.type = IODINE_CONNECTION_SSE, .arg = NULL,
|
249
|
+
.handler = handler, .env = env, .uuid = 0);
|
250
|
+
if (io == Qnil)
|
251
|
+
return;
|
252
|
+
|
253
|
+
http_upgrade2sse(h, .on_open = iodine_sse_on_open,
|
254
|
+
.on_ready = NULL /* will be set after the on_open */,
|
255
|
+
.on_shutdown = iodine_sse_on_shutdown,
|
256
|
+
.on_close = iodine_sse_on_close, .udata = (void *)io);
|
257
|
+
}
|
258
|
+
|
259
|
+
/* *****************************************************************************
|
260
|
+
Copying data from the C request to the Rack's ENV
|
261
|
+
***************************************************************************** */
|
262
|
+
|
263
|
+
#define to_upper(c) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~32) : (c))
|
264
|
+
|
265
|
+
static int iodine_copy2env_task(FIOBJ o, void *env_) {
|
266
|
+
VALUE env = (VALUE)env_;
|
267
|
+
FIOBJ name = fiobj_hash_key_in_loop();
|
268
|
+
fio_str_info_s tmp = fiobj_obj2cstr(name);
|
269
|
+
VALUE hname = (VALUE)0;
|
270
|
+
/* test for common header names, using pre-allocated memory */
|
271
|
+
if (tmp.len == 6 && !memcmp("accept", tmp.data, 6)) {
|
272
|
+
hname = HTTP_ACCEPT;
|
273
|
+
} else if (tmp.len == 10 && !memcmp("user-agent", tmp.data, 10)) {
|
274
|
+
hname = HTTP_USER_AGENT;
|
275
|
+
} else if (tmp.len == 15 && !memcmp("accept-encoding", tmp.data, 15)) {
|
276
|
+
hname = HTTP_ACCEPT_ENCODING;
|
277
|
+
} else if (tmp.len == 15 && !memcmp("accept-language", tmp.data, 15)) {
|
278
|
+
hname = HTTP_ACCEPT_LANGUAGE;
|
279
|
+
} else if (tmp.len == 10 && !memcmp("connection", tmp.data, 10)) {
|
280
|
+
hname = HTTP_CONNECTION;
|
281
|
+
} else if (tmp.len == 4 && !memcmp("host", tmp.data, 4)) {
|
282
|
+
hname = HTTP_HOST;
|
283
|
+
} else if (tmp.len > 123) {
|
284
|
+
char *buf = fio_malloc(tmp.len + 5);
|
285
|
+
memcpy(buf, "HTTP_", 5);
|
286
|
+
for (size_t i = 0; i < tmp.len; ++i) {
|
287
|
+
buf[i + 5] = (tmp.data[i] == '-') ? '_' : to_upper(tmp.data[i]);
|
288
|
+
}
|
289
|
+
hname = rb_enc_str_new(buf, tmp.len + 5, IodineBinaryEncoding);
|
290
|
+
fio_free(buf);
|
291
|
+
} else {
|
292
|
+
char buf[128];
|
293
|
+
memcpy(buf, "HTTP_", 5);
|
294
|
+
for (size_t i = 0; i < tmp.len; ++i) {
|
295
|
+
buf[i + 5] = (tmp.data[i] == '-') ? '_' : to_upper(tmp.data[i]);
|
296
|
+
}
|
297
|
+
hname = rb_enc_str_new(buf, tmp.len + 5, IodineBinaryEncoding);
|
298
|
+
}
|
299
|
+
|
300
|
+
if (FIOBJ_TYPE_IS(o, FIOBJ_T_STRING)) {
|
301
|
+
tmp = fiobj_obj2cstr(o);
|
302
|
+
rb_hash_aset(env, hname,
|
303
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
304
|
+
|
305
|
+
} else {
|
306
|
+
/* it's an array */
|
307
|
+
size_t count = fiobj_ary_count(o);
|
308
|
+
VALUE ary = rb_ary_new2(count);
|
309
|
+
rb_hash_aset(env, hname, ary);
|
310
|
+
for (size_t i = 0; i < count; ++i) {
|
311
|
+
tmp = fiobj_obj2cstr(fiobj_ary_index(o, i));
|
312
|
+
rb_ary_push(ary, rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
313
|
+
}
|
314
|
+
}
|
315
|
+
return 0;
|
316
|
+
}
|
317
|
+
|
318
|
+
static inline VALUE copy2env(iodine_http_request_handle_s *handle) {
|
319
|
+
VALUE env;
|
320
|
+
http_s *h = handle->h;
|
321
|
+
switch (handle->upgrade) {
|
322
|
+
case IODINE_UPGRADE_WEBSOCKET:
|
323
|
+
env = rb_hash_dup(env_template_websockets);
|
324
|
+
break;
|
325
|
+
case IODINE_UPGRADE_SSE:
|
326
|
+
env = rb_hash_dup(env_template_sse);
|
327
|
+
break;
|
328
|
+
case IODINE_UPGRADE_NONE: /* fallthrough */
|
329
|
+
default:
|
330
|
+
env = rb_hash_dup(env_template_no_upgrade);
|
331
|
+
break;
|
332
|
+
}
|
333
|
+
IodineStore.add(env);
|
334
|
+
|
335
|
+
fio_str_info_s tmp;
|
336
|
+
char *pos = NULL;
|
337
|
+
/* Copy basic data */
|
338
|
+
tmp = fiobj_obj2cstr(h->method);
|
339
|
+
rb_hash_aset(env, REQUEST_METHOD,
|
340
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
341
|
+
tmp = fiobj_obj2cstr(h->path);
|
342
|
+
rb_hash_aset(env, PATH_INFO,
|
343
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
344
|
+
if (h->query) {
|
345
|
+
tmp = fiobj_obj2cstr(h->query);
|
346
|
+
rb_hash_aset(env, QUERY_STRING,
|
347
|
+
tmp.len
|
348
|
+
? rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding)
|
349
|
+
: QUERY_ESTRING);
|
350
|
+
} else {
|
351
|
+
rb_hash_aset(env, QUERY_STRING, QUERY_ESTRING);
|
352
|
+
}
|
353
|
+
{
|
354
|
+
// HTTP version appears twice
|
355
|
+
tmp = fiobj_obj2cstr(h->version);
|
356
|
+
VALUE hname = rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding);
|
357
|
+
rb_hash_aset(env, SERVER_PROTOCOL, hname);
|
358
|
+
rb_hash_aset(env, HTTP_VERSION, hname);
|
359
|
+
}
|
360
|
+
|
361
|
+
{ // Support for Ruby web-console.
|
362
|
+
fio_str_info_s peer = http_peer_addr(h);
|
363
|
+
if (peer.len) {
|
364
|
+
rb_hash_aset(env, REMOTE_ADDR, rb_str_new(peer.data, peer.len));
|
365
|
+
}
|
366
|
+
}
|
367
|
+
|
368
|
+
/* handle the HOST header, including the possible host:#### format*/
|
369
|
+
static uint64_t host_hash = 0;
|
370
|
+
if (!host_hash)
|
371
|
+
host_hash = fiobj_hash_string("host", 4);
|
372
|
+
tmp = fiobj_obj2cstr(fiobj_hash_get2(h->headers, host_hash));
|
373
|
+
pos = tmp.data;
|
374
|
+
while (*pos && *pos != ':')
|
375
|
+
pos++;
|
376
|
+
if (*pos == 0) {
|
377
|
+
rb_hash_aset(env, SERVER_NAME,
|
378
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
379
|
+
} else {
|
380
|
+
rb_hash_aset(
|
381
|
+
env, SERVER_NAME,
|
382
|
+
rb_enc_str_new(tmp.data, pos - tmp.data, IodineBinaryEncoding));
|
383
|
+
++pos;
|
384
|
+
rb_hash_aset(
|
385
|
+
env, SERVER_PORT,
|
386
|
+
rb_enc_str_new(pos, tmp.len - (pos - tmp.data), IodineBinaryEncoding));
|
387
|
+
}
|
388
|
+
|
389
|
+
/* remove special headers */
|
390
|
+
{
|
391
|
+
static uint64_t content_length_hash = 0;
|
392
|
+
if (!content_length_hash)
|
393
|
+
content_length_hash = fiobj_hash_string("content-length", 14);
|
394
|
+
FIOBJ cl = fiobj_hash_get2(h->headers, content_length_hash);
|
395
|
+
if (cl) {
|
396
|
+
tmp = fiobj_obj2cstr(fiobj_hash_get2(h->headers, content_length_hash));
|
397
|
+
if (tmp.data) {
|
398
|
+
rb_hash_aset(env, CONTENT_LENGTH,
|
399
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
400
|
+
fiobj_hash_delete2(h->headers, content_length_hash);
|
401
|
+
}
|
402
|
+
}
|
403
|
+
}
|
404
|
+
{
|
405
|
+
static uint64_t content_type_hash = 0;
|
406
|
+
if (!content_type_hash)
|
407
|
+
content_type_hash = fiobj_hash_string("content-type", 12);
|
408
|
+
FIOBJ ct = fiobj_hash_get2(h->headers, content_type_hash);
|
409
|
+
if (ct) {
|
410
|
+
tmp = fiobj_obj2cstr(ct);
|
411
|
+
if (tmp.len && tmp.data) {
|
412
|
+
rb_hash_aset(env, CONTENT_TYPE,
|
413
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
414
|
+
fiobj_hash_delete2(h->headers, content_type_hash);
|
415
|
+
}
|
416
|
+
}
|
417
|
+
}
|
418
|
+
/* handle scheme / sepcial forwarding headers */
|
419
|
+
{
|
420
|
+
FIOBJ objtmp;
|
421
|
+
static uint64_t xforward_hash = 0;
|
422
|
+
if (!xforward_hash)
|
423
|
+
xforward_hash = fiobj_hash_string("x-forwarded-proto", 27);
|
424
|
+
static uint64_t forward_hash = 0;
|
425
|
+
if (!forward_hash)
|
426
|
+
forward_hash = fiobj_hash_string("forwarded", 9);
|
427
|
+
if ((objtmp = fiobj_hash_get2(h->headers, xforward_hash))) {
|
428
|
+
tmp = fiobj_obj2cstr(objtmp);
|
429
|
+
if (tmp.len >= 5 && !strncasecmp(tmp.data, "https", 5)) {
|
430
|
+
rb_hash_aset(env, R_URL_SCHEME, HTTPS_SCHEME);
|
431
|
+
} else if (tmp.len == 4 && !strncasecmp(tmp.data, "http", 4)) {
|
432
|
+
rb_hash_aset(env, R_URL_SCHEME, HTTP_SCHEME);
|
433
|
+
} else {
|
434
|
+
rb_hash_aset(env, R_URL_SCHEME,
|
435
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
436
|
+
}
|
437
|
+
} else if ((objtmp = fiobj_hash_get2(h->headers, forward_hash))) {
|
438
|
+
tmp = fiobj_obj2cstr(objtmp);
|
439
|
+
pos = tmp.data;
|
440
|
+
if (pos) {
|
441
|
+
while (*pos) {
|
442
|
+
if (((*(pos++) | 32) == 'p') && ((*(pos++) | 32) == 'r') &&
|
443
|
+
((*(pos++) | 32) == 'o') && ((*(pos++) | 32) == 't') &&
|
444
|
+
((*(pos++) | 32) == 'o') && ((*(pos++) | 32) == '=')) {
|
445
|
+
if ((pos[0] | 32) == 'h' && (pos[1] | 32) == 't' &&
|
446
|
+
(pos[2] | 32) == 't' && (pos[3] | 32) == 'p') {
|
447
|
+
if ((pos[4] | 32) == 's') {
|
448
|
+
rb_hash_aset(env, R_URL_SCHEME, HTTPS_SCHEME);
|
449
|
+
} else {
|
450
|
+
rb_hash_aset(env, R_URL_SCHEME, HTTP_SCHEME);
|
451
|
+
}
|
452
|
+
} else {
|
453
|
+
char *tmp = pos;
|
454
|
+
while (*tmp && *tmp != ';')
|
455
|
+
tmp++;
|
456
|
+
rb_hash_aset(env, R_URL_SCHEME, rb_str_new(pos, tmp - pos));
|
457
|
+
}
|
458
|
+
break;
|
459
|
+
}
|
460
|
+
}
|
461
|
+
}
|
462
|
+
} else if (http_settings(h)->tls) {
|
463
|
+
/* no forwarding information, but we do have TLS */
|
464
|
+
rb_hash_aset(env, R_URL_SCHEME, HTTPS_SCHEME);
|
465
|
+
} else {
|
466
|
+
/* no TLS, no forwarding, assume `http`, which is the default */
|
467
|
+
}
|
468
|
+
}
|
469
|
+
{
|
470
|
+
rb_hash_aset(env, IODINE_REQUEST_ID, rb_str_new_cstr(fiobj_obj2cstr(handle->h->request_id).data));
|
471
|
+
}
|
472
|
+
|
473
|
+
/* add all remaining headers */
|
474
|
+
fiobj_each1(h->headers, 0, iodine_copy2env_task, (void *)env);
|
475
|
+
return env;
|
476
|
+
}
|
477
|
+
#undef add_str_to_env
|
478
|
+
#undef add_value_to_env
|
479
|
+
#undef add_header_to_env
|
480
|
+
|
481
|
+
/* *****************************************************************************
|
482
|
+
Handling the HTTP response
|
483
|
+
***************************************************************************** */
|
484
|
+
|
485
|
+
// itterate through the headers and add them to the response buffer
|
486
|
+
// (we are recycling the request's buffer)
|
487
|
+
static int for_each_header_data(VALUE key, VALUE val, VALUE h_) {
|
488
|
+
http_s *h = (http_s *)h_;
|
489
|
+
// fprintf(stderr, "For_each - headers\n");
|
490
|
+
if (TYPE(val) == T_NIL || TYPE(key) == T_NIL)
|
491
|
+
return ST_CONTINUE;
|
492
|
+
if (TYPE(val) == T_ARRAY)
|
493
|
+
goto array_style_multi_value;
|
494
|
+
if (TYPE(key) != T_STRING)
|
495
|
+
key = IodineCaller.call(key, iodine_to_s_id);
|
496
|
+
if (TYPE(key) != T_STRING)
|
497
|
+
return ST_CONTINUE;
|
498
|
+
if (TYPE(val) != T_STRING) {
|
499
|
+
val = IodineCaller.call(val, iodine_to_s_id);
|
500
|
+
if (TYPE(val) != T_STRING)
|
501
|
+
return ST_STOP;
|
502
|
+
}
|
503
|
+
char *key_s = RSTRING_PTR(key);
|
504
|
+
int key_len = RSTRING_LEN(key);
|
505
|
+
char *val_s = RSTRING_PTR(val);
|
506
|
+
int val_len = RSTRING_LEN(val);
|
507
|
+
// make the headers lowercase
|
508
|
+
|
509
|
+
FIOBJ name = fiobj_str_new(key_s, key_len);
|
510
|
+
{
|
511
|
+
fio_str_info_s tmp = fiobj_obj2cstr(name);
|
512
|
+
for (int i = 0; i < key_len; ++i) {
|
513
|
+
tmp.data[i] = tolower(tmp.data[i]);
|
514
|
+
}
|
515
|
+
}
|
516
|
+
// scan the value for newline (\n) delimiters
|
517
|
+
char *pos_s = val_s;
|
518
|
+
char *pos_e = val_s + val_len;
|
519
|
+
while (pos_s < pos_e) {
|
520
|
+
// scanning for newline (\n) delimiters
|
521
|
+
char *const start = pos_s;
|
522
|
+
pos_s = memchr(pos_s, '\n', pos_e - pos_s);
|
523
|
+
if (!pos_s)
|
524
|
+
pos_s = pos_e;
|
525
|
+
http_set_header(h, name, fiobj_str_new(start, (pos_s - start)));
|
526
|
+
// move forward (skip the '\n' if exists)
|
527
|
+
++pos_s;
|
528
|
+
}
|
529
|
+
fiobj_free(name);
|
530
|
+
// no errors, return 0
|
531
|
+
return ST_CONTINUE;
|
532
|
+
|
533
|
+
array_style_multi_value:
|
534
|
+
for (size_t i = 0, end = RARRAY_LEN(val); i < end; ++i) {
|
535
|
+
if (for_each_header_data(key, RARRAY_AREF(val, i), h_) == ST_CONTINUE)
|
536
|
+
continue;
|
537
|
+
return ST_STOP;
|
538
|
+
}
|
539
|
+
return ST_CONTINUE;
|
540
|
+
}
|
541
|
+
|
542
|
+
// writes the body to the response object
|
543
|
+
static VALUE for_each_body_string(VALUE str, VALUE body_, int argc,
|
544
|
+
VALUE *argv) {
|
545
|
+
// fprintf(stderr, "For_each - body\n");
|
546
|
+
// write body
|
547
|
+
if (TYPE(str) != T_STRING) {
|
548
|
+
FIO_LOG_ERROR("(Iodine) response body not a String\n");
|
549
|
+
return Qfalse;
|
550
|
+
}
|
551
|
+
if (RSTRING_LEN(str) && RSTRING_PTR(str)) {
|
552
|
+
fiobj_str_write((FIOBJ)body_, RSTRING_PTR(str), RSTRING_LEN(str));
|
553
|
+
}
|
554
|
+
return Qtrue;
|
555
|
+
(void)argc;
|
556
|
+
(void)argv;
|
557
|
+
}
|
558
|
+
|
559
|
+
static inline int ruby2c_response_send(iodine_http_request_handle_s *handle,
|
560
|
+
VALUE rbresponse, VALUE env) {
|
561
|
+
(void)(env);
|
562
|
+
VALUE body = rb_ary_entry(rbresponse, 2);
|
563
|
+
if (handle->h->status < 200 || handle->h->status == 204 ||
|
564
|
+
handle->h->status == 304) {
|
565
|
+
if (body && rb_respond_to(body, close_method_id))
|
566
|
+
IodineCaller.call(body, close_method_id);
|
567
|
+
body = Qnil;
|
568
|
+
handle->type = IODINE_HTTP_NONE;
|
569
|
+
return 0;
|
570
|
+
}
|
571
|
+
if (TYPE(body) == T_ARRAY) {
|
572
|
+
if (RARRAY_LEN(body) == 0) { // only headers
|
573
|
+
handle->type = IODINE_HTTP_EMPTY;
|
574
|
+
return 0;
|
575
|
+
} else if (RARRAY_LEN(body) == 1) { // [String] is likely
|
576
|
+
body = rb_ary_entry(body, 0);
|
577
|
+
// fprintf(stderr, "Body was a single item array, unpacket to string\n");
|
578
|
+
}
|
579
|
+
}
|
580
|
+
|
581
|
+
if (TYPE(body) == T_STRING) {
|
582
|
+
// fprintf(stderr, "Review body as String\n");
|
583
|
+
handle->type = IODINE_HTTP_NONE;
|
584
|
+
if (RSTRING_LEN(body)) {
|
585
|
+
handle->body = fiobj_str_new(RSTRING_PTR(body), RSTRING_LEN(body));
|
586
|
+
handle->type = IODINE_HTTP_SENDBODY;
|
587
|
+
}
|
588
|
+
return 0;
|
589
|
+
} else if (rb_respond_to(body, each_method_id)) {
|
590
|
+
// fprintf(stderr, "Review body as for-each ...\n");
|
591
|
+
handle->body = fiobj_str_buf(1);
|
592
|
+
handle->type = IODINE_HTTP_SENDBODY;
|
593
|
+
IodineCaller.call_with_block(body, each_method_id, 0, NULL,
|
594
|
+
(VALUE)handle->body, for_each_body_string);
|
595
|
+
// we need to call `close` in case the object is an IO / BodyProxy
|
596
|
+
if (rb_respond_to(body, close_method_id))
|
597
|
+
IodineCaller.call(body, close_method_id);
|
598
|
+
return 0;
|
599
|
+
}
|
600
|
+
return -1;
|
601
|
+
}
|
602
|
+
|
603
|
+
/* *****************************************************************************
|
604
|
+
Handling Upgrade cases
|
605
|
+
***************************************************************************** */
|
606
|
+
|
607
|
+
static inline int ruby2c_review_upgrade(iodine_http_request_handle_s *req,
|
608
|
+
VALUE rbresponse, VALUE env) {
|
609
|
+
http_s *h = req->h;
|
610
|
+
VALUE handler;
|
611
|
+
if ((handler = rb_hash_aref(env, IODINE_R_HIJACK_CB)) != Qnil) {
|
612
|
+
// send headers
|
613
|
+
http_finish(h);
|
614
|
+
// call the callback
|
615
|
+
VALUE io_ruby = IodineCaller.call(rb_hash_aref(env, IODINE_R_HIJACK),
|
616
|
+
iodine_call_proc_id);
|
617
|
+
IodineCaller.call2(handler, iodine_call_proc_id, 1, &io_ruby);
|
618
|
+
goto upgraded;
|
619
|
+
} else if ((handler = rb_hash_aref(env, IODINE_R_HIJACK_IO)) != Qnil) {
|
620
|
+
// do nothing, just cleanup
|
621
|
+
goto upgraded;
|
622
|
+
} else if ((handler = rb_hash_aref(env, UPGRADE_TCP)) != Qnil) {
|
623
|
+
goto tcp_ip_upgrade;
|
624
|
+
} else {
|
625
|
+
switch (req->upgrade) {
|
626
|
+
case IODINE_UPGRADE_WEBSOCKET:
|
627
|
+
if ((handler = rb_hash_aref(env, RACK_UPGRADE)) != Qnil) {
|
628
|
+
// use response as existing base for native websocket upgrade
|
629
|
+
iodine_ws_attach(h, handler, env);
|
630
|
+
goto upgraded;
|
631
|
+
}
|
632
|
+
break;
|
633
|
+
case IODINE_UPGRADE_SSE:
|
634
|
+
if ((handler = rb_hash_aref(env, RACK_UPGRADE)) != Qnil) {
|
635
|
+
// use response as existing base for SSE upgrade
|
636
|
+
iodine_sse_attach(h, handler, env);
|
637
|
+
goto upgraded;
|
638
|
+
}
|
639
|
+
break;
|
640
|
+
default:
|
641
|
+
if ((handler = rb_hash_aref(env, RACK_UPGRADE)) != Qnil) {
|
642
|
+
tcp_ip_upgrade : {
|
643
|
+
// use response as existing base for raw TCP/IP upgrade
|
644
|
+
intptr_t uuid = http_hijack(h, NULL);
|
645
|
+
// send headers
|
646
|
+
http_finish(h);
|
647
|
+
// upgrade protocol to raw TCP/IP
|
648
|
+
iodine_tcp_attch_uuid(uuid, handler);
|
649
|
+
goto upgraded;
|
650
|
+
}
|
651
|
+
}
|
652
|
+
break;
|
653
|
+
}
|
654
|
+
}
|
655
|
+
return 0;
|
656
|
+
|
657
|
+
upgraded:
|
658
|
+
// get body object to close it (if needed)
|
659
|
+
handler = rb_ary_entry(rbresponse, 2);
|
660
|
+
// we need to call `close` in case the object is an IO / BodyProxy
|
661
|
+
if (handler != Qnil && rb_respond_to(handler, close_method_id))
|
662
|
+
IodineCaller.call(handler, close_method_id);
|
663
|
+
return 1;
|
664
|
+
}
|
665
|
+
|
666
|
+
/* *****************************************************************************
|
667
|
+
Handling HTTP requests
|
668
|
+
***************************************************************************** */
|
669
|
+
|
670
|
+
static inline void *iodine_handle_request_in_GVL(void *handle_) {
|
671
|
+
iodine_http_request_handle_s *handle = handle_;
|
672
|
+
VALUE rbresponse = 0;
|
673
|
+
VALUE env = 0;
|
674
|
+
http_s *h = handle->h;
|
675
|
+
if (!h->udata)
|
676
|
+
goto err_not_found;
|
677
|
+
|
678
|
+
// create / register env variable
|
679
|
+
env = copy2env(handle);
|
680
|
+
// create rack.io
|
681
|
+
VALUE tmp = IodineRackIO.create(h, env);
|
682
|
+
// pass env variable to handler
|
683
|
+
rbresponse =
|
684
|
+
IodineCaller.call2((VALUE)h->udata, iodine_call_proc_id, 1, &env);
|
685
|
+
// close rack.io
|
686
|
+
IodineRackIO.close(tmp);
|
687
|
+
// test handler's return value
|
688
|
+
if (rbresponse == 0 || rbresponse == Qnil || TYPE(rbresponse) != T_ARRAY) {
|
689
|
+
goto internal_error;
|
690
|
+
}
|
691
|
+
|
692
|
+
tmp = rb_ary_entry(rbresponse, 0);
|
693
|
+
// rack will return `[:__http_defer__, fiber_to_wait_on]` in case the request needs to be paused
|
694
|
+
if (TYPE(tmp) == T_SYMBOL && tmp == http_wait_directive) {
|
695
|
+
h->fiber = (void *)IodineStore.add(rb_ary_entry(rbresponse, 1));
|
696
|
+
goto defer;
|
697
|
+
}
|
698
|
+
|
699
|
+
IodineStore.add(rbresponse);
|
700
|
+
// set response status
|
701
|
+
if (TYPE(tmp) == T_STRING) {
|
702
|
+
char *data = RSTRING_PTR(tmp);
|
703
|
+
h->status = fio_atol(&data);
|
704
|
+
} else if (TYPE(tmp) == T_FIXNUM) {
|
705
|
+
h->status = FIX2ULONG(tmp);
|
706
|
+
} else {
|
707
|
+
goto internal_error;
|
708
|
+
}
|
709
|
+
|
710
|
+
// handle header copy from ruby land to C land.
|
711
|
+
VALUE response_headers = rb_ary_entry(rbresponse, 1);
|
712
|
+
if (TYPE(response_headers) != T_HASH)
|
713
|
+
goto internal_error;
|
714
|
+
// extract the X-Sendfile header (never show original path)
|
715
|
+
// X-Sendfile support only present when iodine serves static files.
|
716
|
+
VALUE xfiles;
|
717
|
+
if (support_xsendfile &&
|
718
|
+
(xfiles = rb_hash_aref(response_headers, XSENDFILE)) != Qnil &&
|
719
|
+
TYPE(xfiles) == T_STRING) {
|
720
|
+
if (OBJ_FROZEN(response_headers)) {
|
721
|
+
response_headers = rb_hash_dup(response_headers);
|
722
|
+
}
|
723
|
+
IodineStore.add(response_headers);
|
724
|
+
handle->body = fiobj_str_new(RSTRING_PTR(xfiles), RSTRING_LEN(xfiles));
|
725
|
+
handle->type = IODINE_HTTP_XSENDFILE;
|
726
|
+
rb_hash_delete(response_headers, XSENDFILE);
|
727
|
+
// remove content length headers, as this will be controled by iodine
|
728
|
+
rb_hash_delete(response_headers, CONTENT_LENGTH_HEADER);
|
729
|
+
// review each header and write it to the response.
|
730
|
+
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(h));
|
731
|
+
IodineStore.remove(response_headers);
|
732
|
+
// send the file directly and finish
|
733
|
+
goto finish;
|
734
|
+
}
|
735
|
+
// review each header and write it to the response.
|
736
|
+
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(h));
|
737
|
+
// review for upgrade.
|
738
|
+
if ((intptr_t)h->status < 300 &&
|
739
|
+
ruby2c_review_upgrade(handle, rbresponse, env))
|
740
|
+
goto external_done;
|
741
|
+
// send the request body.
|
742
|
+
if (ruby2c_response_send(handle, rbresponse, env))
|
743
|
+
goto internal_error;
|
744
|
+
|
745
|
+
finish:
|
746
|
+
IodineStore.remove(rbresponse);
|
747
|
+
IodineStore.remove(env);
|
748
|
+
return NULL;
|
749
|
+
|
750
|
+
external_done:
|
751
|
+
IodineStore.remove(rbresponse);
|
752
|
+
IodineStore.remove(env);
|
753
|
+
handle->type = IODINE_HTTP_NONE;
|
754
|
+
return NULL;
|
755
|
+
|
756
|
+
err_not_found:
|
757
|
+
IodineStore.remove(rbresponse);
|
758
|
+
IodineStore.remove(env);
|
759
|
+
h->status = 404;
|
760
|
+
handle->type = IODINE_HTTP_ERROR;
|
761
|
+
return NULL;
|
762
|
+
|
763
|
+
internal_error:
|
764
|
+
IodineStore.remove(rbresponse);
|
765
|
+
IodineStore.remove(env);
|
766
|
+
h->status = 500;
|
767
|
+
handle->type = IODINE_HTTP_ERROR;
|
768
|
+
return NULL;
|
769
|
+
|
770
|
+
defer:
|
771
|
+
IodineStore.remove(env);
|
772
|
+
handle->type = IODINE_HTTP_WAIT;
|
773
|
+
return NULL;
|
774
|
+
}
|
775
|
+
|
776
|
+
// called once a request that was paused previously needs to be resumed
|
777
|
+
static inline void *iodine_handle_deferred_request_in_GVL(void *handle_) {
|
778
|
+
iodine_http_request_handle_s *handle = handle_;
|
779
|
+
http_s *h = handle->h;
|
780
|
+
|
781
|
+
VALUE rbresponse = rb_ivar_get((VALUE)h->fiber, fiber_result_var_id);
|
782
|
+
VALUE tmp = rb_ary_entry(rbresponse, 0);
|
783
|
+
|
784
|
+
// set response status
|
785
|
+
if (TYPE(tmp) == T_STRING) {
|
786
|
+
char *data = RSTRING_PTR(tmp);
|
787
|
+
h->status = fio_atol(&data);
|
788
|
+
} else if (TYPE(tmp) == T_FIXNUM) {
|
789
|
+
h->status = FIX2ULONG(tmp);
|
790
|
+
} else {
|
791
|
+
goto internal_error;
|
792
|
+
}
|
793
|
+
|
794
|
+
// handle header copy from ruby land to C land.
|
795
|
+
VALUE response_headers = rb_ary_entry(rbresponse, 1);
|
796
|
+
|
797
|
+
// review each header and write it to the response.
|
798
|
+
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(h));
|
799
|
+
|
800
|
+
// send the request body.
|
801
|
+
if (ruby2c_response_send(handle, rbresponse, 0))
|
802
|
+
goto internal_error;
|
803
|
+
|
804
|
+
return NULL;
|
805
|
+
|
806
|
+
internal_error:
|
807
|
+
h->status = 500;
|
808
|
+
handle->type = IODINE_HTTP_ERROR;
|
809
|
+
return NULL;
|
810
|
+
}
|
811
|
+
|
812
|
+
static inline void
|
813
|
+
iodine_perform_handle_action(iodine_http_request_handle_s handle) {
|
814
|
+
switch (handle.type) {
|
815
|
+
case IODINE_HTTP_SENDBODY: {
|
816
|
+
fio_str_info_s data = fiobj_obj2cstr(handle.body);
|
817
|
+
http_send_body(handle.h, data.data, data.len);
|
818
|
+
fiobj_free(handle.body);
|
819
|
+
break;
|
820
|
+
}
|
821
|
+
case IODINE_HTTP_XSENDFILE: {
|
822
|
+
/* remove chunked content-encoding header, if any (Rack issue #1266) */
|
823
|
+
if (fiobj_obj2cstr(
|
824
|
+
fiobj_hash_get2(handle.h->private_data.out_headers,
|
825
|
+
fiobj_obj2hash(HTTP_HEADER_CONTENT_ENCODING)))
|
826
|
+
.len == 7)
|
827
|
+
fiobj_hash_delete2(handle.h->private_data.out_headers,
|
828
|
+
fiobj_obj2hash(HTTP_HEADER_CONTENT_ENCODING));
|
829
|
+
fio_str_info_s data = fiobj_obj2cstr(handle.body);
|
830
|
+
if (http_sendfile2(handle.h, data.data, data.len, NULL, 0)) {
|
831
|
+
http_send_error(handle.h, 404);
|
832
|
+
}
|
833
|
+
fiobj_free(handle.body);
|
834
|
+
break;
|
835
|
+
}
|
836
|
+
case IODINE_HTTP_EMPTY:
|
837
|
+
http_finish(handle.h);
|
838
|
+
fiobj_free(handle.body);
|
839
|
+
break;
|
840
|
+
case IODINE_HTTP_NONE:
|
841
|
+
/* nothing to do - this had to be performed within the Ruby GIL :-( */
|
842
|
+
break;
|
843
|
+
case IODINE_HTTP_ERROR:
|
844
|
+
http_send_error(handle.h, handle.h->status);
|
845
|
+
fiobj_free(handle.body);
|
846
|
+
break;
|
847
|
+
}
|
848
|
+
}
|
849
|
+
|
850
|
+
// gets called by `http_resume`
|
851
|
+
static inline void http_resume_deferred_request_handler(http_s *h) {
|
852
|
+
iodine_http_request_handle_s handle = (iodine_http_request_handle_s){
|
853
|
+
.h = h,
|
854
|
+
.upgrade = IODINE_UPGRADE_NONE,
|
855
|
+
};
|
856
|
+
|
857
|
+
IodineCaller.enterGVL((void *(*)(void *))iodine_handle_deferred_request_in_GVL,
|
858
|
+
&handle);
|
859
|
+
|
860
|
+
iodine_perform_handle_action(handle);
|
861
|
+
}
|
862
|
+
|
863
|
+
static inline void on_iodine_request_id_message(fio_msg_s *msg) {
|
864
|
+
http_resume((http_pause_handle_s *)msg->udata1, http_resume_deferred_request_handler, NULL);
|
865
|
+
}
|
866
|
+
|
867
|
+
static inline void http_close_deferred_request_handler(void *sub) {
|
868
|
+
fio_unsubscribe((subscription_s *)sub);
|
869
|
+
}
|
870
|
+
|
871
|
+
// when Ruby sends a message into the `request_id` channel means the fiber attached to
|
872
|
+
// to the `http_s h` var can be resumed
|
873
|
+
static inline void http_pause_request_handler(http_pause_handle_s *s) {
|
874
|
+
subscription_s *sub = fio_subscribe(.channel = fiobj_obj2cstr(s->h->request_id),
|
875
|
+
.on_message = on_iodine_request_id_message,
|
876
|
+
.udata1 = (void *)s);
|
877
|
+
fio_uuid_link(s->uuid, (void *)sub, http_close_deferred_request_handler);
|
878
|
+
}
|
879
|
+
|
880
|
+
static void on_rack_request(http_s *h) {
|
881
|
+
iodine_http_request_handle_s handle = (iodine_http_request_handle_s){
|
882
|
+
.h = h,
|
883
|
+
.upgrade = IODINE_UPGRADE_NONE,
|
884
|
+
};
|
885
|
+
IodineCaller.enterGVL((void *(*)(void *))iodine_handle_request_in_GVL,
|
886
|
+
&handle);
|
887
|
+
|
888
|
+
if (handle.type == IODINE_HTTP_WAIT) {
|
889
|
+
http_pause(handle.h, http_pause_request_handler);
|
890
|
+
} else {
|
891
|
+
iodine_perform_handle_action(handle);
|
892
|
+
}
|
893
|
+
}
|
894
|
+
|
895
|
+
static void on_rack_upgrade(http_s *h, char *proto, size_t len) {
|
896
|
+
iodine_http_request_handle_s handle = (iodine_http_request_handle_s){.h = h};
|
897
|
+
if (len == 9 && (proto[1] == 'e' || proto[1] == 'E')) {
|
898
|
+
handle.upgrade = IODINE_UPGRADE_WEBSOCKET;
|
899
|
+
} else if (len == 3 && proto[0] == 's') {
|
900
|
+
handle.upgrade = IODINE_UPGRADE_SSE;
|
901
|
+
}
|
902
|
+
/* when we stop supporting custom Upgrade headers: */
|
903
|
+
// else {
|
904
|
+
// http_send_error(h, 400);
|
905
|
+
// return;
|
906
|
+
// }
|
907
|
+
IodineCaller.enterGVL(iodine_handle_request_in_GVL, &handle);
|
908
|
+
iodine_perform_handle_action(handle);
|
909
|
+
(void)proto;
|
910
|
+
(void)len;
|
911
|
+
}
|
912
|
+
|
913
|
+
/* *****************************************************************************
|
914
|
+
Rack `env` Template Initialization
|
915
|
+
***************************************************************************** */
|
916
|
+
|
917
|
+
static void initialize_env_template(void) {
|
918
|
+
if (env_template_no_upgrade)
|
919
|
+
return;
|
920
|
+
env_template_no_upgrade = rb_hash_new();
|
921
|
+
IodineStore.add(env_template_no_upgrade);
|
922
|
+
|
923
|
+
#define add_str_to_env(env, key, value) \
|
924
|
+
{ \
|
925
|
+
VALUE k = rb_enc_str_new((key), strlen((key)), IodineBinaryEncoding); \
|
926
|
+
rb_obj_freeze(k); \
|
927
|
+
VALUE v = rb_enc_str_new((value), strlen((value)), IodineBinaryEncoding); \
|
928
|
+
rb_obj_freeze(v); \
|
929
|
+
rb_hash_aset(env, k, v); \
|
930
|
+
}
|
931
|
+
#define add_value_to_env(env, key, value) \
|
932
|
+
{ \
|
933
|
+
VALUE k = rb_enc_str_new((key), strlen((key)), IodineBinaryEncoding); \
|
934
|
+
rb_obj_freeze(k); \
|
935
|
+
rb_hash_aset((env), k, value); \
|
936
|
+
}
|
937
|
+
|
938
|
+
/* Set global template */
|
939
|
+
rb_hash_aset(env_template_no_upgrade, RACK_UPGRADE_Q, Qnil);
|
940
|
+
rb_hash_aset(env_template_no_upgrade, RACK_UPGRADE, Qnil);
|
941
|
+
{
|
942
|
+
/* add the rack.version */
|
943
|
+
static VALUE rack_version = 0;
|
944
|
+
if (!rack_version) {
|
945
|
+
rack_version = rb_ary_new(); // rb_ary_new is Ruby 2.0 compatible
|
946
|
+
rb_ary_push(rack_version, INT2FIX(1));
|
947
|
+
rb_ary_push(rack_version, INT2FIX(3));
|
948
|
+
rb_global_variable(&rack_version);
|
949
|
+
rb_ary_freeze(rack_version);
|
950
|
+
}
|
951
|
+
add_value_to_env(env_template_no_upgrade, "rack.version", rack_version);
|
952
|
+
}
|
953
|
+
|
954
|
+
{
|
955
|
+
const char *sn = getenv("SCRIPT_NAME");
|
956
|
+
if (!sn || (sn[0] == '/' && sn[1] == 0)) {
|
957
|
+
sn = "";
|
958
|
+
}
|
959
|
+
add_str_to_env(env_template_no_upgrade, "SCRIPT_NAME", sn);
|
960
|
+
}
|
961
|
+
rb_hash_aset(env_template_no_upgrade, IODINE_R_INPUT, IODINE_R_INPUT_DEFAULT);
|
962
|
+
add_value_to_env(env_template_no_upgrade, "rack.errors", rb_stderr);
|
963
|
+
add_value_to_env(env_template_no_upgrade, "rack.hijack?", Qtrue);
|
964
|
+
add_value_to_env(env_template_no_upgrade, "rack.multiprocess", Qtrue);
|
965
|
+
add_value_to_env(env_template_no_upgrade, "rack.multithread", Qtrue);
|
966
|
+
add_value_to_env(env_template_no_upgrade, "rack.run_once", Qfalse);
|
967
|
+
/* default schema to http, it might be updated later */
|
968
|
+
rb_hash_aset(env_template_no_upgrade, R_URL_SCHEME, HTTP_SCHEME);
|
969
|
+
/* placeholders... minimize rehashing*/
|
970
|
+
rb_hash_aset(env_template_no_upgrade, HTTP_VERSION, QUERY_STRING);
|
971
|
+
rb_hash_aset(env_template_no_upgrade, IODINE_R_HIJACK, QUERY_STRING);
|
972
|
+
rb_hash_aset(env_template_no_upgrade, PATH_INFO, QUERY_STRING);
|
973
|
+
rb_hash_aset(env_template_no_upgrade, QUERY_STRING, QUERY_STRING);
|
974
|
+
rb_hash_aset(env_template_no_upgrade, REMOTE_ADDR, QUERY_STRING);
|
975
|
+
rb_hash_aset(env_template_no_upgrade, REQUEST_METHOD, QUERY_STRING);
|
976
|
+
rb_hash_aset(env_template_no_upgrade, SERVER_NAME, QUERY_STRING);
|
977
|
+
rb_hash_aset(env_template_no_upgrade, SERVER_PROTOCOL, QUERY_STRING);
|
978
|
+
rb_hash_aset(env_template_no_upgrade, IODINE_REQUEST_ID, QUERY_STRING);
|
979
|
+
|
980
|
+
/* WebSocket upgrade support */
|
981
|
+
env_template_websockets = rb_hash_dup(env_template_no_upgrade);
|
982
|
+
IodineStore.add(env_template_websockets);
|
983
|
+
rb_hash_aset(env_template_websockets, RACK_UPGRADE_Q, RACK_UPGRADE_WEBSOCKET);
|
984
|
+
|
985
|
+
/* SSE upgrade support */
|
986
|
+
env_template_sse = rb_hash_dup(env_template_no_upgrade);
|
987
|
+
IodineStore.add(env_template_sse);
|
988
|
+
rb_hash_aset(env_template_sse, RACK_UPGRADE_Q, RACK_UPGRADE_SSE);
|
989
|
+
|
990
|
+
#undef add_value_to_env
|
991
|
+
#undef add_str_to_env
|
992
|
+
}
|
993
|
+
|
994
|
+
/* *****************************************************************************
|
995
|
+
Listenninng to HTTP
|
996
|
+
*****************************************************************************
|
997
|
+
*/
|
998
|
+
|
999
|
+
static void free_iodine_http(http_settings_s *s) {
|
1000
|
+
IodineStore.remove((VALUE)s->udata);
|
1001
|
+
}
|
1002
|
+
|
1003
|
+
// clang-format off
|
1004
|
+
/**
|
1005
|
+
Listens to incoming HTTP connections and handles incoming requests using the
|
1006
|
+
Rack specification.
|
1007
|
+
|
1008
|
+
This is delegated to a lower level C HTTP and Websocket implementation, no
|
1009
|
+
Ruby object will be crated except the `env` object required by the Rack
|
1010
|
+
specifications.
|
1011
|
+
|
1012
|
+
Accepts a single Hash argument with the following properties:
|
1013
|
+
|
1014
|
+
(it's possible to set default values using the {Iodine::DEFAULT_HTTP_ARGS} Hash)
|
1015
|
+
|
1016
|
+
app:: the Rack application that handles incoming requests. Default: `nil`.
|
1017
|
+
port:: the port to listen to. Default: 3000.
|
1018
|
+
address:: the address to bind to. Default: binds to all possible addresses.
|
1019
|
+
log:: enable response logging (Hijacked sockets aren't logged). Default: off.
|
1020
|
+
public:: The root public folder for static file service. Default: none.
|
1021
|
+
timeout:: Timeout for inactive HTTP/1.x connections. Defaults: 40 seconds.
|
1022
|
+
max_body:: The maximum body size for incoming HTTP messages in bytes. Default: ~50Mib.
|
1023
|
+
max_headers:: The maximum total header length for incoming HTTP messages. Default: ~64Kib.
|
1024
|
+
max_msg:: The maximum Websocket message size allowed. Default: ~250Kib.
|
1025
|
+
ping:: The Websocket `ping` interval. Default: 40 seconds.
|
1026
|
+
|
1027
|
+
Either the `app` or the `public` properties are required. If niether exists,
|
1028
|
+
the function will fail. If both exist, Iodine will serve static files as well
|
1029
|
+
as dynamic requests.
|
1030
|
+
|
1031
|
+
When using the static file server, it's possible to serve `gzip` versions of
|
1032
|
+
the static files by saving a compressed version with the `gz` extension (i.e.
|
1033
|
+
`styles.css.gz`).
|
1034
|
+
|
1035
|
+
`gzip` will only be served to clients tat support the `gzip` transfer
|
1036
|
+
encoding.
|
1037
|
+
|
1038
|
+
Once HTTP/2 is supported (planned, but probably very far away), HTTP/2
|
1039
|
+
timeouts will be dynamically managed by Iodine. The `timeout` option is only
|
1040
|
+
relevant to HTTP/1.x connections.
|
1041
|
+
*/
|
1042
|
+
intptr_t iodine_http_listen(iodine_connection_args_s args){
|
1043
|
+
// clang-format on
|
1044
|
+
if (args.public.data) {
|
1045
|
+
rb_hash_aset(env_template_no_upgrade, XSENDFILE_TYPE, XSENDFILE);
|
1046
|
+
rb_hash_aset(env_template_no_upgrade, XSENDFILE_TYPE_HEADER, XSENDFILE);
|
1047
|
+
support_xsendfile = 1;
|
1048
|
+
}
|
1049
|
+
IodineStore.add(args.handler);
|
1050
|
+
#ifdef __MINGW32__
|
1051
|
+
intptr_t uuid = http_listen(
|
1052
|
+
args.port.data, args.address.data, .on_request = on_rack_request,
|
1053
|
+
.on_upgrade = on_rack_upgrade, .udata = (void *)args.handler,
|
1054
|
+
.timeout = args.timeout, .ws_timeout = args.ping,
|
1055
|
+
.ws_max_msg_size = args.max_msg, .max_header_size = args.max_headers,
|
1056
|
+
.on_finish = free_iodine_http, .log = args.log,
|
1057
|
+
.max_body_size = args.max_body, .public_folder = args.public.data);
|
1058
|
+
#else
|
1059
|
+
intptr_t uuid = http_listen(
|
1060
|
+
args.port.data, args.address.data, .on_request = on_rack_request,
|
1061
|
+
.on_upgrade = on_rack_upgrade, .udata = (void *)args.handler,
|
1062
|
+
.tls = args.tls, .timeout = args.timeout, .ws_timeout = args.ping,
|
1063
|
+
.ws_max_msg_size = args.max_msg, .max_header_size = args.max_headers,
|
1064
|
+
.on_finish = free_iodine_http, .log = args.log,
|
1065
|
+
.max_body_size = args.max_body, .public_folder = args.public.data);
|
1066
|
+
#endif
|
1067
|
+
if (uuid == -1)
|
1068
|
+
return uuid;
|
1069
|
+
|
1070
|
+
if ((args.handler == Qnil || args.handler == Qfalse)) {
|
1071
|
+
FIO_LOG_WARNING("(listen) no handler / app, the HTTP service on port %s "
|
1072
|
+
"will only serve "
|
1073
|
+
"static files.",
|
1074
|
+
args.port.data ? args.port.data : args.address.data);
|
1075
|
+
}
|
1076
|
+
if (args.public.data) {
|
1077
|
+
FIO_LOG_INFO("Serving static files from %s", args.public.data);
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
return uuid;
|
1081
|
+
}
|
1082
|
+
|
1083
|
+
/* *****************************************************************************
|
1084
|
+
HTTP Websocket Connect
|
1085
|
+
***************************************************************************** */
|
1086
|
+
|
1087
|
+
typedef struct {
|
1088
|
+
FIOBJ method;
|
1089
|
+
FIOBJ headers;
|
1090
|
+
FIOBJ cookies;
|
1091
|
+
FIOBJ body;
|
1092
|
+
VALUE io;
|
1093
|
+
} request_data_s;
|
1094
|
+
|
1095
|
+
static request_data_s *request_data_create(iodine_connection_args_s *args) {
|
1096
|
+
request_data_s *r = fio_malloc(sizeof(*r));
|
1097
|
+
FIO_ASSERT_ALLOC(r);
|
1098
|
+
VALUE io =
|
1099
|
+
iodine_connection_new(.type = IODINE_CONNECTION_WEBSOCKET, .arg = NULL,
|
1100
|
+
.handler = args->handler, .env = Qnil, .uuid = 0);
|
1101
|
+
|
1102
|
+
*r = (request_data_s){
|
1103
|
+
.method = fiobj_str_new(args->method.data, args->method.len),
|
1104
|
+
.headers = fiobj_dup(args->headers),
|
1105
|
+
.cookies = fiobj_dup(args->cookies),
|
1106
|
+
.body = fiobj_str_new(args->body.data, args->body.len),
|
1107
|
+
.io = io,
|
1108
|
+
};
|
1109
|
+
return r;
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
static void request_data_destroy(request_data_s *r) {
|
1113
|
+
fiobj_free(r->method);
|
1114
|
+
fiobj_free(r->body);
|
1115
|
+
fiobj_free(r->headers);
|
1116
|
+
fiobj_free(r->cookies);
|
1117
|
+
fio_free(r);
|
1118
|
+
}
|
1119
|
+
|
1120
|
+
static int each_header_ws_client_task(FIOBJ val, void *h_) {
|
1121
|
+
http_s *h = h_;
|
1122
|
+
FIOBJ key = fiobj_hash_key_in_loop();
|
1123
|
+
http_set_header(h, key, fiobj_dup(val));
|
1124
|
+
return 0;
|
1125
|
+
}
|
1126
|
+
static int each_cookie_ws_client_task(FIOBJ val, void *h_) {
|
1127
|
+
http_s *h = h_;
|
1128
|
+
FIOBJ key = fiobj_hash_key_in_loop();
|
1129
|
+
fio_str_info_s n = fiobj_obj2cstr(key);
|
1130
|
+
fio_str_info_s v = fiobj_obj2cstr(val);
|
1131
|
+
http_set_cookie(h, .name = n.data, .name_len = n.len, .value = v.data,
|
1132
|
+
.value_len = v.len);
|
1133
|
+
return 0;
|
1134
|
+
}
|
1135
|
+
|
1136
|
+
static void ws_client_http_connected(http_s *h) {
|
1137
|
+
request_data_s *s = h->udata;
|
1138
|
+
if (!s)
|
1139
|
+
return;
|
1140
|
+
h->udata = http_settings(h)->udata = NULL;
|
1141
|
+
if (!h->path) {
|
1142
|
+
h->path = fiobj_str_new("/", 1);
|
1143
|
+
}
|
1144
|
+
/* TODO: add headers and cookies */
|
1145
|
+
fiobj_each1(s->headers, 0, each_header_ws_client_task, h);
|
1146
|
+
fiobj_each1(s->headers, 0, each_cookie_ws_client_task, h);
|
1147
|
+
if (s->io && s->io != Qnil)
|
1148
|
+
http_upgrade2ws(
|
1149
|
+
h, .on_message = iodine_ws_on_message, .on_open = iodine_ws_on_open,
|
1150
|
+
.on_ready = iodine_ws_on_ready, .on_shutdown = iodine_ws_on_shutdown,
|
1151
|
+
.on_close = iodine_ws_on_close, .udata = (void *)s->io);
|
1152
|
+
request_data_destroy(s);
|
1153
|
+
}
|
1154
|
+
|
1155
|
+
static void ws_client_http_connection_finished(http_settings_s *settings) {
|
1156
|
+
if (!settings)
|
1157
|
+
return;
|
1158
|
+
request_data_s *s = settings->udata;
|
1159
|
+
if (s) {
|
1160
|
+
if (s->io && s->io != Qnil)
|
1161
|
+
iodine_connection_fire_event(s->io, IODINE_CONNECTION_ON_CLOSE, Qnil);
|
1162
|
+
request_data_destroy(s);
|
1163
|
+
}
|
1164
|
+
}
|
1165
|
+
|
1166
|
+
/** Connects to a (remote) WebSocket service. */
|
1167
|
+
intptr_t iodine_ws_connect(iodine_connection_args_s args) {
|
1168
|
+
// http_connect(url, unixaddr, struct http_settings_s)
|
1169
|
+
uint8_t is_unix_socket = 0;
|
1170
|
+
if (memchr(args.address.data, '/', args.address.len)) {
|
1171
|
+
is_unix_socket = 1;
|
1172
|
+
}
|
1173
|
+
FIOBJ url_tmp = FIOBJ_INVALID;
|
1174
|
+
if (!args.url.data) {
|
1175
|
+
url_tmp = fiobj_str_buf(64);
|
1176
|
+
#ifndef __MINGW32__
|
1177
|
+
if (args.tls)
|
1178
|
+
fiobj_str_write(url_tmp, "wss://", 6);
|
1179
|
+
else
|
1180
|
+
#endif
|
1181
|
+
fiobj_str_write(url_tmp, "ws://", 5);
|
1182
|
+
if (!is_unix_socket) {
|
1183
|
+
fiobj_str_write(url_tmp, args.address.data, args.address.len);
|
1184
|
+
if (args.port.data) {
|
1185
|
+
fiobj_str_write(url_tmp, ":", 1);
|
1186
|
+
fiobj_str_write(url_tmp, args.port.data, args.port.len);
|
1187
|
+
}
|
1188
|
+
}
|
1189
|
+
if (args.path.data)
|
1190
|
+
fiobj_str_write(url_tmp, args.path.data, args.path.len);
|
1191
|
+
else
|
1192
|
+
fiobj_str_write(url_tmp, "/", 1);
|
1193
|
+
args.url = fiobj_obj2cstr(url_tmp);
|
1194
|
+
}
|
1195
|
+
|
1196
|
+
#ifdef __MINGW32__
|
1197
|
+
intptr_t uuid =
|
1198
|
+
http_connect(args.url.data, (is_unix_socket ? args.address.data : NULL),
|
1199
|
+
.udata = request_data_create(&args),
|
1200
|
+
.on_response = ws_client_http_connected,
|
1201
|
+
.on_finish = ws_client_http_connection_finished);
|
1202
|
+
#else
|
1203
|
+
intptr_t uuid = http_connect(
|
1204
|
+
args.url.data, (is_unix_socket ? args.address.data : NULL),
|
1205
|
+
.udata = request_data_create(&args),
|
1206
|
+
.on_response = ws_client_http_connected,
|
1207
|
+
.on_finish = ws_client_http_connection_finished, .tls = args.tls);
|
1208
|
+
#endif
|
1209
|
+
fiobj_free(url_tmp);
|
1210
|
+
return uuid;
|
1211
|
+
}
|
1212
|
+
|
1213
|
+
/* *****************************************************************************
|
1214
|
+
Initialization
|
1215
|
+
***************************************************************************** */
|
1216
|
+
|
1217
|
+
void iodine_init_http(void) {
|
1218
|
+
|
1219
|
+
rack_autoset(REQUEST_METHOD);
|
1220
|
+
rack_autoset(PATH_INFO);
|
1221
|
+
rack_autoset(QUERY_STRING);
|
1222
|
+
rack_autoset(SERVER_NAME);
|
1223
|
+
rack_autoset(SERVER_PORT);
|
1224
|
+
rack_autoset(CONTENT_LENGTH);
|
1225
|
+
rack_autoset(CONTENT_TYPE);
|
1226
|
+
rack_autoset(SERVER_PROTOCOL);
|
1227
|
+
rack_autoset(HTTP_VERSION);
|
1228
|
+
rack_autoset(REMOTE_ADDR);
|
1229
|
+
|
1230
|
+
rack_autoset(HTTP_ACCEPT);
|
1231
|
+
rack_autoset(HTTP_USER_AGENT);
|
1232
|
+
rack_autoset(HTTP_ACCEPT_ENCODING);
|
1233
|
+
rack_autoset(HTTP_ACCEPT_LANGUAGE);
|
1234
|
+
rack_autoset(HTTP_CONNECTION);
|
1235
|
+
rack_autoset(HTTP_HOST);
|
1236
|
+
|
1237
|
+
rack_autoset(IODINE_REQUEST_ID);
|
1238
|
+
|
1239
|
+
rack_set(HTTP_SCHEME, "http");
|
1240
|
+
rack_set(HTTPS_SCHEME, "https");
|
1241
|
+
rack_set(QUERY_ESTRING, "");
|
1242
|
+
rack_set(R_URL_SCHEME, "rack.url_scheme");
|
1243
|
+
rack_set(R_INPUT, "rack.input");
|
1244
|
+
rack_set(XSENDFILE, "X-Sendfile");
|
1245
|
+
rack_set(XSENDFILE_TYPE, "sendfile.type");
|
1246
|
+
rack_set(XSENDFILE_TYPE_HEADER, "HTTP_X_SENDFILE_TYPE");
|
1247
|
+
rack_set(CONTENT_LENGTH_HEADER, "Content-Length");
|
1248
|
+
|
1249
|
+
rack_set(IODINE_R_INPUT, "rack.input");
|
1250
|
+
rack_set(IODINE_R_HIJACK_IO, "rack.hijack_io");
|
1251
|
+
rack_set(IODINE_R_HIJACK, "rack.hijack");
|
1252
|
+
rack_set(IODINE_R_HIJACK_CB, "iodine.hijack_cb");
|
1253
|
+
|
1254
|
+
rack_set(RACK_UPGRADE, "rack.upgrade");
|
1255
|
+
rack_set(RACK_UPGRADE_Q, "rack.upgrade?");
|
1256
|
+
rack_set_sym(RACK_UPGRADE_SSE, "sse");
|
1257
|
+
rack_set_sym(RACK_UPGRADE_WEBSOCKET, "websocket");
|
1258
|
+
|
1259
|
+
UPGRADE_TCP = IodineStore.add(rb_str_new("upgrade.tcp", 11));
|
1260
|
+
|
1261
|
+
hijack_func_sym = ID2SYM(rb_intern("_hijack"));
|
1262
|
+
close_method_id = rb_intern("close");
|
1263
|
+
each_method_id = rb_intern("each");
|
1264
|
+
attach_method_id = rb_intern("attach_fd");
|
1265
|
+
iodine_call_proc_id = rb_intern("call");
|
1266
|
+
fiber_result_var_id = rb_intern("@__result");
|
1267
|
+
http_wait_directive = ID2SYM(rb_intern("__http_defer__"));
|
1268
|
+
|
1269
|
+
IodineUTF8Encoding = rb_enc_find("UTF-8");
|
1270
|
+
IodineBinaryEncoding = rb_enc_find("binary");
|
1271
|
+
|
1272
|
+
{
|
1273
|
+
VALUE STRIO_CLASS = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
1274
|
+
IODINE_R_INPUT_DEFAULT = rb_str_new_static("", 0);
|
1275
|
+
IODINE_R_INPUT_DEFAULT =
|
1276
|
+
rb_funcallv(STRIO_CLASS, rb_intern("new"), 1, &IODINE_R_INPUT_DEFAULT);
|
1277
|
+
rb_global_variable(&IODINE_R_INPUT_DEFAULT);
|
1278
|
+
}
|
1279
|
+
initialize_env_template();
|
1280
|
+
}
|