iodine 0.4.19 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +22 -0
- data/LIMITS.md +19 -9
- data/README.md +92 -77
- data/SPEC-PubSub-Draft.md +113 -0
- data/SPEC-Websocket-Draft.md +127 -143
- data/bin/http-hello +0 -1
- data/bin/raw-rbhttp +1 -1
- data/bin/raw_broadcast +8 -10
- data/bin/updated api +2 -2
- data/bin/ws-broadcast +2 -4
- data/bin/ws-echo +2 -2
- data/examples/config.ru +13 -13
- data/examples/echo.ru +5 -6
- data/examples/hello.ru +2 -3
- data/examples/info.md +316 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/redis.ru +9 -9
- data/examples/shootout.ru +45 -11
- data/ext/iodine/defer.c +194 -297
- data/ext/iodine/defer.h +61 -53
- data/ext/iodine/evio.c +0 -260
- data/ext/iodine/evio.h +50 -22
- data/ext/iodine/evio_callbacks.c +26 -0
- data/ext/iodine/evio_epoll.c +251 -0
- data/ext/iodine/evio_kqueue.c +193 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +1420 -542
- data/ext/iodine/facil.h +151 -64
- data/ext/iodine/fio_ary.h +418 -0
- data/ext/iodine/{base64.c → fio_base64.c} +33 -24
- data/ext/iodine/{base64.h → fio_base64.h} +6 -7
- data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
- data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
- data/ext/iodine/fio_hashmap.h +759 -0
- data/ext/iodine/fio_json_parser.h +651 -0
- data/ext/iodine/fio_llist.h +257 -0
- data/ext/iodine/fio_mem.c +672 -0
- data/ext/iodine/fio_mem.h +140 -0
- data/ext/iodine/fio_random.c +248 -0
- data/ext/iodine/{random.h → fio_random.h} +11 -14
- data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
- data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
- data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
- data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
- data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
- data/ext/iodine/fio_siphash.h +18 -0
- data/ext/iodine/fio_tmpfile.h +38 -0
- data/ext/iodine/fiobj.h +24 -7
- data/ext/iodine/fiobj4sock.h +23 -0
- data/ext/iodine/fiobj_ary.c +143 -226
- data/ext/iodine/fiobj_ary.h +17 -16
- data/ext/iodine/fiobj_data.c +1160 -0
- data/ext/iodine/fiobj_data.h +164 -0
- data/ext/iodine/fiobj_hash.c +298 -406
- data/ext/iodine/fiobj_hash.h +101 -54
- data/ext/iodine/fiobj_json.c +478 -601
- data/ext/iodine/fiobj_json.h +34 -9
- data/ext/iodine/fiobj_numbers.c +383 -51
- data/ext/iodine/fiobj_numbers.h +87 -11
- data/ext/iodine/fiobj_str.c +423 -184
- data/ext/iodine/fiobj_str.h +81 -32
- data/ext/iodine/fiobject.c +273 -522
- data/ext/iodine/fiobject.h +477 -112
- data/ext/iodine/http.c +2243 -83
- data/ext/iodine/http.h +842 -121
- data/ext/iodine/http1.c +810 -385
- data/ext/iodine/http1.h +16 -39
- data/ext/iodine/http1_parser.c +146 -74
- data/ext/iodine/http1_parser.h +15 -4
- data/ext/iodine/http_internal.c +1258 -0
- data/ext/iodine/http_internal.h +226 -0
- data/ext/iodine/http_mime_parser.h +341 -0
- data/ext/iodine/iodine.c +86 -68
- data/ext/iodine/iodine.h +26 -11
- data/ext/iodine/iodine_helpers.c +8 -7
- data/ext/iodine/iodine_http.c +487 -324
- data/ext/iodine/iodine_json.c +304 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_protocol.c +107 -45
- data/ext/iodine/iodine_pubsub.c +526 -225
- data/ext/iodine/iodine_pubsub.h +10 -0
- data/ext/iodine/iodine_websockets.c +268 -510
- data/ext/iodine/iodine_websockets.h +2 -4
- data/ext/iodine/pubsub.c +726 -432
- data/ext/iodine/pubsub.h +85 -103
- data/ext/iodine/rb-call.c +4 -4
- data/ext/iodine/rb-defer.c +46 -22
- data/ext/iodine/rb-fiobj2rb.h +117 -0
- data/ext/iodine/rb-rack-io.c +73 -238
- data/ext/iodine/rb-rack-io.h +2 -2
- data/ext/iodine/rb-registry.c +35 -93
- data/ext/iodine/rb-registry.h +1 -0
- data/ext/iodine/redis_engine.c +742 -304
- data/ext/iodine/redis_engine.h +42 -39
- data/ext/iodine/resp_parser.h +311 -0
- data/ext/iodine/sock.c +627 -490
- data/ext/iodine/sock.h +345 -297
- data/ext/iodine/spnlock.inc +15 -4
- data/ext/iodine/websocket_parser.h +16 -20
- data/ext/iodine/websockets.c +188 -257
- data/ext/iodine/websockets.h +24 -133
- data/lib/iodine.rb +52 -7
- data/lib/iodine/cli.rb +6 -24
- data/lib/iodine/json.rb +40 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +5 -3
- data/lib/rack/handler/iodine.rb +58 -13
- metadata +38 -48
- data/bin/ws-shootout +0 -107
- data/examples/broadcast.ru +0 -56
- data/ext/iodine/bscrypt-common.h +0 -116
- data/ext/iodine/bscrypt.h +0 -49
- data/ext/iodine/fio2resp.c +0 -60
- data/ext/iodine/fio2resp.h +0 -51
- data/ext/iodine/fio_dict.c +0 -446
- data/ext/iodine/fio_dict.h +0 -99
- data/ext/iodine/fio_hash_table.h +0 -370
- data/ext/iodine/fio_list.h +0 -111
- data/ext/iodine/fiobj_internal.h +0 -280
- data/ext/iodine/fiobj_primitives.c +0 -131
- data/ext/iodine/fiobj_primitives.h +0 -55
- data/ext/iodine/fiobj_sym.c +0 -135
- data/ext/iodine/fiobj_sym.h +0 -60
- data/ext/iodine/hex.c +0 -124
- data/ext/iodine/hex.h +0 -70
- data/ext/iodine/http1_request.c +0 -81
- data/ext/iodine/http1_request.h +0 -58
- data/ext/iodine/http1_response.c +0 -417
- data/ext/iodine/http1_response.h +0 -95
- data/ext/iodine/http_request.c +0 -111
- data/ext/iodine/http_request.h +0 -102
- data/ext/iodine/http_response.c +0 -1703
- data/ext/iodine/http_response.h +0 -250
- data/ext/iodine/misc.c +0 -182
- data/ext/iodine/misc.h +0 -74
- data/ext/iodine/random.c +0 -208
- data/ext/iodine/redis_connection.c +0 -278
- data/ext/iodine/redis_connection.h +0 -86
- data/ext/iodine/resp.c +0 -842
- data/ext/iodine/resp.h +0 -261
- data/ext/iodine/siphash.c +0 -154
- data/ext/iodine/siphash.h +0 -22
- data/ext/iodine/xor-crypt.c +0 -193
- data/ext/iodine/xor-crypt.h +0 -107
data/ext/iodine/iodine.c
CHANGED
@@ -15,16 +15,24 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
15
15
|
|
16
16
|
VALUE Iodine;
|
17
17
|
VALUE IodineBase;
|
18
|
-
|
18
|
+
|
19
|
+
VALUE iodine_force_var_id;
|
20
|
+
VALUE iodine_channel_var_id;
|
21
|
+
VALUE iodine_pattern_var_id;
|
22
|
+
VALUE iodine_text_var_id;
|
23
|
+
VALUE iodine_binary_var_id;
|
24
|
+
VALUE iodine_engine_var_id;
|
25
|
+
VALUE iodine_message_var_id;
|
19
26
|
|
20
27
|
ID iodine_fd_var_id;
|
28
|
+
ID iodine_cdata_var_id;
|
21
29
|
ID iodine_timeout_var_id;
|
22
30
|
ID iodine_call_proc_id;
|
23
31
|
ID iodine_new_func_id;
|
24
32
|
ID iodine_on_open_func_id;
|
25
33
|
ID iodine_on_message_func_id;
|
26
34
|
ID iodine_on_data_func_id;
|
27
|
-
ID
|
35
|
+
ID iodine_on_drained_func_id;
|
28
36
|
ID iodine_on_shutdown_func_id;
|
29
37
|
ID iodine_on_close_func_id;
|
30
38
|
ID iodine_ping_func_id;
|
@@ -149,17 +157,11 @@ static VALUE iodine_run(VALUE self) {
|
|
149
157
|
/* *****************************************************************************
|
150
158
|
Idling
|
151
159
|
***************************************************************************** */
|
152
|
-
#include "
|
160
|
+
#include "fio_llist.h"
|
153
161
|
#include "spnlock.inc"
|
154
162
|
|
155
|
-
typedef struct {
|
156
|
-
fio_list_s node;
|
157
|
-
VALUE block;
|
158
|
-
} iodine_idle_block_s;
|
159
|
-
|
160
163
|
static spn_lock_i iodine_on_idle_lock = SPN_LOCK_INIT;
|
161
|
-
static
|
162
|
-
FIO_LIST_INIT_STATIC(iodine_on_idle_list);
|
164
|
+
static fio_ls_s iodine_on_idle_list = FIO_LS_INIT(iodine_on_idle_list);
|
163
165
|
|
164
166
|
/**
|
165
167
|
Schedules a single occuring event for the next idle cycle.
|
@@ -174,22 +176,20 @@ i.e.
|
|
174
176
|
*/
|
175
177
|
VALUE iodine_sched_on_idle(VALUE self) {
|
176
178
|
rb_need_block();
|
177
|
-
|
178
|
-
|
179
|
-
Registry.add(b->block);
|
179
|
+
VALUE block = rb_block_proc();
|
180
|
+
Registry.add(block);
|
180
181
|
spn_lock(&iodine_on_idle_lock);
|
181
|
-
|
182
|
+
fio_ls_push(&iodine_on_idle_list, (void *)block);
|
182
183
|
spn_unlock(&iodine_on_idle_lock);
|
183
|
-
return
|
184
|
+
return block;
|
184
185
|
(void)self;
|
185
186
|
}
|
186
187
|
|
187
188
|
static void iodine_on_idle(void) {
|
188
|
-
iodine_idle_block_s *b;
|
189
189
|
spn_lock(&iodine_on_idle_lock);
|
190
|
-
while ((
|
191
|
-
|
192
|
-
|
190
|
+
while (fio_ls_any(&iodine_on_idle_list)) {
|
191
|
+
VALUE block = (VALUE)fio_ls_shift(&iodine_on_idle_list);
|
192
|
+
defer(iodine_perform_deferred, (void *)block, NULL);
|
193
193
|
}
|
194
194
|
spn_unlock(&iodine_on_idle_lock);
|
195
195
|
}
|
@@ -202,14 +202,17 @@ Running the server
|
|
202
202
|
|
203
203
|
static volatile int sock_io_thread = 0;
|
204
204
|
static pthread_t sock_io_pthread;
|
205
|
+
typedef struct {
|
206
|
+
size_t threads;
|
207
|
+
size_t processes;
|
208
|
+
} iodine_start_settings_s;
|
205
209
|
|
206
210
|
static void *iodine_io_thread(void *arg) {
|
207
211
|
(void)arg;
|
208
212
|
struct timespec tm;
|
209
|
-
// static const struct timespec tm = {.tv_nsec = 524288UL};
|
210
213
|
while (sock_io_thread) {
|
211
214
|
sock_flush_all();
|
212
|
-
tm = (struct timespec){.tv_nsec =
|
215
|
+
tm = (struct timespec){.tv_nsec = 0, .tv_sec = 1};
|
213
216
|
nanosleep(&tm, NULL);
|
214
217
|
}
|
215
218
|
return NULL;
|
@@ -224,39 +227,17 @@ static void iodine_join_io_thread(void) {
|
|
224
227
|
pthread_join(sock_io_pthread, NULL);
|
225
228
|
}
|
226
229
|
|
227
|
-
static void *srv_start_no_gvl(void *
|
228
|
-
|
229
|
-
// collect requested settings
|
230
|
-
VALUE rb_th_i = rb_iv_get(Iodine, "@threads");
|
231
|
-
VALUE rb_pr_i = rb_iv_get(Iodine, "@processes");
|
232
|
-
ssize_t threads = (TYPE(rb_th_i) == T_FIXNUM) ? FIX2LONG(rb_th_i) : 0;
|
233
|
-
ssize_t processes = (TYPE(rb_pr_i) == T_FIXNUM) ? FIX2LONG(rb_pr_i) : 0;
|
234
|
-
// print a warnning if settings are sub optimal
|
235
|
-
#ifdef _SC_NPROCESSORS_ONLN
|
236
|
-
size_t cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
|
237
|
-
if (processes <= 0)
|
238
|
-
processes = 0;
|
239
|
-
if (threads <= 0)
|
240
|
-
threads = 0;
|
241
|
-
|
242
|
-
if (processes && threads && cpu_count > 0 &&
|
243
|
-
(((size_t)processes << 1) < cpu_count ||
|
244
|
-
(size_t)processes > (cpu_count << 1)))
|
245
|
-
fprintf(stderr,
|
246
|
-
"\n* Performance warnning:\n"
|
247
|
-
" - This computer reports %lu available CPUs... "
|
248
|
-
"they will not be fully utilized.\n",
|
249
|
-
cpu_count);
|
250
|
-
#else
|
251
|
-
if (processes <= 0)
|
252
|
-
processes = 0;
|
253
|
-
if (threads <= 0)
|
254
|
-
threads = 0;
|
255
|
-
#endif
|
230
|
+
static void *srv_start_no_gvl(void *s_) {
|
231
|
+
iodine_start_settings_s *s = s_;
|
256
232
|
sock_io_thread = 1;
|
257
233
|
defer(iodine_start_io_thread, NULL, NULL);
|
258
234
|
fprintf(stderr, "\n");
|
259
|
-
|
235
|
+
if (s->processes == 1 || (s->processes == 0 && s->threads > 0)) {
|
236
|
+
/* single worker */
|
237
|
+
RubyCaller.call(Iodine, rb_intern("before_fork"));
|
238
|
+
RubyCaller.call(Iodine, rb_intern("after_fork"));
|
239
|
+
}
|
240
|
+
facil_run(.threads = s->threads, .processes = s->processes,
|
260
241
|
.on_idle = iodine_on_idle, .on_finish = iodine_join_io_thread);
|
261
242
|
return NULL;
|
262
243
|
}
|
@@ -296,8 +277,13 @@ static int iodine_review_rack_app(void) {
|
|
296
277
|
rb_ivar_get(rack, rb_intern("@ws_timeout")));
|
297
278
|
rb_hash_aset(opt, ID2SYM(rb_intern("timeout")),
|
298
279
|
rb_ivar_get(rack, rb_intern("@ws_timeout")));
|
299
|
-
|
280
|
+
rb_hash_aset(opt, ID2SYM(rb_intern("max_headers")),
|
281
|
+
rb_ivar_get(rack, rb_intern("@max_headers")));
|
282
|
+
if (rb_funcall2(Iodine, rb_intern("listen2http"), 1, &opt) == Qfalse) {
|
283
|
+
Registry.remove(opt);
|
300
284
|
return -1;
|
285
|
+
}
|
286
|
+
Registry.remove(opt);
|
301
287
|
return 0;
|
302
288
|
}
|
303
289
|
|
@@ -308,12 +294,32 @@ Starts the Iodine event loop. This will hang the thread until an interrupt
|
|
308
294
|
Returns the Iodine module.
|
309
295
|
*/
|
310
296
|
static VALUE iodine_start(VALUE self) {
|
297
|
+
/* re-register the Rack::Push namespace to point at Iodine */
|
298
|
+
if (rb_const_defined(rb_cObject, rb_intern("Rack"))) {
|
299
|
+
VALUE rack = rb_const_get(rb_cObject, rb_intern("Rack"));
|
300
|
+
if (rack != Qnil) {
|
301
|
+
if (rb_const_defined(rack, rb_intern("PubSub"))) {
|
302
|
+
rb_const_remove(rack, rb_intern("PubSub"));
|
303
|
+
}
|
304
|
+
rb_const_set(rack, rb_intern("PubSub"), Iodine);
|
305
|
+
}
|
306
|
+
}
|
311
307
|
/* for the special Iodine::Rack object and backwards compatibility */
|
312
308
|
if (iodine_review_rack_app()) {
|
313
309
|
fprintf(stderr, "ERROR: (iodine) cann't start Iodine::Rack.\n");
|
314
310
|
return Qnil;
|
315
311
|
}
|
316
|
-
|
312
|
+
|
313
|
+
VALUE rb_th_i = rb_iv_get(Iodine, "@threads");
|
314
|
+
VALUE rb_pr_i = rb_iv_get(Iodine, "@processes");
|
315
|
+
|
316
|
+
iodine_start_settings_s s = {
|
317
|
+
.threads = ((TYPE(rb_th_i) == T_FIXNUM) ? FIX2INT(rb_th_i) : 0),
|
318
|
+
.processes = ((TYPE(rb_pr_i) == T_FIXNUM) ? FIX2INT(rb_pr_i) : 0)};
|
319
|
+
|
320
|
+
RubyCaller.set_gvl_state(1);
|
321
|
+
RubyCaller.leave_gvl(srv_start_no_gvl, (void *)&s);
|
322
|
+
|
317
323
|
return self;
|
318
324
|
}
|
319
325
|
|
@@ -342,39 +348,52 @@ static void patch_env(void) {
|
|
342
348
|
#ifdef __APPLE__
|
343
349
|
/* patch for dealing with the High Sierra `fork` limitations */
|
344
350
|
void *obj_c_runtime = dlopen("Foundation.framework/Foundation", RTLD_LAZY);
|
351
|
+
(void)obj_c_runtime;
|
345
352
|
#endif
|
346
353
|
}
|
347
354
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
355
|
+
/* *****************************************************************************
|
356
|
+
Ruby loads the library and invokes the Init_<lib_name> function...
|
357
|
+
|
358
|
+
Here we connect all the C code to the Ruby interface, completing the bridge
|
359
|
+
between Lib-Server and Ruby.
|
360
|
+
***************************************************************************** */
|
353
361
|
void Init_iodine(void) {
|
362
|
+
// Set GVL for main thread
|
363
|
+
RubyCaller.set_gvl_state(1);
|
354
364
|
// load any environment specific patches
|
355
365
|
patch_env();
|
356
366
|
// initialize globally used IDs, for faster access to the Ruby layer.
|
357
|
-
|
367
|
+
iodine_buff_var_id = rb_intern("scrtbuffer");
|
358
368
|
iodine_call_proc_id = rb_intern("call");
|
369
|
+
iodine_cdata_var_id = rb_intern("iodine_cdata");
|
370
|
+
iodine_fd_var_id = rb_intern("iodine_fd");
|
359
371
|
iodine_new_func_id = rb_intern("new");
|
360
|
-
|
361
|
-
iodine_on_message_func_id = rb_intern("on_message");
|
372
|
+
iodine_on_close_func_id = rb_intern("on_close");
|
362
373
|
iodine_on_data_func_id = rb_intern("on_data");
|
374
|
+
iodine_on_message_func_id = rb_intern("on_message");
|
375
|
+
iodine_on_open_func_id = rb_intern("on_open");
|
376
|
+
iodine_on_drained_func_id = rb_intern("on_drained");
|
363
377
|
iodine_on_shutdown_func_id = rb_intern("on_shutdown");
|
364
|
-
iodine_on_close_func_id = rb_intern("on_close");
|
365
|
-
iodine_on_ready_func_id = rb_intern("on_ready");
|
366
378
|
iodine_ping_func_id = rb_intern("ping");
|
367
|
-
iodine_buff_var_id = rb_intern("scrtbuffer");
|
368
379
|
iodine_timeout_var_id = rb_intern("@timeout");
|
369
|
-
iodine_to_s_method_id = rb_intern("to_s");
|
370
380
|
iodine_to_i_func_id = rb_intern("to_i");
|
381
|
+
iodine_to_s_method_id = rb_intern("to_s");
|
382
|
+
|
383
|
+
iodine_binary_var_id = ID2SYM(rb_intern("binary"));
|
384
|
+
iodine_channel_var_id = ID2SYM(rb_intern("channel"));
|
385
|
+
iodine_engine_var_id = ID2SYM(rb_intern("engine"));
|
386
|
+
iodine_force_var_id = ID2SYM(rb_intern("encoding"));
|
387
|
+
iodine_message_var_id = ID2SYM(rb_intern("message"));
|
388
|
+
iodine_pattern_var_id = ID2SYM(rb_intern("pattern"));
|
389
|
+
iodine_text_var_id = ID2SYM(rb_intern("text"));
|
371
390
|
|
372
391
|
IodineBinaryEncodingIndex = rb_enc_find_index("binary");
|
373
392
|
IodineUTF8EncodingIndex = rb_enc_find_index("UTF-8");
|
374
393
|
IodineBinaryEncoding = rb_enc_find("binary");
|
375
394
|
IodineUTF8Encoding = rb_enc_find("UTF-8");
|
376
395
|
|
377
|
-
// The core Iodine module wraps
|
396
|
+
// The core Iodine module wraps facil.io functionality and little more.
|
378
397
|
Iodine = rb_define_module("Iodine");
|
379
398
|
|
380
399
|
// the Iodine singleton functions
|
@@ -386,8 +405,7 @@ void Init_iodine(void) {
|
|
386
405
|
rb_define_module_function(Iodine, "run_every", iodine_run_every, -1);
|
387
406
|
rb_define_module_function(Iodine, "on_idle", iodine_sched_on_idle, 0);
|
388
407
|
|
389
|
-
|
390
|
-
// define the Server Ruby class.
|
408
|
+
/// Iodine::Base is for internal use.
|
391
409
|
IodineBase = rb_define_module_under(Iodine, "Base");
|
392
410
|
rb_define_module_function(IodineBase, "db_print_registry",
|
393
411
|
iodine_print_registry, 0);
|
data/ext/iodine/iodine.h
CHANGED
@@ -30,22 +30,30 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
30
30
|
|
31
31
|
extern VALUE Iodine;
|
32
32
|
extern VALUE IodineBase;
|
33
|
-
extern VALUE Iodine_Version;
|
34
33
|
|
35
|
-
extern
|
36
|
-
extern
|
34
|
+
extern VALUE iodine_binary_var_id;
|
35
|
+
extern VALUE iodine_channel_var_id;
|
36
|
+
extern VALUE iodine_engine_var_id;
|
37
|
+
extern VALUE iodine_force_var_id;
|
38
|
+
extern VALUE iodine_message_var_id;
|
39
|
+
extern VALUE iodine_pattern_var_id;
|
40
|
+
extern VALUE iodine_text_var_id;
|
41
|
+
|
42
|
+
extern ID iodine_buff_var_id;
|
37
43
|
extern ID iodine_call_proc_id;
|
44
|
+
extern ID iodine_cdata_var_id;
|
45
|
+
extern ID iodine_fd_var_id;
|
38
46
|
extern ID iodine_new_func_id;
|
39
|
-
extern ID
|
40
|
-
extern ID iodine_on_message_func_id;
|
47
|
+
extern ID iodine_on_close_func_id;
|
41
48
|
extern ID iodine_on_data_func_id;
|
42
|
-
extern ID
|
49
|
+
extern ID iodine_on_message_func_id;
|
50
|
+
extern ID iodine_on_open_func_id;
|
51
|
+
extern ID iodine_on_drained_func_id;
|
43
52
|
extern ID iodine_on_shutdown_func_id;
|
44
|
-
extern ID iodine_on_close_func_id;
|
45
53
|
extern ID iodine_ping_func_id;
|
46
|
-
extern ID
|
47
|
-
extern ID iodine_to_s_method_id;
|
54
|
+
extern ID iodine_timeout_var_id;
|
48
55
|
extern ID iodine_to_i_func_id;
|
56
|
+
extern ID iodine_to_s_method_id;
|
49
57
|
|
50
58
|
extern rb_encoding *IodineBinaryEncoding;
|
51
59
|
extern rb_encoding *IodineUTF8Encoding;
|
@@ -53,10 +61,17 @@ extern int IodineBinaryEncodingIndex;
|
|
53
61
|
extern int IodineUTF8EncodingIndex;
|
54
62
|
|
55
63
|
UNUSED_FUNC static inline void iodine_set_fd(VALUE handler, intptr_t fd) {
|
56
|
-
rb_ivar_set(handler, iodine_fd_var_id,
|
64
|
+
rb_ivar_set(handler, iodine_fd_var_id, LL2NUM((long)fd));
|
57
65
|
}
|
58
66
|
UNUSED_FUNC static inline intptr_t iodine_get_fd(VALUE handler) {
|
59
|
-
return ((intptr_t)
|
67
|
+
return ((intptr_t)NUM2LL(rb_ivar_get(handler, iodine_fd_var_id)));
|
68
|
+
}
|
69
|
+
|
70
|
+
UNUSED_FUNC static inline void iodine_set_cdata(VALUE handler, void *protocol) {
|
71
|
+
rb_ivar_set(handler, iodine_cdata_var_id, ULL2NUM((uintptr_t)protocol));
|
72
|
+
}
|
73
|
+
UNUSED_FUNC static inline void *iodine_get_cdata(VALUE handler) {
|
74
|
+
return ((void *)NUM2ULL(rb_ivar_get(handler, iodine_cdata_var_id)));
|
60
75
|
}
|
61
76
|
|
62
77
|
#endif
|
data/ext/iodine/iodine_helpers.c
CHANGED
@@ -140,13 +140,14 @@ static VALUE date_str(int argc, VALUE *argv, VALUE self) {
|
|
140
140
|
if (TYPE(argv[0]) != T_FIXNUM)
|
141
141
|
argv[0] = rb_funcallv(argv[0], iodine_to_i_func_id, 0, NULL);
|
142
142
|
Check_Type(argv[0], T_FIXNUM);
|
143
|
-
last_tick =
|
143
|
+
last_tick =
|
144
|
+
FIX2ULONG(argv[0]) ? FIX2ULONG(argv[0]) : facil_last_tick().tv_sec;
|
144
145
|
} else
|
145
|
-
last_tick = facil_last_tick();
|
146
|
+
last_tick = facil_last_tick().tv_sec;
|
146
147
|
VALUE str = rb_str_buf_new(32);
|
147
148
|
struct tm tm;
|
148
149
|
|
149
|
-
http_gmtime(
|
150
|
+
http_gmtime(last_tick, &tm);
|
150
151
|
size_t len = http_date2str(RSTRING_PTR(str), &tm);
|
151
152
|
rb_str_set_len(str, len);
|
152
153
|
return str;
|
@@ -168,11 +169,11 @@ Since Iodine uses time caching within it's reactor, using the default value
|
|
168
169
|
static VALUE iodine_rfc2822(VALUE self, VALUE rtm) {
|
169
170
|
time_t last_tick;
|
170
171
|
rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
|
171
|
-
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : facil_last_tick();
|
172
|
+
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : facil_last_tick().tv_sec;
|
172
173
|
VALUE str = rb_str_buf_new(34);
|
173
174
|
struct tm tm;
|
174
175
|
|
175
|
-
http_gmtime(
|
176
|
+
http_gmtime(last_tick, &tm);
|
176
177
|
size_t len = http_date2rfc2822(RSTRING_PTR(str), &tm);
|
177
178
|
rb_str_set_len(str, len);
|
178
179
|
return str;
|
@@ -194,11 +195,11 @@ Since Iodine uses time caching within it's reactor, using the default value
|
|
194
195
|
static VALUE iodine_rfc2109(VALUE self, VALUE rtm) {
|
195
196
|
time_t last_tick;
|
196
197
|
rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
|
197
|
-
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : facil_last_tick();
|
198
|
+
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : facil_last_tick().tv_sec;
|
198
199
|
VALUE str = rb_str_buf_new(32);
|
199
200
|
struct tm tm;
|
200
201
|
|
201
|
-
http_gmtime(
|
202
|
+
http_gmtime(last_tick, &tm);
|
202
203
|
size_t len = http_date2rfc2109(RSTRING_PTR(str), &tm);
|
203
204
|
rb_str_set_len(str, len);
|
204
205
|
return str;
|
data/ext/iodine/iodine_http.c
CHANGED
@@ -5,22 +5,26 @@ License: MIT
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
6
6
|
*/
|
7
7
|
#include "iodine_http.h"
|
8
|
+
#include "fio_mem.h"
|
9
|
+
#include "http.h"
|
10
|
+
#include "iodine_json.h"
|
8
11
|
#include "iodine_websockets.h"
|
9
12
|
#include "rb-rack-io.h"
|
10
13
|
|
11
14
|
#include <arpa/inet.h>
|
15
|
+
#include <ctype.h>
|
16
|
+
#include <stdlib.h>
|
17
|
+
#include <string.h>
|
12
18
|
#include <sys/socket.h>
|
13
19
|
|
14
20
|
/* *****************************************************************************
|
15
21
|
Available Globals
|
16
22
|
***************************************************************************** */
|
17
|
-
VALUE IodineHTTP;
|
23
|
+
static VALUE IodineHTTP;
|
18
24
|
|
19
25
|
typedef struct {
|
20
26
|
VALUE app;
|
21
27
|
VALUE env;
|
22
|
-
unsigned long max_msg : 56;
|
23
|
-
unsigned ping : 8;
|
24
28
|
} iodine_http_settings_s;
|
25
29
|
|
26
30
|
/* these three are used also by rb-rack-io.c */
|
@@ -28,23 +32,35 @@ VALUE IODINE_R_HIJACK;
|
|
28
32
|
VALUE IODINE_R_HIJACK_IO;
|
29
33
|
VALUE IODINE_R_HIJACK_CB;
|
30
34
|
|
31
|
-
VALUE UPGRADE_TCP;
|
32
|
-
VALUE UPGRADE_TCP_Q;
|
33
|
-
VALUE UPGRADE_WEBSOCKET;
|
34
|
-
VALUE UPGRADE_WEBSOCKET_Q;
|
35
|
+
static VALUE UPGRADE_TCP;
|
36
|
+
static VALUE UPGRADE_TCP_Q;
|
37
|
+
static VALUE UPGRADE_WEBSOCKET;
|
38
|
+
static VALUE UPGRADE_WEBSOCKET_Q;
|
39
|
+
static VALUE RACK_UPGRADE;
|
40
|
+
static VALUE RACK_UPGRADE_Q;
|
41
|
+
static VALUE RACK_UPGRADE_SSE;
|
42
|
+
static VALUE RACK_UPGRADE_WEBSOCKET;
|
35
43
|
|
36
44
|
static VALUE hijack_func_sym;
|
37
|
-
static ID to_fixnum_func_id;
|
38
45
|
static ID close_method_id;
|
39
46
|
static ID each_method_id;
|
40
47
|
static ID attach_method_id;
|
41
48
|
|
49
|
+
static VALUE env_template_no_upgrade;
|
50
|
+
static VALUE env_template_websockets;
|
51
|
+
static VALUE env_template_sse;
|
52
|
+
|
53
|
+
static uint8_t support_xsendfile = 0;
|
54
|
+
|
42
55
|
#define rack_declare(rack_name) static VALUE rack_name
|
43
56
|
|
44
57
|
#define rack_set(rack_name, str) \
|
45
58
|
(rack_name) = rb_enc_str_new((str), strlen((str)), IodineBinaryEncoding); \
|
46
59
|
rb_global_variable(&(rack_name)); \
|
47
60
|
rb_obj_freeze(rack_name);
|
61
|
+
#define rack_set_sym(rack_name, sym) \
|
62
|
+
(rack_name) = rb_id2sym(rb_intern2((sym), strlen((sym)))); \
|
63
|
+
rb_global_variable(&(rack_name));
|
48
64
|
|
49
65
|
#define rack_autoset(rack_name) rack_set((rack_name), #rack_name)
|
50
66
|
|
@@ -67,9 +83,27 @@ rack_declare(CONTENT_TYPE);
|
|
67
83
|
rack_declare(R_URL_SCHEME); // rack.url_scheme
|
68
84
|
rack_declare(R_INPUT); // rack.input
|
69
85
|
rack_declare(XSENDFILE); // for X-Sendfile support
|
86
|
+
rack_declare(XSENDFILE_TYPE); // for X-Sendfile support
|
87
|
+
rack_declare(XSENDFILE_TYPE_HEADER); // for X-Sendfile support
|
70
88
|
rack_declare(CONTENT_LENGTH_HEADER); // for X-Sendfile support
|
71
|
-
|
72
|
-
|
89
|
+
|
90
|
+
/* used internally to handle requests */
|
91
|
+
typedef struct {
|
92
|
+
http_s *h;
|
93
|
+
FIOBJ body;
|
94
|
+
enum iodine_http_response_type_enum {
|
95
|
+
IODINE_HTTP_NONE,
|
96
|
+
IODINE_HTTP_SENDBODY,
|
97
|
+
IODINE_HTTP_XSENDFILE,
|
98
|
+
IODINE_HTTP_EMPTY,
|
99
|
+
IODINE_HTTP_ERROR,
|
100
|
+
} type;
|
101
|
+
enum iodine_upgrade_type_enum {
|
102
|
+
IODINE_UPGRADE_NONE = 0,
|
103
|
+
IODINE_UPGRADE_WEBSOCKET,
|
104
|
+
IODINE_UPGRADE_SSE,
|
105
|
+
} upgrade;
|
106
|
+
} iodine_http_request_handle_s;
|
73
107
|
|
74
108
|
/* *****************************************************************************
|
75
109
|
Copying data from the C request to the Rack's ENV
|
@@ -77,116 +111,184 @@ Copying data from the C request to the Rack's ENV
|
|
77
111
|
|
78
112
|
#define to_upper(c) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~32) : (c))
|
79
113
|
|
80
|
-
|
81
|
-
VALUE env =
|
114
|
+
int iodine_copy2env_task(FIOBJ o, void *env_) {
|
115
|
+
VALUE env = (VALUE)env_;
|
116
|
+
FIOBJ name = fiobj_hash_key_in_loop();
|
117
|
+
fio_cstr_s tmp = fiobj_obj2cstr(name);
|
118
|
+
VALUE hname = (VALUE)0;
|
119
|
+
if (tmp.len > 59) {
|
120
|
+
char *buf = fio_malloc(tmp.len + 5);
|
121
|
+
memcpy(buf, "HTTP_", 5);
|
122
|
+
for (size_t i = 0; i < tmp.len; ++i) {
|
123
|
+
buf[i + 5] = (tmp.data[i] == '-') ? '_' : to_upper(tmp.data[i]);
|
124
|
+
}
|
125
|
+
hname = rb_enc_str_new(buf, tmp.len + 5, IodineBinaryEncoding);
|
126
|
+
fio_free(buf);
|
127
|
+
} else {
|
128
|
+
char buf[64];
|
129
|
+
memcpy(buf, "HTTP_", 5);
|
130
|
+
for (size_t i = 0; i < tmp.len; ++i) {
|
131
|
+
buf[i + 5] = (tmp.data[i] == '-') ? '_' : to_upper(tmp.data[i]);
|
132
|
+
}
|
133
|
+
hname = rb_enc_str_new(buf, tmp.len + 5, IodineBinaryEncoding);
|
134
|
+
}
|
135
|
+
|
136
|
+
if (FIOBJ_TYPE_IS(o, FIOBJ_T_STRING)) {
|
137
|
+
tmp = fiobj_obj2cstr(o);
|
138
|
+
rb_hash_aset(env, hname,
|
139
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
140
|
+
|
141
|
+
} else {
|
142
|
+
/* it's an array */
|
143
|
+
VALUE ary = rb_ary_new();
|
144
|
+
rb_hash_aset(env, hname, ary);
|
145
|
+
size_t count = fiobj_ary_count(o);
|
146
|
+
for (size_t i = 0; i < count; ++i) {
|
147
|
+
tmp = fiobj_obj2cstr(fiobj_ary_index(o, i));
|
148
|
+
rb_ary_push(ary, rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
149
|
+
}
|
150
|
+
}
|
151
|
+
return 0;
|
152
|
+
}
|
153
|
+
static inline VALUE copy2env(iodine_http_request_handle_s *handle) {
|
154
|
+
VALUE env;
|
155
|
+
http_s *h = handle->h;
|
156
|
+
switch (handle->upgrade) {
|
157
|
+
case IODINE_UPGRADE_WEBSOCKET:
|
158
|
+
env = rb_hash_dup(env_template_websockets);
|
159
|
+
break;
|
160
|
+
case IODINE_UPGRADE_SSE:
|
161
|
+
env = rb_hash_dup(env_template_sse);
|
162
|
+
break;
|
163
|
+
case IODINE_UPGRADE_NONE:
|
164
|
+
env = rb_hash_dup(env_template_no_upgrade);
|
165
|
+
break;
|
166
|
+
}
|
82
167
|
Registry.add(env);
|
83
|
-
|
168
|
+
|
169
|
+
fio_cstr_s tmp;
|
84
170
|
char *pos = NULL;
|
85
|
-
const char *reader = NULL;
|
86
171
|
/* Copy basic data */
|
172
|
+
tmp = fiobj_obj2cstr(h->method);
|
87
173
|
rb_hash_aset(env, REQUEST_METHOD,
|
88
|
-
rb_enc_str_new(
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
rb_hash_aset(env, SERVER_PROTOCOL, hname);
|
108
|
-
rb_hash_aset(env, HTTP_VERSION, hname);
|
109
|
-
|
110
|
-
// Suppoer for Ruby web-console.
|
111
|
-
hname = rb_str_buf_new(64);
|
112
|
-
sock_peer_addr_s addrinfo = sock_peer_addr(request->fd);
|
113
|
-
if (addrinfo.addrlen &&
|
114
|
-
inet_ntop(
|
115
|
-
addrinfo.addr->sa_family,
|
116
|
-
addrinfo.addr->sa_family == AF_INET
|
117
|
-
? (void *)&((struct sockaddr_in *)addrinfo.addr)->sin_addr
|
118
|
-
: (void *)&((struct sockaddr_in6 *)addrinfo.addr)->sin6_addr,
|
119
|
-
RSTRING_PTR(hname), 64)) {
|
120
|
-
rb_str_set_len(hname, strlen(RSTRING_PTR(hname)));
|
121
|
-
rb_hash_aset(env, REMOTE_ADDR, hname);
|
174
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
175
|
+
tmp = fiobj_obj2cstr(h->path);
|
176
|
+
rb_hash_aset(env, PATH_INFO,
|
177
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
178
|
+
if (h->query) {
|
179
|
+
tmp = fiobj_obj2cstr(h->query);
|
180
|
+
rb_hash_aset(env, QUERY_STRING,
|
181
|
+
tmp.len
|
182
|
+
? rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding)
|
183
|
+
: QUERY_ESTRING);
|
184
|
+
} else {
|
185
|
+
rb_hash_aset(env, QUERY_STRING, QUERY_ESTRING);
|
186
|
+
}
|
187
|
+
{
|
188
|
+
// HTTP version appears twice
|
189
|
+
tmp = fiobj_obj2cstr(h->version);
|
190
|
+
VALUE hname = rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding);
|
191
|
+
rb_hash_aset(env, SERVER_PROTOCOL, hname);
|
192
|
+
rb_hash_aset(env, HTTP_VERSION, hname);
|
122
193
|
}
|
123
194
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
195
|
+
{ // Support for Ruby web-console.
|
196
|
+
char buf[64];
|
197
|
+
buf[63] = 0;
|
198
|
+
sock_peer_addr_s addrinfo = http_peer_addr(h);
|
199
|
+
if (addrinfo.addrlen &&
|
200
|
+
inet_ntop(
|
201
|
+
addrinfo.addr->sa_family,
|
202
|
+
addrinfo.addr->sa_family == AF_INET
|
203
|
+
? (void *)&((struct sockaddr_in *)addrinfo.addr)->sin_addr
|
204
|
+
: (void *)&((struct sockaddr_in6 *)addrinfo.addr)->sin6_addr,
|
205
|
+
buf, 64)) {
|
206
|
+
rb_hash_aset(env, REMOTE_ADDR, rb_str_new(buf, strlen(buf)));
|
207
|
+
}
|
131
208
|
}
|
132
209
|
|
133
|
-
|
134
|
-
|
210
|
+
/* setup input IO + hijack support */
|
211
|
+
{
|
212
|
+
VALUE m;
|
213
|
+
rb_hash_aset(env, R_INPUT, (m = IodineRackIO.create(h, env)));
|
214
|
+
m = rb_obj_method(m, hijack_func_sym);
|
215
|
+
rb_hash_aset(env, IODINE_R_HIJACK, m);
|
216
|
+
}
|
135
217
|
|
136
218
|
/* handle the HOST header, including the possible host:#### format*/
|
137
|
-
|
219
|
+
static uint64_t host_hash = 0;
|
220
|
+
if (!host_hash)
|
221
|
+
host_hash = fio_siphash("host", 4);
|
222
|
+
tmp = fiobj_obj2cstr(fiobj_hash_get2(h->headers, host_hash));
|
223
|
+
pos = tmp.data;
|
138
224
|
while (*pos && *pos != ':')
|
139
225
|
pos++;
|
140
226
|
if (*pos == 0) {
|
141
|
-
rb_hash_aset(
|
142
|
-
|
143
|
-
rb_enc_str_new(request->host, request->host_len, IodineBinaryEncoding));
|
227
|
+
rb_hash_aset(env, SERVER_NAME,
|
228
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
144
229
|
rb_hash_aset(env, SERVER_PORT, QUERY_ESTRING);
|
145
230
|
} else {
|
146
|
-
rb_hash_aset(
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
231
|
+
rb_hash_aset(
|
232
|
+
env, SERVER_NAME,
|
233
|
+
rb_enc_str_new(tmp.data, pos - tmp.data, IodineBinaryEncoding));
|
234
|
+
++pos;
|
235
|
+
rb_hash_aset(
|
236
|
+
env, SERVER_PORT,
|
237
|
+
rb_enc_str_new(pos, tmp.len - (pos - tmp.data), IodineBinaryEncoding));
|
153
238
|
}
|
154
239
|
|
155
|
-
/*
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
if (
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
}
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
if (
|
240
|
+
/* remove special headers */
|
241
|
+
{
|
242
|
+
static uint64_t content_length_hash = 0;
|
243
|
+
if (!content_length_hash)
|
244
|
+
content_length_hash = fio_siphash("content-length", 14);
|
245
|
+
FIOBJ cl = fiobj_hash_get2(h->headers, content_length_hash);
|
246
|
+
if (cl) {
|
247
|
+
tmp = fiobj_obj2cstr(fiobj_hash_get2(h->headers, content_length_hash));
|
248
|
+
if (tmp.data) {
|
249
|
+
rb_hash_aset(env, CONTENT_LENGTH,
|
250
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
251
|
+
fiobj_hash_delete2(h->headers, content_length_hash);
|
252
|
+
}
|
253
|
+
}
|
254
|
+
}
|
255
|
+
{
|
256
|
+
static uint64_t content_type_hash = 0;
|
257
|
+
if (!content_type_hash)
|
258
|
+
content_type_hash = fio_siphash("content-type", 12);
|
259
|
+
FIOBJ ct = fiobj_hash_get2(h->headers, content_type_hash);
|
260
|
+
if (ct) {
|
261
|
+
tmp = fiobj_obj2cstr(ct);
|
262
|
+
if (tmp.len && tmp.data) {
|
263
|
+
fprintf(stderr, "Content type: %s\n", tmp.data);
|
264
|
+
rb_hash_aset(env, CONTENT_TYPE,
|
265
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
266
|
+
fiobj_hash_delete2(h->headers, content_type_hash);
|
267
|
+
}
|
268
|
+
}
|
269
|
+
}
|
270
|
+
/* handle scheme / sepcial forwarding headers */
|
271
|
+
{
|
272
|
+
FIOBJ objtmp;
|
273
|
+
static uint64_t xforward_hash = 0;
|
274
|
+
if (!xforward_hash)
|
275
|
+
xforward_hash = fio_siphash("x-forwarded-proto", 27);
|
276
|
+
static uint64_t forward_hash = 0;
|
277
|
+
if (!forward_hash)
|
278
|
+
forward_hash = fio_siphash("forwarded", 9);
|
279
|
+
if ((objtmp = fiobj_hash_get2(h->headers, xforward_hash))) {
|
280
|
+
tmp = fiobj_obj2cstr(objtmp);
|
281
|
+
if (tmp.len >= 5 && !strncasecmp(tmp.data, "https", 5)) {
|
178
282
|
rb_hash_aset(env, R_URL_SCHEME, HTTPS_SCHEME);
|
179
|
-
} else if (
|
180
|
-
*((uint32_t *)header.value) == *((uint32_t *)"http")) {
|
283
|
+
} else if (tmp.len == 4 && !strncasecmp(tmp.data, "http", 4)) {
|
181
284
|
rb_hash_aset(env, R_URL_SCHEME, HTTP_SCHEME);
|
182
285
|
} else {
|
183
286
|
rb_hash_aset(env, R_URL_SCHEME,
|
184
|
-
rb_enc_str_new(
|
185
|
-
IodineBinaryEncoding));
|
287
|
+
rb_enc_str_new(tmp.data, tmp.len, IodineBinaryEncoding));
|
186
288
|
}
|
187
|
-
} else if (
|
188
|
-
|
189
|
-
pos =
|
289
|
+
} else if ((objtmp = fiobj_hash_get2(h->headers, forward_hash))) {
|
290
|
+
tmp = fiobj_obj2cstr(objtmp);
|
291
|
+
pos = tmp.data;
|
190
292
|
if (pos) {
|
191
293
|
while (*pos) {
|
192
294
|
if (((*(pos++) | 32) == 'p') && ((*(pos++) | 32) == 'r') &&
|
@@ -209,25 +311,17 @@ static inline VALUE copy2env(http_request_s *request, VALUE template) {
|
|
209
311
|
}
|
210
312
|
}
|
211
313
|
}
|
314
|
+
} else {
|
212
315
|
}
|
213
|
-
|
214
|
-
hname = rb_str_buf_new(6 + header.name_len);
|
215
|
-
memcpy(RSTRING_PTR(hname), "HTTP_", 5);
|
216
|
-
pos = RSTRING_PTR(hname) + 5;
|
217
|
-
reader = header.name;
|
218
|
-
while (*reader) {
|
219
|
-
*(pos++) = *reader == '-' ? '_' : to_upper(*reader);
|
220
|
-
++reader;
|
221
|
-
}
|
222
|
-
*pos = 0;
|
223
|
-
rb_str_set_len(hname, 5 + header.name_len);
|
224
|
-
rb_hash_aset(
|
225
|
-
env, hname,
|
226
|
-
rb_enc_str_new(header.value, header.value_len, IodineBinaryEncoding));
|
227
|
-
header = http_request_header_next(request);
|
228
316
|
}
|
317
|
+
|
318
|
+
/* add all remianing headers */
|
319
|
+
fiobj_each1(h->headers, 0, iodine_copy2env_task, (void *)env);
|
229
320
|
return env;
|
230
321
|
}
|
322
|
+
#undef add_str_to_env
|
323
|
+
#undef add_value_to_env
|
324
|
+
#undef add_header_to_env
|
231
325
|
|
232
326
|
/* *****************************************************************************
|
233
327
|
Handling the HTTP response
|
@@ -235,7 +329,8 @@ Handling the HTTP response
|
|
235
329
|
|
236
330
|
// itterate through the headers and add them to the response buffer
|
237
331
|
// (we are recycling the request's buffer)
|
238
|
-
static int for_each_header_data(VALUE key, VALUE val, VALUE
|
332
|
+
static int for_each_header_data(VALUE key, VALUE val, VALUE h_) {
|
333
|
+
http_s *h = (http_s *)h_;
|
239
334
|
// fprintf(stderr, "For_each - headers\n");
|
240
335
|
if (TYPE(key) != T_STRING)
|
241
336
|
key = RubyCaller.call(key, iodine_to_s_method_id);
|
@@ -246,30 +341,37 @@ static int for_each_header_data(VALUE key, VALUE val, VALUE _res) {
|
|
246
341
|
if (TYPE(val) != T_STRING)
|
247
342
|
return ST_STOP;
|
248
343
|
}
|
344
|
+
char *key_s = RSTRING_PTR(key);
|
345
|
+
int key_len = RSTRING_LEN(key);
|
249
346
|
char *val_s = RSTRING_PTR(val);
|
250
347
|
int val_len = RSTRING_LEN(val);
|
348
|
+
// make the headers lowercase
|
349
|
+
FIOBJ name = fiobj_str_new(key_s, key_len);
|
350
|
+
{
|
351
|
+
fio_cstr_s tmp = fiobj_obj2cstr(name);
|
352
|
+
for (int i = 0; i < key_len; ++i) {
|
353
|
+
tmp.data[i] = tolower(tmp.data[i]);
|
354
|
+
}
|
355
|
+
}
|
251
356
|
// scan the value for newline (\n) delimiters
|
252
357
|
int pos_s = 0, pos_e = 0;
|
253
358
|
while (pos_e < val_len) {
|
254
359
|
// scanning for newline (\n) delimiters
|
255
360
|
while (pos_e < val_len && val_s[pos_e] != '\n')
|
256
361
|
pos_e++;
|
257
|
-
|
258
|
-
(void *)_res, .name = RSTRING_PTR(key), .name_len = RSTRING_LEN(key),
|
259
|
-
.value = val_s + pos_s, .value_len = pos_e - pos_s);
|
362
|
+
http_set_header(h, name, fiobj_str_new(val_s + pos_s, pos_e - pos_s));
|
260
363
|
// fprintf(stderr, "For_each - headers: wrote header\n");
|
261
364
|
// move forward (skip the '\n' if exists)
|
262
|
-
|
263
|
-
pos_e
|
365
|
+
++pos_e;
|
366
|
+
pos_s = pos_e;
|
264
367
|
}
|
368
|
+
fiobj_free(name);
|
265
369
|
// no errors, return 0
|
266
370
|
return ST_CONTINUE;
|
267
371
|
}
|
268
372
|
|
269
373
|
// writes the body to the response object
|
270
|
-
static VALUE for_each_body_string(VALUE str, VALUE
|
271
|
-
(void)(argv);
|
272
|
-
(void)(argc);
|
374
|
+
static VALUE for_each_body_string(VALUE str, VALUE body_) {
|
273
375
|
// fprintf(stderr, "For_each - body\n");
|
274
376
|
// write body
|
275
377
|
if (TYPE(str) != T_STRING) {
|
@@ -277,51 +379,45 @@ static VALUE for_each_body_string(VALUE str, VALUE _res, int argc, VALUE argv) {
|
|
277
379
|
"response body was not a String\n");
|
278
380
|
return Qfalse;
|
279
381
|
}
|
280
|
-
if (RSTRING_LEN(str)) {
|
281
|
-
|
282
|
-
RSTRING_LEN(str))) {
|
283
|
-
// fprintf(stderr, "Iodine Server Error:"
|
284
|
-
// "couldn't write response to connection\n");
|
285
|
-
return Qfalse;
|
286
|
-
}
|
382
|
+
if (RSTRING_LEN(str) && RSTRING_PTR(str)) {
|
383
|
+
fiobj_str_write((FIOBJ)body_, RSTRING_PTR(str), RSTRING_LEN(str));
|
287
384
|
}
|
288
385
|
return Qtrue;
|
289
386
|
}
|
290
387
|
|
291
|
-
static inline int ruby2c_response_send(
|
388
|
+
static inline int ruby2c_response_send(iodine_http_request_handle_s *handle,
|
292
389
|
VALUE rbresponse, VALUE env) {
|
293
390
|
(void)(env);
|
294
391
|
VALUE body = rb_ary_entry(rbresponse, 2);
|
295
|
-
if (
|
296
|
-
|
392
|
+
if (handle->h->status < 200 || handle->h->status == 204 ||
|
393
|
+
handle->h->status == 304) {
|
297
394
|
if (rb_respond_to(body, close_method_id))
|
298
395
|
RubyCaller.call(body, close_method_id);
|
299
396
|
body = Qnil;
|
300
|
-
|
397
|
+
handle->type = IODINE_HTTP_NONE;
|
398
|
+
return 0;
|
301
399
|
}
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
400
|
+
if (TYPE(body) == T_ARRAY) {
|
401
|
+
if (RARRAY_LEN(body) == 0) { // only headers
|
402
|
+
handle->type = IODINE_HTTP_EMPTY;
|
403
|
+
} else if (RARRAY_LEN(body) == 1) { // [String] is likely
|
404
|
+
body = rb_ary_entry(body, 0);
|
405
|
+
// fprintf(stderr, "Body was a single item array, unpacket to string\n");
|
406
|
+
}
|
306
407
|
}
|
307
408
|
|
308
409
|
if (TYPE(body) == T_STRING) {
|
309
410
|
// fprintf(stderr, "Review body as String\n");
|
310
411
|
if (RSTRING_LEN(body))
|
311
|
-
|
312
|
-
|
313
|
-
} else if (body == Qnil) {
|
412
|
+
handle->body = fiobj_str_new(RSTRING_PTR(body), RSTRING_LEN(body));
|
413
|
+
handle->type = IODINE_HTTP_SENDBODY;
|
314
414
|
return 0;
|
315
415
|
} else if (rb_respond_to(body, each_method_id)) {
|
316
416
|
// fprintf(stderr, "Review body as for-each ...\n");
|
317
|
-
|
318
|
-
|
319
|
-
// protection from bad code
|
320
|
-
response->should_close = 1;
|
321
|
-
response->content_length = -1;
|
322
|
-
}
|
417
|
+
handle->body = fiobj_str_buf(1);
|
418
|
+
handle->type = IODINE_HTTP_SENDBODY;
|
323
419
|
rb_block_call(body, each_method_id, 0, NULL, for_each_body_string,
|
324
|
-
(VALUE)
|
420
|
+
(VALUE)handle->body);
|
325
421
|
// we need to call `close` in case the object is an IO / BodyProxy
|
326
422
|
if (rb_respond_to(body, close_method_id))
|
327
423
|
RubyCaller.call(body, close_method_id);
|
@@ -334,36 +430,37 @@ static inline int ruby2c_response_send(http_response_s *response,
|
|
334
430
|
Handling Upgrade cases
|
335
431
|
***************************************************************************** */
|
336
432
|
|
337
|
-
static inline int ruby2c_review_upgrade(
|
433
|
+
static inline int ruby2c_review_upgrade(iodine_http_request_handle_s *req,
|
338
434
|
VALUE rbresponse, VALUE env) {
|
435
|
+
http_s *h = req->h;
|
339
436
|
VALUE handler;
|
340
437
|
if ((handler = rb_hash_aref(env, IODINE_R_HIJACK_CB)) != Qnil) {
|
341
438
|
// send headers
|
342
|
-
|
343
|
-
// remove socket from facil.io
|
344
|
-
facil_attach(response->fd, NULL);
|
439
|
+
http_finish(h);
|
345
440
|
// call the callback
|
346
441
|
VALUE io_ruby = RubyCaller.call(rb_hash_aref(env, IODINE_R_HIJACK),
|
347
442
|
iodine_call_proc_id);
|
348
443
|
RubyCaller.call2(handler, iodine_call_proc_id, 1, &io_ruby);
|
349
444
|
} else if ((handler = rb_hash_aref(env, IODINE_R_HIJACK_IO)) != Qnil) {
|
350
|
-
//
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
} else if ((handler = rb_hash_aref(env, UPGRADE_WEBSOCKET)) != Qnil) {
|
355
|
-
iodine_http_settings_s *settings = response->request->settings->udata;
|
445
|
+
// do nothing
|
446
|
+
} else if (req->upgrade == IODINE_UPGRADE_WEBSOCKET &&
|
447
|
+
((handler = rb_hash_aref(env, RACK_UPGRADE)) != Qnil ||
|
448
|
+
(handler = rb_hash_aref(env, UPGRADE_WEBSOCKET)) != Qnil)) {
|
356
449
|
// use response as existing base for native websocket upgrade
|
357
|
-
|
358
|
-
|
450
|
+
iodine_upgrade_websocket(h, handler);
|
451
|
+
} else if (req->upgrade == IODINE_UPGRADE_SSE &&
|
452
|
+
(handler = rb_hash_aref(env, RACK_UPGRADE)) != Qnil) {
|
453
|
+
// use response as existing base for SSE upgrade
|
454
|
+
iodine_upgrade_sse(h, handler);
|
359
455
|
} else if ((handler = rb_hash_aref(env, UPGRADE_TCP)) != Qnil) {
|
360
|
-
|
456
|
+
// hijack post headers (might be very bad)
|
457
|
+
intptr_t uuid = http_hijack(h, NULL);
|
361
458
|
// send headers
|
362
|
-
|
459
|
+
http_finish(h);
|
363
460
|
// upgrade protocol
|
364
|
-
VALUE args[2] = {(ULONG2NUM(sock_uuid2fd(
|
461
|
+
VALUE args[2] = {(ULONG2NUM(sock_uuid2fd(uuid))), handler};
|
365
462
|
RubyCaller.call2(Iodine, attach_method_id, 2, args);
|
366
|
-
// prevent response processing.
|
463
|
+
// nothing left to do to prevent response processing.
|
367
464
|
} else {
|
368
465
|
return 0;
|
369
466
|
}
|
@@ -379,176 +476,155 @@ static inline int ruby2c_review_upgrade(http_response_s *response,
|
|
379
476
|
Handling HTTP requests
|
380
477
|
***************************************************************************** */
|
381
478
|
|
382
|
-
static void *
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
if (!
|
479
|
+
static inline void *iodine_handle_request_in_GVL(void *handle_) {
|
480
|
+
iodine_http_request_handle_s *handle = handle_;
|
481
|
+
VALUE rbresponse = 0;
|
482
|
+
VALUE env = 0;
|
483
|
+
http_s *h = handle->h;
|
484
|
+
if (!h->udata)
|
388
485
|
goto err_not_found;
|
389
486
|
|
390
|
-
// create /register env variable
|
391
|
-
|
487
|
+
// create / register env variable
|
488
|
+
env = copy2env(handle);
|
392
489
|
// will be used later
|
393
490
|
VALUE tmp;
|
394
491
|
// pass env variable to handler
|
395
|
-
VALUE
|
396
|
-
|
492
|
+
rbresponse = RubyCaller.call2((VALUE)h->udata, iodine_call_proc_id, 1, &env);
|
493
|
+
// test handler's return value
|
397
494
|
if (rbresponse == 0 || rbresponse == Qnil)
|
398
495
|
goto internal_error;
|
399
496
|
Registry.add(rbresponse);
|
497
|
+
|
400
498
|
// set response status
|
401
499
|
tmp = rb_ary_entry(rbresponse, 0);
|
402
|
-
if (TYPE(tmp) == T_STRING)
|
403
|
-
|
404
|
-
|
500
|
+
if (TYPE(tmp) == T_STRING) {
|
501
|
+
char *data = RSTRING_PTR(tmp);
|
502
|
+
h->status = fio_atol(&data);
|
503
|
+
} else if (TYPE(tmp) == T_FIXNUM) {
|
504
|
+
h->status = FIX2ULONG(tmp);
|
505
|
+
} else {
|
405
506
|
goto internal_error;
|
406
|
-
|
507
|
+
}
|
508
|
+
|
407
509
|
// handle header copy from ruby land to C land.
|
408
510
|
VALUE response_headers = rb_ary_entry(rbresponse, 1);
|
409
511
|
if (TYPE(response_headers) != T_HASH)
|
410
512
|
goto internal_error;
|
411
513
|
// extract the X-Sendfile header (never show original path)
|
412
|
-
// X-Sendfile support only present when iodine
|
514
|
+
// X-Sendfile support only present when iodine serves static files.
|
413
515
|
VALUE xfiles;
|
414
|
-
if (
|
516
|
+
if (support_xsendfile &&
|
415
517
|
(xfiles = rb_hash_aref(response_headers, XSENDFILE)) != Qnil &&
|
416
518
|
TYPE(xfiles) == T_STRING) {
|
417
|
-
int fr = 0;
|
418
519
|
if (OBJ_FROZEN(response_headers)) {
|
419
520
|
response_headers = rb_hash_dup(response_headers);
|
420
|
-
Registry.add(response_headers);
|
421
|
-
fr = 1;
|
422
521
|
}
|
522
|
+
Registry.add(response_headers);
|
523
|
+
handle->body = fiobj_str_new(RSTRING_PTR(xfiles), RSTRING_LEN(xfiles));
|
524
|
+
handle->type = IODINE_HTTP_XSENDFILE;
|
423
525
|
rb_hash_delete(response_headers, XSENDFILE);
|
424
|
-
// remove
|
425
|
-
// Iodine
|
526
|
+
// remove content length headers, as this will be controled by iodine
|
426
527
|
rb_hash_delete(response_headers, CONTENT_LENGTH_HEADER);
|
427
528
|
// review each header and write it to the response.
|
428
|
-
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(
|
429
|
-
|
430
|
-
Registry.remove(response_headers);
|
529
|
+
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(h));
|
530
|
+
Registry.remove(response_headers);
|
431
531
|
// send the file directly and finish
|
432
|
-
|
433
|
-
RSTRING_LEN(xfiles), NULL, 0, 1)) {
|
434
|
-
http_response_destroy(response);
|
435
|
-
response = http_response_create(request);
|
436
|
-
if (request->settings->log_static)
|
437
|
-
http_response_log_start(response);
|
438
|
-
Registry.remove(rbresponse);
|
439
|
-
Registry.remove(env);
|
440
|
-
goto err_not_found;
|
441
|
-
}
|
442
|
-
goto external_done;
|
532
|
+
return NULL;
|
443
533
|
}
|
444
534
|
// review each header and write it to the response.
|
445
|
-
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(
|
446
|
-
//
|
447
|
-
|
448
|
-
if (ruby2c_review_upgrade(response, rbresponse, env))
|
535
|
+
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(h));
|
536
|
+
// review for upgrade.
|
537
|
+
if (h->status < 300 && ruby2c_review_upgrade(handle, rbresponse, env))
|
449
538
|
goto external_done;
|
450
539
|
// send the request body.
|
451
|
-
if (ruby2c_response_send(
|
540
|
+
if (ruby2c_response_send(handle, rbresponse, env))
|
452
541
|
goto internal_error;
|
453
542
|
|
454
543
|
Registry.remove(rbresponse);
|
455
544
|
Registry.remove(env);
|
456
|
-
http_response_finish(response);
|
457
545
|
return NULL;
|
458
546
|
|
459
547
|
external_done:
|
460
548
|
Registry.remove(rbresponse);
|
461
549
|
Registry.remove(env);
|
550
|
+
handle->type = IODINE_HTTP_NONE;
|
462
551
|
return NULL;
|
463
552
|
|
464
553
|
err_not_found:
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
request->settings->public_folder_length,
|
470
|
-
"404.html", 8, request->settings->log_static)) {
|
471
|
-
http_response_write_body(response, "Error 404, Page Not Found.", 26);
|
472
|
-
http_response_finish(response);
|
473
|
-
}
|
554
|
+
Registry.remove(rbresponse);
|
555
|
+
Registry.remove(env);
|
556
|
+
h->status = 404;
|
557
|
+
handle->type = IODINE_HTTP_ERROR;
|
474
558
|
return NULL;
|
475
559
|
|
476
560
|
internal_error:
|
477
|
-
|
478
|
-
Registry.remove(rbresponse);
|
561
|
+
Registry.remove(rbresponse);
|
479
562
|
Registry.remove(env);
|
480
|
-
|
481
|
-
|
482
|
-
if (request->settings->log_static)
|
483
|
-
http_response_log_start(response);
|
484
|
-
response->status = 500;
|
485
|
-
if (!request->settings->public_folder ||
|
486
|
-
http_response_sendfile2(response, request,
|
487
|
-
request->settings->public_folder,
|
488
|
-
request->settings->public_folder_length,
|
489
|
-
"500.html", 8, request->settings->log_static)) {
|
490
|
-
http_response_write_body(response, "Error 500, Internal error.", 26);
|
491
|
-
http_response_finish(response);
|
492
|
-
}
|
563
|
+
h->status = 500;
|
564
|
+
handle->type = IODINE_HTTP_ERROR;
|
493
565
|
return NULL;
|
494
566
|
}
|
495
567
|
|
496
|
-
static void
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
***************************************************************************** */
|
505
|
-
|
506
|
-
#define add_str_to_env(env, key, value) \
|
507
|
-
{ \
|
508
|
-
VALUE k = rb_enc_str_new((key), strlen((key)), IodineBinaryEncoding); \
|
509
|
-
rb_obj_freeze(k); \
|
510
|
-
VALUE v = rb_enc_str_new((value), strlen((value)), IodineBinaryEncoding); \
|
511
|
-
rb_obj_freeze(v); \
|
512
|
-
rb_hash_aset(env, k, v); \
|
568
|
+
static inline void
|
569
|
+
iodine_perform_handle_action(iodine_http_request_handle_s handle) {
|
570
|
+
switch (handle.type) {
|
571
|
+
case IODINE_HTTP_SENDBODY: {
|
572
|
+
fio_cstr_s data = fiobj_obj2cstr(handle.body);
|
573
|
+
http_send_body(handle.h, data.data, data.len);
|
574
|
+
fiobj_free(handle.body);
|
575
|
+
break;
|
513
576
|
}
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
577
|
+
case IODINE_HTTP_XSENDFILE: {
|
578
|
+
/* remove chunked content-encoding header, if any (Rack issue #1266) */
|
579
|
+
if (fiobj_obj2cstr(
|
580
|
+
fiobj_hash_get2(handle.h->private_data.out_headers,
|
581
|
+
fiobj_obj2hash(HTTP_HEADER_CONTENT_ENCODING)))
|
582
|
+
.len == 7)
|
583
|
+
fiobj_hash_delete2(handle.h->private_data.out_headers,
|
584
|
+
fiobj_obj2hash(HTTP_HEADER_CONTENT_ENCODING));
|
585
|
+
fio_cstr_s data = fiobj_obj2cstr(handle.body);
|
586
|
+
if (http_sendfile2(handle.h, data.data, data.len, NULL, 0)) {
|
587
|
+
http_send_error(handle.h, 404);
|
588
|
+
}
|
589
|
+
fiobj_free(handle.body);
|
590
|
+
break;
|
519
591
|
}
|
592
|
+
case IODINE_HTTP_EMPTY:
|
593
|
+
http_finish(handle.h);
|
594
|
+
fiobj_free(handle.body);
|
595
|
+
break;
|
596
|
+
case IODINE_HTTP_NONE:
|
597
|
+
/* nothing to do - this had to be performed within the Ruby GIL :-( */
|
598
|
+
break;
|
599
|
+
case IODINE_HTTP_ERROR:
|
600
|
+
http_send_error(handle.h, handle.h->status);
|
601
|
+
fiobj_free(handle.body);
|
602
|
+
break;
|
603
|
+
}
|
604
|
+
}
|
605
|
+
static void on_rack_request(http_s *h) {
|
606
|
+
iodine_http_request_handle_s handle = (iodine_http_request_handle_s){
|
607
|
+
.h = h, .upgrade = IODINE_UPGRADE_NONE,
|
608
|
+
};
|
609
|
+
RubyCaller.call_c((void *(*)(void *))iodine_handle_request_in_GVL, &handle);
|
610
|
+
iodine_perform_handle_action(handle);
|
611
|
+
}
|
520
612
|
|
521
|
-
static void
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
// add the rack.version
|
537
|
-
tmp = rb_ary_new(); // rb_ary_new is Ruby 2.0 compatible
|
538
|
-
rb_ary_push(tmp, INT2FIX(1));
|
539
|
-
rb_ary_push(tmp, INT2FIX(3));
|
540
|
-
// rb_ary_push(tmp, rb_enc_str_new("1", 1, IodineBinaryEncoding));
|
541
|
-
// rb_ary_push(tmp, rb_enc_str_new("3", 1, IodineBinaryEncoding));
|
542
|
-
add_value_to_env(set->env, "rack.version", tmp);
|
543
|
-
add_value_to_env(set->env, "rack.errors", rb_stderr);
|
544
|
-
add_value_to_env(set->env, "rack.multithread", Qtrue);
|
545
|
-
add_value_to_env(set->env, "rack.multiprocess", Qtrue);
|
546
|
-
add_value_to_env(set->env, "rack.run_once", Qfalse);
|
547
|
-
add_value_to_env(set->env, "rack.hijack?", Qtrue);
|
548
|
-
add_str_to_env(set->env, "SCRIPT_NAME", "");
|
613
|
+
static void on_rack_upgrade(http_s *h, char *proto, size_t len) {
|
614
|
+
iodine_http_request_handle_s handle = (iodine_http_request_handle_s){.h = h};
|
615
|
+
if (len == 9 && proto[1] == 'e') {
|
616
|
+
handle.upgrade = IODINE_UPGRADE_WEBSOCKET;
|
617
|
+
} else if (len == 3 && proto[0] == 's') {
|
618
|
+
handle.upgrade = IODINE_UPGRADE_SSE;
|
619
|
+
} else {
|
620
|
+
http_send_error(h, 400);
|
621
|
+
return;
|
622
|
+
}
|
623
|
+
RubyCaller.call_c(iodine_handle_request_in_GVL, &handle);
|
624
|
+
iodine_perform_handle_action(handle);
|
625
|
+
(void)proto;
|
626
|
+
(void)len;
|
549
627
|
}
|
550
|
-
#undef add_str_to_env
|
551
|
-
#undef add_value_to_env
|
552
628
|
|
553
629
|
/* *****************************************************************************
|
554
630
|
Listenninng to HTTP
|
@@ -566,9 +642,7 @@ void *iodine_print_http_msg2_in_gvl(void *d_) {
|
|
566
642
|
"Iodine HTTP Server on port %s:\n"
|
567
643
|
" * Serving static files from %s\n\n",
|
568
644
|
StringValueCStr(arg->port), StringValueCStr(arg->www));
|
569
|
-
Registry.remove(arg->www);
|
570
645
|
}
|
571
|
-
Registry.remove(arg->port);
|
572
646
|
return NULL;
|
573
647
|
}
|
574
648
|
|
@@ -584,49 +658,51 @@ void *iodine_print_http_msg_in_gvl(void *d_) {
|
|
584
658
|
fprintf(stderr,
|
585
659
|
"\nStarting up Iodine HTTP Server on port %s:\n"
|
586
660
|
" * Ruby v.%s\n * Iodine v.%s \n"
|
587
|
-
" * %lu max concurrent connections / open files\n"
|
588
661
|
" * Serving static files from %s\n\n",
|
589
662
|
StringValueCStr(arg->port), StringValueCStr(ruby_version),
|
590
|
-
StringValueCStr(iodine_version), (
|
591
|
-
|
592
|
-
Registry.remove(arg->www);
|
593
|
-
} else
|
663
|
+
StringValueCStr(iodine_version), StringValueCStr(arg->www));
|
664
|
+
} else {
|
594
665
|
fprintf(stderr,
|
595
666
|
"\nStarting up Iodine HTTP Server on port %s:\n"
|
596
|
-
" * Ruby v.%s\n * Iodine v.%s \n"
|
597
|
-
" * %lu max concurrent connections / open files\n\n",
|
667
|
+
" * Ruby v.%s\n * Iodine v.%s \n\n",
|
598
668
|
StringValueCStr(arg->port), StringValueCStr(ruby_version),
|
599
|
-
StringValueCStr(iodine_version)
|
600
|
-
|
669
|
+
StringValueCStr(iodine_version));
|
670
|
+
}
|
601
671
|
|
602
672
|
return NULL;
|
603
673
|
}
|
604
674
|
|
605
675
|
static void iodine_print_http_msg1(void *www, void *port) {
|
606
|
-
if (
|
607
|
-
|
676
|
+
if (getpid() != facil_parent_pid())
|
677
|
+
goto finish;
|
608
678
|
struct {
|
609
679
|
void *www;
|
610
680
|
void *port;
|
611
681
|
} data = {.www = www, .port = port};
|
612
682
|
RubyCaller.call_c(iodine_print_http_msg_in_gvl, (void *)&data);
|
683
|
+
finish:
|
684
|
+
if (www) {
|
685
|
+
Registry.remove((VALUE)www);
|
686
|
+
}
|
687
|
+
Registry.remove((VALUE)port);
|
613
688
|
}
|
614
689
|
static void iodine_print_http_msg2(void *www, void *port) {
|
615
|
-
if (
|
616
|
-
|
690
|
+
if (getpid() != facil_parent_pid())
|
691
|
+
goto finish;
|
617
692
|
struct {
|
618
693
|
void *www;
|
619
694
|
void *port;
|
620
695
|
} data = {.www = www, .port = port};
|
621
696
|
RubyCaller.call_c(iodine_print_http_msg2_in_gvl, (void *)&data);
|
697
|
+
finish:
|
698
|
+
if (www) {
|
699
|
+
Registry.remove((VALUE)www);
|
700
|
+
}
|
701
|
+
Registry.remove((VALUE)port);
|
622
702
|
}
|
623
703
|
|
624
|
-
static void free_iodine_http(
|
625
|
-
|
626
|
-
Registry.remove(set->app);
|
627
|
-
Registry.remove(set->env);
|
628
|
-
free(set);
|
629
|
-
(void)uuid;
|
704
|
+
static void free_iodine_http(http_settings_s *s) {
|
705
|
+
Registry.remove((VALUE)s->udata);
|
630
706
|
}
|
631
707
|
/**
|
632
708
|
Listens to incoming HTTP connections and handles incoming requests using the
|
@@ -643,10 +719,10 @@ port:: the port to listen to. Default: 3000.
|
|
643
719
|
address:: the address to bind to. Default: binds to all possible addresses.
|
644
720
|
log:: enable response logging (Hijacked sockets aren't logged). Default: off.
|
645
721
|
public:: The root public folder for static file service. Default: none.
|
646
|
-
timeout:: Timeout for inactive HTTP/1.x connections. Defaults:
|
722
|
+
timeout:: Timeout for inactive HTTP/1.x connections. Defaults: 40 seconds.
|
647
723
|
max_body:: The maximum body size for incoming HTTP messages. Default: ~50Mib.
|
648
724
|
max_msg:: The maximum Websocket message size allowed. Default: ~250Kib.
|
649
|
-
ping:: The Websocket `ping` interval. Default: 40
|
725
|
+
ping:: The Websocket `ping` interval. Default: 40 seconds.
|
650
726
|
|
651
727
|
Either the `app` or the `public` properties are required. If niether exists,
|
652
728
|
the function will fail. If both exist, Iodine will serve static files as well
|
@@ -668,6 +744,7 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
668
744
|
uint8_t log_http = 0;
|
669
745
|
size_t ping = 0;
|
670
746
|
size_t max_body = 0;
|
747
|
+
size_t max_headers = 0;
|
671
748
|
size_t max_msg = 0;
|
672
749
|
Check_Type(opt, T_HASH);
|
673
750
|
VALUE app = rb_hash_aref(opt, ID2SYM(rb_intern("app")));
|
@@ -687,6 +764,11 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
687
764
|
Check_Type(tmp, T_FIXNUM);
|
688
765
|
max_body = FIX2ULONG(tmp);
|
689
766
|
}
|
767
|
+
tmp = rb_hash_aref(opt, ID2SYM(rb_intern("max_headers")));
|
768
|
+
if (tmp != Qnil && tmp != Qfalse) {
|
769
|
+
Check_Type(tmp, T_FIXNUM);
|
770
|
+
max_headers = FIX2ULONG(tmp);
|
771
|
+
}
|
690
772
|
|
691
773
|
tmp = rb_hash_aref(opt, ID2SYM(rb_intern("ping")));
|
692
774
|
if (tmp != Qnil && tmp != Qfalse) {
|
@@ -712,6 +794,9 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
712
794
|
if ((www != Qnil && www != Qfalse)) {
|
713
795
|
Check_Type(www, T_STRING);
|
714
796
|
Registry.add(www);
|
797
|
+
rb_hash_aset(env_template_no_upgrade, XSENDFILE_TYPE, XSENDFILE);
|
798
|
+
rb_hash_aset(env_template_no_upgrade, XSENDFILE_TYPE_HEADER, XSENDFILE);
|
799
|
+
support_xsendfile = 1;
|
715
800
|
} else
|
716
801
|
www = 0;
|
717
802
|
|
@@ -737,27 +822,27 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
737
822
|
"The `port` property MUST be either a String or a Number");
|
738
823
|
if (RB_TYPE_P(port, T_FIXNUM))
|
739
824
|
port = rb_funcall2(port, iodine_to_s_method_id, 0, NULL);
|
740
|
-
|
825
|
+
Registry.add(port);
|
826
|
+
} else if (port == Qfalse)
|
827
|
+
port = 0;
|
828
|
+
else {
|
741
829
|
port = rb_str_new("3000", 4);
|
742
|
-
|
830
|
+
Registry.add(port);
|
831
|
+
}
|
743
832
|
|
744
833
|
if ((app != Qnil && app != Qfalse))
|
745
834
|
Registry.add(app);
|
746
835
|
else
|
747
836
|
app = 0;
|
748
837
|
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
.timeout = (tout ? FIX2INT(tout) : tout),
|
758
|
-
.on_finish = free_iodine_http, .log_static = log_http,
|
759
|
-
.max_body_size = max_body,
|
760
|
-
.public_folder = (www ? StringValueCStr(www) : NULL))) {
|
838
|
+
if (http_listen(
|
839
|
+
StringValueCStr(port), (address ? StringValueCStr(address) : NULL),
|
840
|
+
.on_request = on_rack_request, .on_upgrade = on_rack_upgrade,
|
841
|
+
.udata = (void *)app, .timeout = (tout ? FIX2INT(tout) : tout),
|
842
|
+
.ws_timeout = ping, .ws_max_msg_size = max_msg,
|
843
|
+
.max_header_size = max_headers, .on_finish = free_iodine_http,
|
844
|
+
.log = log_http, .max_body_size = max_body,
|
845
|
+
.public_folder = (www ? StringValueCStr(www) : NULL))) {
|
761
846
|
fprintf(stderr,
|
762
847
|
"ERROR: Failed to initialize a listening HTTP socket for port %s\n",
|
763
848
|
port ? StringValueCStr(port) : "3000");
|
@@ -770,9 +855,9 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
770
855
|
"static files.\n",
|
771
856
|
(port ? StringValueCStr(port) : "3000"));
|
772
857
|
}
|
773
|
-
if (called_once)
|
858
|
+
if (called_once) {
|
774
859
|
defer(iodine_print_http_msg2, (www ? (void *)www : NULL), (void *)port);
|
775
|
-
else {
|
860
|
+
} else {
|
776
861
|
called_once = 1;
|
777
862
|
defer(iodine_print_http_msg1, (www ? (void *)www : NULL), (void *)port);
|
778
863
|
}
|
@@ -781,10 +866,83 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
781
866
|
(void)self;
|
782
867
|
}
|
783
868
|
|
869
|
+
static void initialize_env_template(void) {
|
870
|
+
if (env_template_no_upgrade)
|
871
|
+
return;
|
872
|
+
env_template_no_upgrade = rb_hash_new();
|
873
|
+
rb_global_variable(&env_template_no_upgrade);
|
874
|
+
|
875
|
+
#define add_str_to_env(env, key, value) \
|
876
|
+
{ \
|
877
|
+
VALUE k = rb_enc_str_new((key), strlen((key)), IodineBinaryEncoding); \
|
878
|
+
rb_obj_freeze(k); \
|
879
|
+
VALUE v = rb_enc_str_new((value), strlen((value)), IodineBinaryEncoding); \
|
880
|
+
rb_obj_freeze(v); \
|
881
|
+
rb_hash_aset(env, k, v); \
|
882
|
+
}
|
883
|
+
#define add_value_to_env(env, key, value) \
|
884
|
+
{ \
|
885
|
+
VALUE k = rb_enc_str_new((key), strlen((key)), IodineBinaryEncoding); \
|
886
|
+
rb_obj_freeze(k); \
|
887
|
+
rb_hash_aset((env), k, value); \
|
888
|
+
}
|
889
|
+
|
890
|
+
/* Set global template */
|
891
|
+
rb_hash_aset(env_template_no_upgrade, RACK_UPGRADE_Q, Qnil);
|
892
|
+
rb_hash_aset(env_template_no_upgrade, RACK_UPGRADE, Qnil);
|
893
|
+
rb_hash_aset(env_template_no_upgrade, UPGRADE_WEBSOCKET_Q, Qnil);
|
894
|
+
rb_hash_aset(env_template_no_upgrade, UPGRADE_WEBSOCKET, Qnil);
|
895
|
+
rb_hash_aset(env_template_no_upgrade, UPGRADE_TCP_Q, Qnil);
|
896
|
+
rb_hash_aset(env_template_no_upgrade, UPGRADE_TCP, Qnil);
|
897
|
+
{
|
898
|
+
/* add the rack.version */
|
899
|
+
static VALUE rack_version = 0;
|
900
|
+
if (!rack_version) {
|
901
|
+
rack_version = rb_ary_new(); // rb_ary_new is Ruby 2.0 compatible
|
902
|
+
rb_ary_push(rack_version, INT2FIX(1));
|
903
|
+
rb_ary_push(rack_version, INT2FIX(3));
|
904
|
+
rb_global_variable(&rack_version);
|
905
|
+
rb_ary_freeze(rack_version);
|
906
|
+
}
|
907
|
+
add_value_to_env(env_template_no_upgrade, "rack.version", rack_version);
|
908
|
+
}
|
909
|
+
add_str_to_env(env_template_no_upgrade, "SCRIPT_NAME", "");
|
910
|
+
add_value_to_env(env_template_no_upgrade, "rack.errors", rb_stderr);
|
911
|
+
add_value_to_env(env_template_no_upgrade, "rack.hijack?", Qtrue);
|
912
|
+
add_value_to_env(env_template_no_upgrade, "rack.multiprocess", Qtrue);
|
913
|
+
add_value_to_env(env_template_no_upgrade, "rack.multithread", Qtrue);
|
914
|
+
add_value_to_env(env_template_no_upgrade, "rack.run_once", Qfalse);
|
915
|
+
/* default schema to http, it might be updated later */
|
916
|
+
rb_hash_aset(env_template_no_upgrade, R_URL_SCHEME, HTTP_SCHEME);
|
917
|
+
/* placeholders... minimize rehashing*/
|
918
|
+
rb_hash_aset(env_template_no_upgrade, HTTP_VERSION, QUERY_STRING);
|
919
|
+
rb_hash_aset(env_template_no_upgrade, IODINE_R_HIJACK, QUERY_STRING);
|
920
|
+
rb_hash_aset(env_template_no_upgrade, PATH_INFO, QUERY_STRING);
|
921
|
+
rb_hash_aset(env_template_no_upgrade, QUERY_STRING, QUERY_STRING);
|
922
|
+
rb_hash_aset(env_template_no_upgrade, REMOTE_ADDR, QUERY_STRING);
|
923
|
+
rb_hash_aset(env_template_no_upgrade, REQUEST_METHOD, QUERY_STRING);
|
924
|
+
rb_hash_aset(env_template_no_upgrade, SERVER_NAME, QUERY_STRING);
|
925
|
+
rb_hash_aset(env_template_no_upgrade, SERVER_PORT, QUERY_ESTRING);
|
926
|
+
rb_hash_aset(env_template_no_upgrade, SERVER_PROTOCOL, QUERY_STRING);
|
927
|
+
|
928
|
+
/* WebSocket upgrade support */
|
929
|
+
env_template_websockets = rb_hash_dup(env_template_no_upgrade);
|
930
|
+
rb_global_variable(&env_template_websockets);
|
931
|
+
rb_hash_aset(env_template_websockets, UPGRADE_WEBSOCKET_Q, Qtrue);
|
932
|
+
rb_hash_aset(env_template_websockets, RACK_UPGRADE_Q, RACK_UPGRADE_WEBSOCKET);
|
933
|
+
rb_hash_aset(env_template_websockets, UPGRADE_TCP_Q, Qtrue);
|
934
|
+
|
935
|
+
/* SSE upgrade support */
|
936
|
+
env_template_sse = rb_hash_dup(env_template_no_upgrade);
|
937
|
+
rb_global_variable(&env_template_sse);
|
938
|
+
rb_hash_aset(env_template_sse, RACK_UPGRADE_Q, RACK_UPGRADE_SSE);
|
939
|
+
|
940
|
+
#undef add_value_to_env
|
941
|
+
#undef add_str_to_env
|
942
|
+
}
|
784
943
|
/* *****************************************************************************
|
785
944
|
Initialization
|
786
|
-
*****************************************************************************
|
787
|
-
*/
|
945
|
+
***************************************************************************** */
|
788
946
|
|
789
947
|
void Iodine_init_http(void) {
|
790
948
|
|
@@ -806,6 +964,8 @@ void Iodine_init_http(void) {
|
|
806
964
|
rack_set(R_URL_SCHEME, "rack.url_scheme");
|
807
965
|
rack_set(R_INPUT, "rack.input");
|
808
966
|
rack_set(XSENDFILE, "X-Sendfile");
|
967
|
+
rack_set(XSENDFILE_TYPE, "sendfile.type");
|
968
|
+
rack_set(XSENDFILE_TYPE_HEADER, "HTTP_X_SENDFILE_TYPE");
|
809
969
|
rack_set(CONTENT_LENGTH_HEADER, "Content-Length");
|
810
970
|
|
811
971
|
rack_set(IODINE_R_HIJACK_IO, "rack.hijack_io");
|
@@ -817,9 +977,10 @@ void Iodine_init_http(void) {
|
|
817
977
|
|
818
978
|
rack_set(UPGRADE_TCP_Q, "upgrade.tcp?");
|
819
979
|
rack_set(UPGRADE_WEBSOCKET_Q, "upgrade.websocket?");
|
820
|
-
|
821
|
-
rack_set(
|
822
|
-
|
980
|
+
rack_set(RACK_UPGRADE, "rack.upgrade");
|
981
|
+
rack_set(RACK_UPGRADE_Q, "rack.upgrade?");
|
982
|
+
rack_set_sym(RACK_UPGRADE_SSE, "sse");
|
983
|
+
rack_set_sym(RACK_UPGRADE_WEBSOCKET, "websocket");
|
823
984
|
|
824
985
|
hijack_func_sym = ID2SYM(rb_intern("_hijack"));
|
825
986
|
close_method_id = rb_intern("close");
|
@@ -827,4 +988,6 @@ void Iodine_init_http(void) {
|
|
827
988
|
attach_method_id = rb_intern("attach_fd");
|
828
989
|
|
829
990
|
IodineRackIO.init();
|
991
|
+
initialize_env_template();
|
992
|
+
Iodine_init_json();
|
830
993
|
}
|