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