iodine 0.3.6 → 0.4.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/CHANGELOG.md +46 -0
- data/LIMITS.md +25 -0
- data/README.md +39 -80
- data/SPEC-Websocket-Draft.md +129 -4
- data/bin/echo +2 -2
- data/bin/http-hello +1 -0
- data/bin/updated api +113 -0
- data/bin/ws-echo +0 -1
- data/examples/broadcast.ru +56 -0
- data/examples/echo.ru +57 -0
- data/examples/hello.ru +30 -0
- data/examples/redis.ru +69 -0
- data/examples/shootout.ru +53 -0
- data/exe/iodine +2 -80
- data/ext/iodine/defer.c +11 -5
- data/ext/iodine/empty.h +26 -0
- data/ext/iodine/evio.h +1 -1
- data/ext/iodine/facil.c +103 -61
- data/ext/iodine/facil.h +20 -12
- data/ext/iodine/fio_dict.c +446 -0
- data/ext/iodine/fio_dict.h +90 -0
- data/ext/iodine/fio_hash_table.h +370 -0
- data/ext/iodine/fio_list.h +30 -3
- data/ext/iodine/http.c +169 -37
- data/ext/iodine/http.h +33 -10
- data/ext/iodine/http1.c +78 -42
- data/ext/iodine/http_request.c +6 -0
- data/ext/iodine/http_request.h +3 -0
- data/ext/iodine/http_response.c +43 -11
- data/ext/iodine/iodine.c +380 -0
- data/ext/iodine/iodine.h +62 -0
- data/ext/iodine/iodine_helpers.c +235 -0
- data/ext/iodine/iodine_helpers.h +13 -0
- data/ext/iodine/iodine_http.c +409 -241
- data/ext/iodine/iodine_http.h +7 -14
- data/ext/iodine/iodine_protocol.c +626 -0
- data/ext/iodine/iodine_protocol.h +13 -0
- data/ext/iodine/iodine_pubsub.c +646 -0
- data/ext/iodine/iodine_pubsub.h +27 -0
- data/ext/iodine/iodine_websockets.c +796 -0
- data/ext/iodine/iodine_websockets.h +19 -0
- data/ext/iodine/pubsub.c +544 -0
- data/ext/iodine/pubsub.h +215 -0
- data/ext/iodine/random.c +4 -4
- data/ext/iodine/rb-call.c +1 -5
- data/ext/iodine/rb-defer.c +3 -20
- data/ext/iodine/rb-rack-io.c +22 -22
- data/ext/iodine/rb-rack-io.h +3 -4
- data/ext/iodine/rb-registry.c +111 -118
- data/ext/iodine/redis_connection.c +277 -0
- data/ext/iodine/redis_connection.h +77 -0
- data/ext/iodine/redis_engine.c +398 -0
- data/ext/iodine/redis_engine.h +68 -0
- data/ext/iodine/resp.c +842 -0
- data/ext/iodine/resp.h +253 -0
- data/ext/iodine/sock.c +26 -12
- data/ext/iodine/sock.h +14 -3
- data/ext/iodine/spnlock.inc +19 -2
- data/ext/iodine/websockets.c +299 -11
- data/ext/iodine/websockets.h +159 -6
- data/lib/iodine.rb +104 -1
- data/lib/iodine/cli.rb +106 -0
- data/lib/iodine/monkeypatch.rb +40 -0
- data/lib/iodine/pubsub.rb +70 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +12 -0
- data/lib/rack/handler/iodine.rb +33 -7
- metadata +35 -7
- data/ext/iodine/iodine_core.c +0 -760
- data/ext/iodine/iodine_core.h +0 -79
- data/ext/iodine/iodine_websocket.c +0 -551
- data/ext/iodine/iodine_websocket.h +0 -22
- data/lib/iodine/http.rb +0 -4
data/ext/iodine/iodine.h
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#ifndef H_IODINE_H
|
2
|
+
#define H_IODINE_H
|
3
|
+
/*
|
4
|
+
Copyright: Boaz segev, 2016-2017
|
5
|
+
License: MIT
|
6
|
+
|
7
|
+
Feel free to copy, use and enjoy according to the license provided.
|
8
|
+
*/
|
9
|
+
#include <ruby.h>
|
10
|
+
#ifndef _GNU_SOURCE
|
11
|
+
#define _GNU_SOURCE
|
12
|
+
#endif
|
13
|
+
#include <ruby/encoding.h>
|
14
|
+
#include <ruby/io.h>
|
15
|
+
#include <ruby/thread.h>
|
16
|
+
#include <ruby/version.h>
|
17
|
+
|
18
|
+
#include "rb-call.h"
|
19
|
+
#include "rb-registry.h"
|
20
|
+
|
21
|
+
#include "facil.h"
|
22
|
+
|
23
|
+
#include <stdio.h>
|
24
|
+
#include <stdlib.h>
|
25
|
+
#include <string.h>
|
26
|
+
|
27
|
+
#ifndef UNUSED_FUNC
|
28
|
+
#define UNUSED_FUNC __attribute__((unused))
|
29
|
+
#endif
|
30
|
+
|
31
|
+
extern VALUE Iodine;
|
32
|
+
extern VALUE IodineBase;
|
33
|
+
extern VALUE Iodine_Version;
|
34
|
+
|
35
|
+
extern ID iodine_fd_var_id;
|
36
|
+
extern ID iodine_timeout_var_id;
|
37
|
+
extern ID iodine_call_proc_id;
|
38
|
+
extern ID iodine_new_func_id;
|
39
|
+
extern ID iodine_on_open_func_id;
|
40
|
+
extern ID iodine_on_message_func_id;
|
41
|
+
extern ID iodine_on_data_func_id;
|
42
|
+
extern ID iodine_on_ready_func_id;
|
43
|
+
extern ID iodine_on_shutdown_func_id;
|
44
|
+
extern ID iodine_on_close_func_id;
|
45
|
+
extern ID iodine_ping_func_id;
|
46
|
+
extern ID iodine_buff_var_id;
|
47
|
+
extern ID iodine_to_s_method_id;
|
48
|
+
extern ID iodine_to_i_func_id;
|
49
|
+
|
50
|
+
extern rb_encoding *IodineBinaryEncoding;
|
51
|
+
extern rb_encoding *IodineUTF8Encoding;
|
52
|
+
extern int IodineBinaryEncodingIndex;
|
53
|
+
extern int IodineUTF8EncodingIndex;
|
54
|
+
|
55
|
+
UNUSED_FUNC static inline void iodine_set_fd(VALUE handler, intptr_t fd) {
|
56
|
+
rb_ivar_set(handler, iodine_fd_var_id, LONG2NUM((long)fd));
|
57
|
+
}
|
58
|
+
UNUSED_FUNC static inline intptr_t iodine_get_fd(VALUE handler) {
|
59
|
+
return ((intptr_t)NUM2LONG(rb_ivar_get(handler, iodine_fd_var_id)));
|
60
|
+
}
|
61
|
+
|
62
|
+
#endif
|
@@ -0,0 +1,235 @@
|
|
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_helpers.h"
|
8
|
+
|
9
|
+
#include "http.h"
|
10
|
+
|
11
|
+
/*
|
12
|
+
Add all sorts of useless stuff here.
|
13
|
+
*/
|
14
|
+
|
15
|
+
/* *****************************************************************************
|
16
|
+
URL Decoding
|
17
|
+
***************************************************************************** */
|
18
|
+
|
19
|
+
/**
|
20
|
+
Decodes a URL encoded String in place.
|
21
|
+
|
22
|
+
Raises an exception on error... but this might result in a partially decoded
|
23
|
+
String.
|
24
|
+
*/
|
25
|
+
static VALUE url_decode_inplace(VALUE self, VALUE str) {
|
26
|
+
Check_Type(str, T_STRING);
|
27
|
+
ssize_t len =
|
28
|
+
http_decode_url(RSTRING_PTR(str), RSTRING_PTR(str), RSTRING_LEN(str));
|
29
|
+
if (len < 0)
|
30
|
+
rb_raise(rb_eRuntimeError, "Malformed URL string - couldn't decode (String "
|
31
|
+
"might have been partially altered).");
|
32
|
+
rb_str_set_len(str, len);
|
33
|
+
return str;
|
34
|
+
(void)self;
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
Decodes a URL encoded String, returning a new String with the decoded data.
|
39
|
+
*/
|
40
|
+
static VALUE url_decode(VALUE self, VALUE str) {
|
41
|
+
Check_Type(str, T_STRING);
|
42
|
+
VALUE str2 = rb_str_buf_new(RSTRING_LEN(str));
|
43
|
+
ssize_t len =
|
44
|
+
http_decode_url(RSTRING_PTR(str2), RSTRING_PTR(str), RSTRING_LEN(str));
|
45
|
+
if (len < 0)
|
46
|
+
rb_raise(rb_eRuntimeError, "Malformed URL string - couldn't decode.");
|
47
|
+
rb_str_set_len(str2, len);
|
48
|
+
return str2;
|
49
|
+
(void)self;
|
50
|
+
}
|
51
|
+
|
52
|
+
/**
|
53
|
+
Decodes a percent encoded String (normally the "path" of a request), editing the
|
54
|
+
String in place.
|
55
|
+
|
56
|
+
Raises an exception on error... but this might result in a partially decoded
|
57
|
+
String.
|
58
|
+
*/
|
59
|
+
static VALUE path_decode_inplace(VALUE self, VALUE str) {
|
60
|
+
Check_Type(str, T_STRING);
|
61
|
+
ssize_t len =
|
62
|
+
http_decode_path(RSTRING_PTR(str), RSTRING_PTR(str), RSTRING_LEN(str));
|
63
|
+
if (len < 0)
|
64
|
+
rb_raise(rb_eRuntimeError,
|
65
|
+
"Malformed URL path string - couldn't decode (String "
|
66
|
+
"might have been partially altered).");
|
67
|
+
rb_str_set_len(str, len);
|
68
|
+
return str;
|
69
|
+
(void)self;
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
Decodes a percent encoded String (normally the "path" of a request), returning a
|
74
|
+
new String with the decoded data.
|
75
|
+
*/
|
76
|
+
static VALUE path_decode(VALUE self, VALUE str) {
|
77
|
+
Check_Type(str, T_STRING);
|
78
|
+
VALUE str2 = rb_str_buf_new(RSTRING_LEN(str));
|
79
|
+
ssize_t len =
|
80
|
+
http_decode_path(RSTRING_PTR(str2), RSTRING_PTR(str), RSTRING_LEN(str));
|
81
|
+
if (len < 0)
|
82
|
+
rb_raise(rb_eRuntimeError, "Malformed URL path string - couldn't decode.");
|
83
|
+
rb_str_set_len(str2, len);
|
84
|
+
return str2;
|
85
|
+
(void)self;
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
Decodes a URL encoded String, returning a new String with the decoded data.
|
90
|
+
|
91
|
+
This variation matches the Rack::Utils.unescape signature by accepting and
|
92
|
+
mostly ignoring an optional Encoding argument.
|
93
|
+
*/
|
94
|
+
static VALUE unescape(int argc, VALUE *argv, VALUE self) {
|
95
|
+
if (argc < 1 || argc > 2)
|
96
|
+
rb_raise(rb_eArgError,
|
97
|
+
"wrong number of arguments (given %d, expected 1..2).", argc);
|
98
|
+
VALUE str = argv[0];
|
99
|
+
Check_Type(str, T_STRING);
|
100
|
+
VALUE str2 = rb_str_buf_new(RSTRING_LEN(str));
|
101
|
+
ssize_t len =
|
102
|
+
http_decode_url(RSTRING_PTR(str2), RSTRING_PTR(str), RSTRING_LEN(str));
|
103
|
+
if (len < 0)
|
104
|
+
rb_raise(rb_eRuntimeError, "Malformed URL path string - couldn't decode.");
|
105
|
+
rb_str_set_len(str2, len);
|
106
|
+
rb_encoding *enc = IodineUTF8Encoding;
|
107
|
+
if (argc == 2 && argv[1] != Qnil && argv[1] != Qfalse) {
|
108
|
+
enc = rb_enc_get(argv[1]);
|
109
|
+
if (!enc)
|
110
|
+
enc = IodineUTF8Encoding;
|
111
|
+
}
|
112
|
+
rb_enc_associate(str2, enc);
|
113
|
+
return str2;
|
114
|
+
(void)self;
|
115
|
+
}
|
116
|
+
|
117
|
+
/* *****************************************************************************
|
118
|
+
HTTP Dates
|
119
|
+
***************************************************************************** */
|
120
|
+
|
121
|
+
/**
|
122
|
+
Takes an optional Integer for Unix Time and returns a faster (though less
|
123
|
+
localized) HTTP Date formatted String.
|
124
|
+
|
125
|
+
|
126
|
+
Iodine::Rack.time2str => "Sun, 11 Jun 2017 06:14:08 GMT"
|
127
|
+
|
128
|
+
Iodine::Rack.time2str(Time.now.to_i) => "Wed, 15 Nov 1995 06:25:24 GMT"
|
129
|
+
|
130
|
+
Since Iodine uses time caching within it's reactor, using the default value
|
131
|
+
(now) will be faster than providing an explicit time using `Time.now.to_i`.
|
132
|
+
|
133
|
+
*/
|
134
|
+
static VALUE date_str(int argc, VALUE *argv, VALUE self) {
|
135
|
+
if (argc > 1)
|
136
|
+
rb_raise(rb_eArgError,
|
137
|
+
"wrong number of arguments (given %d, expected 0..1).", argc);
|
138
|
+
time_t last_tick;
|
139
|
+
if (argc) {
|
140
|
+
if (TYPE(argv[0]) != T_FIXNUM)
|
141
|
+
argv[0] = rb_funcallv(argv[0], iodine_to_i_func_id, 0, NULL);
|
142
|
+
Check_Type(argv[0], T_FIXNUM);
|
143
|
+
last_tick = FIX2ULONG(argv[0]) ? FIX2ULONG(argv[0]) : facil_last_tick();
|
144
|
+
} else
|
145
|
+
last_tick = facil_last_tick();
|
146
|
+
VALUE str = rb_str_buf_new(32);
|
147
|
+
struct tm tm;
|
148
|
+
|
149
|
+
http_gmtime(&last_tick, &tm);
|
150
|
+
size_t len = http_date2str(RSTRING_PTR(str), &tm);
|
151
|
+
rb_str_set_len(str, len);
|
152
|
+
return str;
|
153
|
+
(void)self;
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
Takes `time` and returns a faster (though less localized) HTTP Date formatted
|
158
|
+
String.
|
159
|
+
|
160
|
+
|
161
|
+
Iodine::Rack.rfc2822(Time.now) => "Sun, 11 Jun 2017 06:14:08 -0000"
|
162
|
+
|
163
|
+
Iodine::Rack.rfc2822(0) => "Sun, 11 Jun 2017 06:14:08 -0000"
|
164
|
+
|
165
|
+
Since Iodine uses time caching within it's reactor, using the default value
|
166
|
+
(by passing 0) will be faster than providing an explicit time using `Time.now`.
|
167
|
+
*/
|
168
|
+
static VALUE iodine_rfc2822(VALUE self, VALUE rtm) {
|
169
|
+
time_t last_tick;
|
170
|
+
rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
|
171
|
+
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : facil_last_tick();
|
172
|
+
VALUE str = rb_str_buf_new(34);
|
173
|
+
struct tm tm;
|
174
|
+
|
175
|
+
http_gmtime(&last_tick, &tm);
|
176
|
+
size_t len = http_date2rfc2822(RSTRING_PTR(str), &tm);
|
177
|
+
rb_str_set_len(str, len);
|
178
|
+
return str;
|
179
|
+
(void)self;
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
Takes `time` and returns a faster (though less localized) HTTP Date formatted
|
184
|
+
String.
|
185
|
+
|
186
|
+
|
187
|
+
Iodine::Rack.rfc2109(Time.now) => "Sun, 11-Jun-2017 06:14:08 GMT"
|
188
|
+
|
189
|
+
Iodine::Rack.rfc2109(0) => "Sun, 11-Jun-2017 06:14:08 GMT"
|
190
|
+
|
191
|
+
Since Iodine uses time caching within it's reactor, using the default value
|
192
|
+
(by passing 0) will be faster than providing an explicit time using `Time.now`.
|
193
|
+
*/
|
194
|
+
static VALUE iodine_rfc2109(VALUE self, VALUE rtm) {
|
195
|
+
time_t last_tick;
|
196
|
+
rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
|
197
|
+
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : facil_last_tick();
|
198
|
+
VALUE str = rb_str_buf_new(32);
|
199
|
+
struct tm tm;
|
200
|
+
|
201
|
+
http_gmtime(&last_tick, &tm);
|
202
|
+
size_t len = http_date2rfc2109(RSTRING_PTR(str), &tm);
|
203
|
+
rb_str_set_len(str, len);
|
204
|
+
return str;
|
205
|
+
(void)self;
|
206
|
+
}
|
207
|
+
|
208
|
+
/* *****************************************************************************
|
209
|
+
Ruby Initialization
|
210
|
+
***************************************************************************** */
|
211
|
+
|
212
|
+
void Iodine_init_helpers(void) {
|
213
|
+
VALUE tmp = rb_define_module_under(Iodine, "Rack");
|
214
|
+
tmp = rb_define_module_under(tmp, "Utils");
|
215
|
+
rb_define_module_function(tmp, "decode_url!", url_decode_inplace, 1);
|
216
|
+
rb_define_module_function(tmp, "decode_url", url_decode, 1);
|
217
|
+
rb_define_module_function(tmp, "decode_path!", path_decode_inplace, 1);
|
218
|
+
rb_define_module_function(tmp, "decode_path", path_decode, 1);
|
219
|
+
rb_define_module_function(tmp, "time2str", date_str, -1);
|
220
|
+
rb_define_module_function(tmp, "rfc2109", iodine_rfc2109, 1);
|
221
|
+
rb_define_module_function(tmp, "rfc2822", iodine_rfc2822, 1);
|
222
|
+
|
223
|
+
tmp = rb_define_module_under(IodineBase, "MonkeyPatch");
|
224
|
+
tmp = rb_define_module_under(tmp, "RackUtils");
|
225
|
+
/* we define it all twice for easier monkey patching */
|
226
|
+
rb_define_method(tmp, "unescape", unescape, -1);
|
227
|
+
rb_define_method(tmp, "unescape_path", path_decode, 1);
|
228
|
+
rb_define_method(tmp, "rfc2109", iodine_rfc2109, 1);
|
229
|
+
rb_define_method(tmp, "rfc2822", iodine_rfc2822, 1);
|
230
|
+
rb_define_singleton_method(tmp, "unescape", unescape, -1);
|
231
|
+
rb_define_singleton_method(tmp, "unescape_path", path_decode, 1);
|
232
|
+
rb_define_singleton_method(tmp, "rfc2109", iodine_rfc2109, 1);
|
233
|
+
rb_define_singleton_method(tmp, "rfc2822", iodine_rfc2822, 1);
|
234
|
+
// rb_define_module_function(IodineUtils, "time2str", date_str, -1);
|
235
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#ifndef H_IODINE_HELPERS_H
|
2
|
+
#define H_IODINE_HELPERS_H
|
3
|
+
/*
|
4
|
+
Copyright: Boaz segev, 2016-2017
|
5
|
+
License: MIT
|
6
|
+
|
7
|
+
Feel free to copy, use and enjoy according to the license provided.
|
8
|
+
*/
|
9
|
+
#include "iodine.h"
|
10
|
+
|
11
|
+
void Iodine_init_helpers(void);
|
12
|
+
|
13
|
+
#endif
|
data/ext/iodine/iodine_http.c
CHANGED
@@ -5,45 +5,50 @@ 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 "
|
9
|
-
#include "
|
8
|
+
#include "iodine_websockets.h"
|
9
|
+
#include "rb-rack-io.h"
|
10
10
|
|
11
11
|
#include <arpa/inet.h>
|
12
12
|
#include <sys/socket.h>
|
13
13
|
|
14
|
-
/*
|
15
|
-
|
14
|
+
/* *****************************************************************************
|
15
|
+
Available Globals
|
16
|
+
***************************************************************************** */
|
17
|
+
VALUE IodineHTTP;
|
18
|
+
|
19
|
+
typedef struct {
|
20
|
+
VALUE app;
|
21
|
+
VALUE env;
|
22
|
+
unsigned long max_msg : 56;
|
23
|
+
unsigned ping : 8;
|
24
|
+
} iodine_http_settings_s;
|
25
|
+
|
16
26
|
/* these three are used also by rb-rack-io.c */
|
17
|
-
VALUE
|
18
|
-
VALUE
|
19
|
-
VALUE
|
27
|
+
VALUE IODINE_R_HIJACK;
|
28
|
+
VALUE IODINE_R_HIJACK_IO;
|
29
|
+
VALUE IODINE_R_HIJACK_CB;
|
20
30
|
|
21
31
|
VALUE UPGRADE_TCP;
|
22
32
|
VALUE UPGRADE_TCP_Q;
|
23
33
|
VALUE UPGRADE_WEBSOCKET;
|
24
34
|
VALUE UPGRADE_WEBSOCKET_Q;
|
25
|
-
/* backwards compatibility, temp */
|
26
|
-
VALUE IODINE_UPGRADE;
|
27
|
-
VALUE IODINE_WEBSOCKET;
|
28
35
|
|
29
36
|
static VALUE hijack_func_sym;
|
30
37
|
static ID to_fixnum_func_id;
|
31
38
|
static ID close_method_id;
|
32
39
|
static ID each_method_id;
|
33
|
-
static
|
34
|
-
|
40
|
+
static ID attach_method_id;
|
41
|
+
|
35
42
|
#define rack_declare(rack_name) static VALUE rack_name
|
36
43
|
|
37
44
|
#define rack_set(rack_name, str) \
|
38
|
-
(rack_name) = rb_enc_str_new((str), strlen((str)),
|
45
|
+
(rack_name) = rb_enc_str_new((str), strlen((str)), IodineBinaryEncoding); \
|
39
46
|
rb_global_variable(&(rack_name)); \
|
40
47
|
rb_obj_freeze(rack_name);
|
41
48
|
|
42
49
|
#define rack_autoset(rack_name) rack_set((rack_name), #rack_name)
|
43
50
|
|
44
|
-
static uint8_t IODINE_IS_DEVELOPMENT_MODE = 0;
|
45
|
-
|
46
|
-
static VALUE ENV_TEMPLATE;
|
51
|
+
// static uint8_t IODINE_IS_DEVELOPMENT_MODE = 0;
|
47
52
|
|
48
53
|
rack_declare(HTTP_SCHEME);
|
49
54
|
rack_declare(HTTPS_SCHEME);
|
@@ -63,44 +68,42 @@ rack_declare(R_URL_SCHEME); // rack.url_scheme
|
|
63
68
|
rack_declare(R_INPUT); // rack.input
|
64
69
|
rack_declare(XSENDFILE); // for X-Sendfile support
|
65
70
|
rack_declare(CONTENT_LENGTH_HEADER); // for X-Sendfile support
|
66
|
-
// rack_declare(
|
67
|
-
// rack_declare(
|
71
|
+
// rack_declare(IODINE_R_HIJACK); // rack.hijack
|
72
|
+
// rack_declare(IODINE_R_HIJACK_CB);// rack.hijack_io
|
68
73
|
|
69
74
|
/* *****************************************************************************
|
70
|
-
|
71
|
-
*/
|
72
|
-
/* allow quick access to the Rack app */
|
73
|
-
static VALUE rack_app_handler = 0;
|
75
|
+
Copying data from the C request to the Rack's ENV
|
76
|
+
***************************************************************************** */
|
74
77
|
|
75
78
|
#define to_upper(c) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~32) : (c))
|
76
79
|
|
77
|
-
static inline VALUE copy2env(http_request_s *request) {
|
78
|
-
VALUE env = rb_hash_dup(
|
80
|
+
static inline VALUE copy2env(http_request_s *request, VALUE template) {
|
81
|
+
VALUE env = rb_hash_dup(template);
|
82
|
+
Registry.add(env);
|
79
83
|
VALUE hname; /* will be used later, both as tmp and to iterate header names */
|
80
84
|
char *pos = NULL;
|
81
85
|
const char *reader = NULL;
|
82
|
-
Registry.add(env);
|
83
86
|
/* Copy basic data */
|
84
|
-
rb_hash_aset(
|
85
|
-
|
86
|
-
|
87
|
+
rb_hash_aset(env, REQUEST_METHOD,
|
88
|
+
rb_enc_str_new(request->method, request->method_len,
|
89
|
+
IodineBinaryEncoding));
|
87
90
|
|
88
91
|
rb_hash_aset(
|
89
92
|
env, PATH_INFO,
|
90
|
-
rb_enc_str_new(request->path, request->path_len,
|
91
|
-
rb_hash_aset(
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
rb_hash_aset(
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
hname =
|
103
|
-
|
93
|
+
rb_enc_str_new(request->path, request->path_len, IodineBinaryEncoding));
|
94
|
+
rb_hash_aset(env, QUERY_STRING,
|
95
|
+
(request->query
|
96
|
+
? rb_enc_str_new(request->query, request->query_len,
|
97
|
+
IodineBinaryEncoding)
|
98
|
+
: QUERY_ESTRING));
|
99
|
+
rb_hash_aset(env, QUERY_STRING,
|
100
|
+
(request->query
|
101
|
+
? rb_enc_str_new(request->query, request->query_len,
|
102
|
+
IodineBinaryEncoding)
|
103
|
+
: QUERY_ESTRING));
|
104
|
+
|
105
|
+
hname = rb_enc_str_new(request->version, request->version_len,
|
106
|
+
IodineBinaryEncoding);
|
104
107
|
rb_hash_aset(env, SERVER_PROTOCOL, hname);
|
105
108
|
rb_hash_aset(env, HTTP_VERSION, hname);
|
106
109
|
|
@@ -119,7 +122,7 @@ static inline VALUE copy2env(http_request_s *request) {
|
|
119
122
|
}
|
120
123
|
|
121
124
|
/* setup input IO + hijack support */
|
122
|
-
rb_hash_aset(env, R_INPUT, (hname =
|
125
|
+
rb_hash_aset(env, R_INPUT, (hname = IodineRackIO.create(request, env)));
|
123
126
|
|
124
127
|
/* publish upgrade support */
|
125
128
|
if (request->upgrade) {
|
@@ -128,7 +131,7 @@ static inline VALUE copy2env(http_request_s *request) {
|
|
128
131
|
}
|
129
132
|
|
130
133
|
hname = rb_obj_method(hname, hijack_func_sym);
|
131
|
-
rb_hash_aset(env,
|
134
|
+
rb_hash_aset(env, IODINE_R_HIJACK, hname);
|
132
135
|
|
133
136
|
/* handle the HOST header, including the possible host:#### format*/
|
134
137
|
pos = (char *)request->host;
|
@@ -137,16 +140,16 @@ static inline VALUE copy2env(http_request_s *request) {
|
|
137
140
|
if (*pos == 0) {
|
138
141
|
rb_hash_aset(
|
139
142
|
env, SERVER_NAME,
|
140
|
-
rb_enc_str_new(request->host, request->host_len,
|
143
|
+
rb_enc_str_new(request->host, request->host_len, IodineBinaryEncoding));
|
141
144
|
rb_hash_aset(env, SERVER_PORT, QUERY_ESTRING);
|
142
145
|
} else {
|
143
|
-
rb_hash_aset(
|
144
|
-
|
145
|
-
|
146
|
+
rb_hash_aset(env, SERVER_NAME,
|
147
|
+
rb_enc_str_new(request->host, pos - request->host,
|
148
|
+
IodineBinaryEncoding));
|
146
149
|
rb_hash_aset(env, SERVER_PORT,
|
147
150
|
rb_enc_str_new(pos + 1,
|
148
151
|
request->host_len - ((pos + 1) - request->host),
|
149
|
-
|
152
|
+
IodineBinaryEncoding));
|
150
153
|
}
|
151
154
|
|
152
155
|
/* default schema to http, it might be updated later */
|
@@ -159,14 +162,14 @@ static inline VALUE copy2env(http_request_s *request) {
|
|
159
162
|
strncasecmp("content-length", header.name, 14) == 0) {
|
160
163
|
rb_hash_aset(
|
161
164
|
env, CONTENT_LENGTH,
|
162
|
-
rb_enc_str_new(header.value, header.value_len,
|
165
|
+
rb_enc_str_new(header.value, header.value_len, IodineBinaryEncoding));
|
163
166
|
header = http_request_header_next(request);
|
164
167
|
continue;
|
165
168
|
} else if (header.name_len == 12 &&
|
166
169
|
strncasecmp("content-type", header.name, 12) == 0) {
|
167
170
|
rb_hash_aset(
|
168
171
|
env, CONTENT_TYPE,
|
169
|
-
rb_enc_str_new(header.value, header.value_len,
|
172
|
+
rb_enc_str_new(header.value, header.value_len, IodineBinaryEncoding));
|
170
173
|
header = http_request_header_next(request);
|
171
174
|
continue;
|
172
175
|
} else if (header.name_len == 27 &&
|
@@ -177,9 +180,9 @@ static inline VALUE copy2env(http_request_s *request) {
|
|
177
180
|
*((uint32_t *)header.value) == *((uint32_t *)"http")) {
|
178
181
|
rb_hash_aset(env, R_URL_SCHEME, HTTP_SCHEME);
|
179
182
|
} else {
|
180
|
-
rb_hash_aset(
|
181
|
-
|
182
|
-
|
183
|
+
rb_hash_aset(env, R_URL_SCHEME,
|
184
|
+
rb_enc_str_new(header.value, header.value_len,
|
185
|
+
IodineBinaryEncoding));
|
183
186
|
}
|
184
187
|
} else if (header.name_len == 9 &&
|
185
188
|
strncasecmp("forwarded", header.name, 9) == 0) {
|
@@ -220,22 +223,26 @@ static inline VALUE copy2env(http_request_s *request) {
|
|
220
223
|
rb_str_set_len(hname, 5 + header.name_len);
|
221
224
|
rb_hash_aset(
|
222
225
|
env, hname,
|
223
|
-
rb_enc_str_new(header.value, header.value_len,
|
226
|
+
rb_enc_str_new(header.value, header.value_len, IodineBinaryEncoding));
|
224
227
|
header = http_request_header_next(request);
|
225
228
|
}
|
226
229
|
return env;
|
227
230
|
}
|
228
231
|
|
232
|
+
/* *****************************************************************************
|
233
|
+
Handling the HTTP response
|
234
|
+
***************************************************************************** */
|
235
|
+
|
229
236
|
// itterate through the headers and add them to the response buffer
|
230
237
|
// (we are recycling the request's buffer)
|
231
238
|
static int for_each_header_data(VALUE key, VALUE val, VALUE _res) {
|
232
239
|
// fprintf(stderr, "For_each - headers\n");
|
233
240
|
if (TYPE(key) != T_STRING)
|
234
|
-
key = RubyCaller.call(key,
|
241
|
+
key = RubyCaller.call(key, iodine_to_s_method_id);
|
235
242
|
if (TYPE(key) != T_STRING)
|
236
243
|
return ST_CONTINUE;
|
237
244
|
if (TYPE(val) != T_STRING) {
|
238
|
-
val = RubyCaller.call(val,
|
245
|
+
val = RubyCaller.call(val, iodine_to_s_method_id);
|
239
246
|
if (TYPE(val) != T_STRING)
|
240
247
|
return ST_STOP;
|
241
248
|
}
|
@@ -273,13 +280,10 @@ static VALUE for_each_body_string(VALUE str, VALUE _res, int argc, VALUE argv) {
|
|
273
280
|
if (RSTRING_LEN(str)) {
|
274
281
|
if (http_response_write_body((void *)_res, RSTRING_PTR(str),
|
275
282
|
RSTRING_LEN(str))) {
|
276
|
-
// fprintf(stderr,
|
277
|
-
//
|
278
|
-
// "couldn't write response to connection\n");
|
283
|
+
// fprintf(stderr, "Iodine Server Error:"
|
284
|
+
// "couldn't write response to connection\n");
|
279
285
|
return Qfalse;
|
280
286
|
}
|
281
|
-
} else {
|
282
|
-
return Qfalse;
|
283
287
|
}
|
284
288
|
return Qtrue;
|
285
289
|
}
|
@@ -326,33 +330,39 @@ static inline int ruby2c_response_send(http_response_s *response,
|
|
326
330
|
return -1;
|
327
331
|
}
|
328
332
|
|
333
|
+
/* *****************************************************************************
|
334
|
+
Handling Upgrade cases
|
335
|
+
***************************************************************************** */
|
336
|
+
|
329
337
|
static inline int ruby2c_review_upgrade(http_response_s *response,
|
330
338
|
VALUE rbresponse, VALUE env) {
|
331
339
|
VALUE handler;
|
332
|
-
if ((handler = rb_hash_aref(env,
|
340
|
+
if ((handler = rb_hash_aref(env, IODINE_R_HIJACK_CB)) != Qnil) {
|
333
341
|
// send headers
|
334
342
|
http_response_finish(response);
|
335
|
-
// remove socket from
|
343
|
+
// remove socket from facil.io
|
336
344
|
facil_attach(response->fd, NULL);
|
337
345
|
// call the callback
|
338
|
-
VALUE io_ruby = RubyCaller.call(rb_hash_aref(env,
|
339
|
-
|
340
|
-
|
346
|
+
VALUE io_ruby = RubyCaller.call(rb_hash_aref(env, IODINE_R_HIJACK),
|
347
|
+
iodine_call_proc_id);
|
348
|
+
RubyCaller.call2(handler, iodine_call_proc_id, 1, &io_ruby);
|
349
|
+
} else if ((handler = rb_hash_aref(env, IODINE_R_HIJACK_IO)) != Qnil) {
|
341
350
|
// send nothing.
|
342
351
|
http_response_destroy(response);
|
343
|
-
//
|
352
|
+
// remove socket from facil.io
|
344
353
|
facil_attach(response->fd, NULL);
|
345
|
-
} else if ((handler = rb_hash_aref(env, UPGRADE_WEBSOCKET)) != Qnil
|
346
|
-
|
354
|
+
} else if ((handler = rb_hash_aref(env, UPGRADE_WEBSOCKET)) != Qnil) {
|
355
|
+
iodine_http_settings_s *settings = response->request->settings->udata;
|
347
356
|
// use response as existing base for native websocket upgrade
|
348
|
-
iodine_websocket_upgrade(response->request, response, handler
|
349
|
-
|
350
|
-
|
357
|
+
iodine_websocket_upgrade(response->request, response, handler,
|
358
|
+
settings->max_msg, settings->ping);
|
359
|
+
} else if ((handler = rb_hash_aref(env, UPGRADE_TCP)) != Qnil) {
|
351
360
|
intptr_t fduuid = response->fd;
|
352
361
|
// send headers
|
353
362
|
http_response_finish(response);
|
354
363
|
// upgrade protocol
|
355
|
-
|
364
|
+
VALUE args[2] = {(ULONG2NUM(sock_uuid2fd(fduuid))), handler};
|
365
|
+
RubyCaller.call2(Iodine, attach_method_id, 2, args);
|
356
366
|
// prevent response processing.
|
357
367
|
} else {
|
358
368
|
return 0;
|
@@ -365,16 +375,25 @@ static inline int ruby2c_review_upgrade(http_response_s *response,
|
|
365
375
|
return 1;
|
366
376
|
}
|
367
377
|
|
378
|
+
/* *****************************************************************************
|
379
|
+
Handling HTTP requests
|
380
|
+
***************************************************************************** */
|
381
|
+
|
368
382
|
static void *on_rack_request_in_GVL(http_request_s *request) {
|
369
383
|
http_response_s *response = http_response_create(request);
|
370
|
-
|
384
|
+
iodine_http_settings_s *settings = request->settings->udata;
|
385
|
+
if (request->settings->log_static)
|
371
386
|
http_response_log_start(response);
|
387
|
+
if (!settings->app)
|
388
|
+
goto err_not_found;
|
389
|
+
|
372
390
|
// create /register env variable
|
373
|
-
VALUE env = copy2env(request);
|
391
|
+
VALUE env = copy2env(request, settings->env);
|
374
392
|
// will be used later
|
375
393
|
VALUE tmp;
|
376
394
|
// pass env variable to handler
|
377
|
-
VALUE rbresponse =
|
395
|
+
VALUE rbresponse =
|
396
|
+
RubyCaller.call2(settings->app, iodine_call_proc_id, 1, &env);
|
378
397
|
if (rbresponse == 0 || rbresponse == Qnil)
|
379
398
|
goto internal_error;
|
380
399
|
Registry.add(rbresponse);
|
@@ -391,20 +410,39 @@ static void *on_rack_request_in_GVL(http_request_s *request) {
|
|
391
410
|
goto internal_error;
|
392
411
|
// extract the X-Sendfile header (never show original path)
|
393
412
|
// X-Sendfile support only present when iodine sercers static files.
|
394
|
-
VALUE xfiles
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
413
|
+
VALUE xfiles;
|
414
|
+
if (request->settings->public_folder &&
|
415
|
+
(xfiles = rb_hash_aref(response_headers, XSENDFILE)) != Qnil) {
|
416
|
+
int fr = 0;
|
417
|
+
if (OBJ_FROZEN(response_headers)) {
|
418
|
+
response_headers = rb_hash_dup(response_headers);
|
419
|
+
Registry.add(response_headers);
|
420
|
+
fr = 1;
|
421
|
+
}
|
422
|
+
rb_hash_delete(response_headers, XSENDFILE);
|
423
|
+
// remove XFile's content length headers, as this will be controled by
|
424
|
+
// Iodine
|
399
425
|
rb_hash_delete(response_headers, CONTENT_LENGTH_HEADER);
|
426
|
+
// review each header and write it to the response.
|
427
|
+
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(response));
|
428
|
+
if (fr)
|
429
|
+
Registry.remove(response_headers);
|
430
|
+
// send the file directly and finish
|
431
|
+
if (http_response_sendfile2(response, request, RSTRING_PTR(xfiles),
|
432
|
+
RSTRING_LEN(xfiles), NULL, 0, 1)) {
|
433
|
+
http_response_destroy(response);
|
434
|
+
response = http_response_create(request);
|
435
|
+
if (request->settings->log_static)
|
436
|
+
http_response_log_start(response);
|
437
|
+
Registry.remove(rbresponse);
|
438
|
+
Registry.remove(env);
|
439
|
+
goto err_not_found;
|
440
|
+
}
|
441
|
+
goto external_done;
|
400
442
|
}
|
401
443
|
// review each header and write it to the response.
|
402
444
|
rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(response));
|
403
445
|
// If the X-Sendfile header was provided, send the file directly and finish
|
404
|
-
if (xfiles != Qnil &&
|
405
|
-
http_response_sendfile2(response, request, RSTRING_PTR(xfiles),
|
406
|
-
RSTRING_LEN(xfiles), NULL, 0, 1) == 0)
|
407
|
-
goto external_done;
|
408
446
|
// review for belated (post response headers) upgrade.
|
409
447
|
if (ruby2c_review_upgrade(response, rbresponse, env))
|
410
448
|
goto external_done;
|
@@ -416,20 +454,41 @@ static void *on_rack_request_in_GVL(http_request_s *request) {
|
|
416
454
|
Registry.remove(env);
|
417
455
|
http_response_finish(response);
|
418
456
|
return NULL;
|
457
|
+
|
419
458
|
external_done:
|
420
459
|
Registry.remove(rbresponse);
|
421
460
|
Registry.remove(env);
|
422
461
|
return NULL;
|
462
|
+
|
463
|
+
err_not_found:
|
464
|
+
response->status = 404;
|
465
|
+
if (!request->settings->public_folder ||
|
466
|
+
http_response_sendfile2(response, request,
|
467
|
+
request->settings->public_folder,
|
468
|
+
request->settings->public_folder_length,
|
469
|
+
"404.html", 8, request->settings->log_static)) {
|
470
|
+
http_response_write_body(response, "Error 404, Page Not Found.", 26);
|
471
|
+
http_response_finish(response);
|
472
|
+
}
|
473
|
+
return NULL;
|
474
|
+
|
423
475
|
internal_error:
|
424
|
-
|
476
|
+
if (rbresponse && rbresponse != Qnil)
|
477
|
+
Registry.remove(rbresponse);
|
425
478
|
Registry.remove(env);
|
426
479
|
http_response_destroy(response);
|
427
480
|
response = http_response_create(request);
|
428
|
-
if (
|
481
|
+
if (request->settings->log_static)
|
429
482
|
http_response_log_start(response);
|
430
483
|
response->status = 500;
|
431
|
-
|
432
|
-
|
484
|
+
if (!request->settings->public_folder ||
|
485
|
+
http_response_sendfile2(response, request,
|
486
|
+
request->settings->public_folder,
|
487
|
+
request->settings->public_folder_length,
|
488
|
+
"500.html", 8, request->settings->log_static)) {
|
489
|
+
http_response_write_body(response, "Error 500, Internal error.", 26);
|
490
|
+
http_response_finish(response);
|
491
|
+
}
|
433
492
|
return NULL;
|
434
493
|
}
|
435
494
|
|
@@ -440,173 +499,296 @@ static void on_rack_request(http_request_s *request) {
|
|
440
499
|
}
|
441
500
|
|
442
501
|
/* *****************************************************************************
|
443
|
-
Initializing basic ENV template
|
444
|
-
*/
|
502
|
+
Initializing basic Rack ENV template
|
503
|
+
***************************************************************************** */
|
445
504
|
|
446
505
|
#define add_str_to_env(env, key, value) \
|
447
506
|
{ \
|
448
|
-
VALUE k = rb_enc_str_new((key), strlen((key)),
|
507
|
+
VALUE k = rb_enc_str_new((key), strlen((key)), IodineBinaryEncoding); \
|
449
508
|
rb_obj_freeze(k); \
|
450
|
-
VALUE v = rb_enc_str_new((value), strlen((value)),
|
509
|
+
VALUE v = rb_enc_str_new((value), strlen((value)), IodineBinaryEncoding); \
|
451
510
|
rb_obj_freeze(v); \
|
452
511
|
rb_hash_aset(env, k, v); \
|
453
512
|
}
|
454
513
|
#define add_value_to_env(env, key, value) \
|
455
514
|
{ \
|
456
|
-
VALUE k = rb_enc_str_new((key), strlen((key)),
|
515
|
+
VALUE k = rb_enc_str_new((key), strlen((key)), IodineBinaryEncoding); \
|
457
516
|
rb_obj_freeze(k); \
|
458
|
-
rb_hash_aset(env, k, value);
|
517
|
+
rb_hash_aset((env), k, value); \
|
459
518
|
}
|
460
519
|
|
461
|
-
static void init_env_template(
|
520
|
+
static void init_env_template(iodine_http_settings_s *set, uint8_t xsendfile) {
|
462
521
|
VALUE tmp;
|
463
|
-
|
464
|
-
|
522
|
+
set->env = rb_hash_new();
|
523
|
+
Registry.add(set->env);
|
465
524
|
|
466
525
|
// Start with the stuff Iodine will review.
|
467
|
-
rb_hash_aset(
|
468
|
-
rb_hash_aset(
|
469
|
-
if (
|
470
|
-
add_value_to_env(
|
471
|
-
add_value_to_env(
|
526
|
+
rb_hash_aset(set->env, UPGRADE_WEBSOCKET, Qnil);
|
527
|
+
rb_hash_aset(set->env, UPGRADE_TCP, Qnil);
|
528
|
+
if (xsendfile) {
|
529
|
+
add_value_to_env(set->env, "sendfile.type", XSENDFILE);
|
530
|
+
add_value_to_env(set->env, "HTTP_X_SENDFILE_TYPE", XSENDFILE);
|
472
531
|
}
|
473
|
-
rb_hash_aset(
|
474
|
-
rb_hash_aset(
|
475
|
-
|
476
|
-
/* backwards compatibility, temp */
|
477
|
-
rb_hash_aset(ENV_TEMPLATE, IODINE_WEBSOCKET, Qnil);
|
478
|
-
rb_hash_aset(ENV_TEMPLATE, IODINE_UPGRADE, Qnil);
|
532
|
+
rb_hash_aset(set->env, UPGRADE_WEBSOCKET_Q, Qnil);
|
533
|
+
rb_hash_aset(set->env, UPGRADE_TCP_Q, Qnil);
|
479
534
|
|
480
535
|
// add the rack.version
|
481
536
|
tmp = rb_ary_new(); // rb_ary_new is Ruby 2.0 compatible
|
482
537
|
rb_ary_push(tmp, INT2FIX(1));
|
483
538
|
rb_ary_push(tmp, INT2FIX(3));
|
484
|
-
// rb_ary_push(tmp, rb_enc_str_new("1", 1,
|
485
|
-
// rb_ary_push(tmp, rb_enc_str_new("3", 1,
|
486
|
-
add_value_to_env(
|
487
|
-
add_value_to_env(
|
488
|
-
add_value_to_env(
|
489
|
-
add_value_to_env(
|
490
|
-
add_value_to_env(
|
491
|
-
add_value_to_env(
|
492
|
-
add_str_to_env(
|
539
|
+
// rb_ary_push(tmp, rb_enc_str_new("1", 1, IodineBinaryEncoding));
|
540
|
+
// rb_ary_push(tmp, rb_enc_str_new("3", 1, IodineBinaryEncoding));
|
541
|
+
add_value_to_env(set->env, "rack.version", tmp);
|
542
|
+
add_value_to_env(set->env, "rack.errors", rb_stderr);
|
543
|
+
add_value_to_env(set->env, "rack.multithread", Qtrue);
|
544
|
+
add_value_to_env(set->env, "rack.multiprocess", Qtrue);
|
545
|
+
add_value_to_env(set->env, "rack.run_once", Qfalse);
|
546
|
+
add_value_to_env(set->env, "rack.hijack?", Qtrue);
|
547
|
+
add_str_to_env(set->env, "SCRIPT_NAME", "");
|
493
548
|
}
|
494
549
|
#undef add_str_to_env
|
495
550
|
#undef add_value_to_env
|
496
551
|
|
497
552
|
/* *****************************************************************************
|
498
|
-
|
553
|
+
Listenninng to HTTP
|
554
|
+
*****************************************************************************
|
499
555
|
*/
|
500
556
|
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
if (
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
557
|
+
void *iodine_print_http_msg2_in_gvl(void *d_) {
|
558
|
+
// Write message
|
559
|
+
struct {
|
560
|
+
VALUE www;
|
561
|
+
VALUE port;
|
562
|
+
} *arg = d_;
|
563
|
+
if (arg->www) {
|
564
|
+
fprintf(stderr,
|
565
|
+
"Iodine HTTP Server on port %s:\n"
|
566
|
+
" * Serving static files from %s\n\n",
|
567
|
+
StringValueCStr(arg->port), StringValueCStr(arg->www));
|
568
|
+
Registry.remove(arg->www);
|
569
|
+
}
|
570
|
+
Registry.remove(arg->port);
|
571
|
+
return NULL;
|
572
|
+
}
|
573
|
+
|
574
|
+
void *iodine_print_http_msg_in_gvl(void *d_) {
|
575
|
+
// Write message
|
576
|
+
VALUE iodine_version = rb_const_get(Iodine, rb_intern("VERSION"));
|
577
|
+
VALUE ruby_version = rb_const_get(Iodine, rb_intern("RUBY_VERSION"));
|
578
|
+
struct {
|
579
|
+
VALUE www;
|
580
|
+
VALUE port;
|
581
|
+
} *arg = d_;
|
582
|
+
if (arg->www) {
|
583
|
+
fprintf(stderr,
|
584
|
+
"\nStarting up Iodine HTTP Server on port %s:\n"
|
585
|
+
" * Ruby v.%s\n * Iodine v.%s \n"
|
586
|
+
" * %lu max concurrent connections / open files\n"
|
587
|
+
" * Serving static files from %s\n\n",
|
588
|
+
StringValueCStr(arg->port), StringValueCStr(ruby_version),
|
589
|
+
StringValueCStr(iodine_version), (size_t)sock_max_capacity(),
|
590
|
+
StringValueCStr(arg->www));
|
591
|
+
Registry.remove(arg->www);
|
592
|
+
} else
|
593
|
+
fprintf(stderr,
|
594
|
+
"\nStarting up Iodine HTTP Server on port %s:\n"
|
595
|
+
" * Ruby v.%s\n * Iodine v.%s \n"
|
596
|
+
" * %lu max concurrent connections / open files\n\n",
|
597
|
+
StringValueCStr(arg->port), StringValueCStr(ruby_version),
|
598
|
+
StringValueCStr(iodine_version), (size_t)sock_max_capacity());
|
599
|
+
Registry.remove(arg->port);
|
600
|
+
|
601
|
+
return NULL;
|
602
|
+
}
|
603
|
+
|
604
|
+
static void iodine_print_http_msg1(void *www, void *port) {
|
605
|
+
if (defer_fork_pid())
|
606
|
+
return;
|
607
|
+
struct {
|
608
|
+
void *www;
|
609
|
+
void *port;
|
610
|
+
} data = {.www = www, .port = port};
|
611
|
+
RubyCaller.call_c(iodine_print_http_msg_in_gvl, (void *)&data);
|
612
|
+
}
|
613
|
+
static void iodine_print_http_msg2(void *www, void *port) {
|
614
|
+
if (defer_fork_pid())
|
615
|
+
return;
|
616
|
+
struct {
|
617
|
+
void *www;
|
618
|
+
void *port;
|
619
|
+
} data = {.www = www, .port = port};
|
620
|
+
RubyCaller.call_c(iodine_print_http_msg2_in_gvl, (void *)&data);
|
621
|
+
}
|
622
|
+
|
623
|
+
static void free_iodine_http(intptr_t uuid, void *set_) {
|
624
|
+
iodine_http_settings_s *set = set_;
|
625
|
+
Registry.remove(set->app);
|
626
|
+
Registry.remove(set->env);
|
627
|
+
free(set);
|
628
|
+
(void)uuid;
|
629
|
+
}
|
630
|
+
/**
|
631
|
+
Listens to incoming HTTP connections and handles incoming requests using the
|
632
|
+
Rack specification.
|
633
|
+
|
634
|
+
This is delegated to a lower level C HTTP and Websocket implementation, no
|
635
|
+
Ruby object will be crated except the `env` object required by the Rack
|
636
|
+
specifications.
|
637
|
+
|
638
|
+
Accepts a single Hash argument with the following properties:
|
639
|
+
|
640
|
+
app:: the Rack application that handles incoming requests. Default: `nil`.
|
641
|
+
port:: the port to listen to. Default: 3000.
|
642
|
+
address:: the address to bind to. Default: binds to all possible addresses.
|
643
|
+
log:: enable response logging (Hijacked sockets aren't logged). Default: off.
|
644
|
+
public:: The root public folder for static file service. Default: none.
|
645
|
+
timeout:: Timeout for inactive HTTP/1.x connections. Defaults: 5 seconds.
|
646
|
+
max_body:: The maximum body size for incoming HTTP messages. Default: ~50Mib.
|
647
|
+
max_msg:: The maximum Websocket message size allowed. Default: ~250Kib.
|
648
|
+
ping:: The Websocket `ping` interval. Default: 40 sec.
|
649
|
+
|
650
|
+
Either the `app` or the `public` properties are required. If niether exists,
|
651
|
+
the function will fail. If both exist, Iodine will serve static files as well
|
652
|
+
as dynamic requests.
|
653
|
+
|
654
|
+
When using the static file server, it's possible to serve `gzip` versions of
|
655
|
+
the static files by saving a compressed version with the `gz` extension (i.e.
|
656
|
+
`styles.css.gz`).
|
657
|
+
|
658
|
+
`gzip` will only be served to clients tat support the `gzip` transfer
|
659
|
+
encoding.
|
660
|
+
|
661
|
+
Once HTTP/2 is supported (planned, but probably very far away), HTTP/2
|
662
|
+
timeouts will be dynamically managed by Iodine. The `timeout` option is only
|
663
|
+
relevant to HTTP/1.x connections.
|
664
|
+
*/
|
665
|
+
VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
666
|
+
static int called_once = 0;
|
667
|
+
uint8_t log_http = 0;
|
668
|
+
size_t ping = 0;
|
669
|
+
size_t max_body = 0;
|
670
|
+
size_t max_msg = 0;
|
671
|
+
Check_Type(opt, T_HASH);
|
672
|
+
VALUE app = rb_hash_aref(opt, ID2SYM(rb_intern("app")));
|
673
|
+
VALUE www = rb_hash_aref(opt, ID2SYM(rb_intern("public")));
|
674
|
+
VALUE port = rb_hash_aref(opt, ID2SYM(rb_intern("port")));
|
675
|
+
VALUE address = rb_hash_aref(opt, ID2SYM(rb_intern("address")));
|
676
|
+
VALUE tout = rb_hash_aref(opt, ID2SYM(rb_intern("timeout")));
|
677
|
+
|
678
|
+
VALUE tmp = rb_hash_aref(opt, ID2SYM(rb_intern("max_msg")));
|
679
|
+
if (tmp != Qnil && tmp != Qfalse) {
|
680
|
+
Check_Type(tmp, T_FIXNUM);
|
681
|
+
max_msg = FIX2ULONG(tmp);
|
682
|
+
}
|
683
|
+
|
684
|
+
tmp = rb_hash_aref(opt, ID2SYM(rb_intern("max_body")));
|
685
|
+
if (tmp != Qnil && tmp != Qfalse) {
|
686
|
+
Check_Type(tmp, T_FIXNUM);
|
687
|
+
max_body = FIX2ULONG(tmp);
|
688
|
+
}
|
689
|
+
|
690
|
+
tmp = rb_hash_aref(opt, ID2SYM(rb_intern("ping")));
|
691
|
+
if (tmp != Qnil && tmp != Qfalse) {
|
692
|
+
Check_Type(tmp, T_FIXNUM);
|
693
|
+
ping = FIX2ULONG(tmp);
|
694
|
+
}
|
695
|
+
if (ping > 255) {
|
696
|
+
fprintf(stderr, "Iodine Warning: Websocket timeout value "
|
697
|
+
"is over 255 and is silently ignored.\n");
|
698
|
+
ping = 0;
|
699
|
+
}
|
700
|
+
|
701
|
+
tmp = rb_hash_aref(opt, ID2SYM(rb_intern("log")));
|
702
|
+
if (tmp != Qnil && tmp != Qfalse)
|
703
|
+
log_http = 1;
|
704
|
+
|
705
|
+
if ((app == Qnil || app == Qfalse) && (www == Qnil || www == Qfalse)) {
|
706
|
+
fprintf(stderr, "Iodine Warning: HTTP without application or public folder "
|
707
|
+
"(is silently ignored).\n");
|
708
|
+
return Qfalse;
|
709
|
+
}
|
710
|
+
|
711
|
+
if ((www != Qnil && www != Qfalse)) {
|
712
|
+
Check_Type(www, T_STRING);
|
713
|
+
Registry.add(www);
|
714
|
+
} else
|
715
|
+
www = 0;
|
716
|
+
|
717
|
+
if ((address != Qnil && address != Qfalse))
|
718
|
+
Check_Type(address, T_STRING);
|
719
|
+
else
|
720
|
+
address = 0;
|
721
|
+
|
722
|
+
if ((tout != Qnil && tout != Qfalse)) {
|
723
|
+
Check_Type(tout, T_FIXNUM);
|
724
|
+
tout = FIX2ULONG(tout);
|
725
|
+
} else
|
726
|
+
tout = 0;
|
727
|
+
if (tout > 255) {
|
728
|
+
fprintf(stderr, "Iodine Warning: HTTP timeout value "
|
729
|
+
"is over 255 and is silently ignored.\n");
|
730
|
+
tout = 0;
|
731
|
+
}
|
732
|
+
|
733
|
+
if (port != Qnil && port != Qfalse) {
|
734
|
+
if (!RB_TYPE_P(port, T_STRING) && !RB_TYPE_P(port, T_FIXNUM))
|
541
735
|
rb_raise(rb_eTypeError,
|
542
|
-
"The
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
VALUE ruby_version = rb_const_get(Iodine, rb_intern("RUBY_VERSION"));
|
578
|
-
if (public_folder)
|
579
|
-
fprintf(stderr,
|
580
|
-
"Starting up Iodine HTTP Server:\n"
|
581
|
-
" * Ruby v.%s\n * Iodine v.%s \n"
|
582
|
-
" * %lu max concurrent connections / open files\n"
|
583
|
-
" * Serving static files from:\n"
|
584
|
-
" %s\n\n",
|
585
|
-
StringValueCStr(ruby_version), StringValueCStr(iodine_version),
|
586
|
-
(size_t)sock_max_capacity(), public_folder);
|
587
|
-
else
|
588
|
-
fprintf(stderr,
|
589
|
-
"Starting up Iodine HTTP Server:\n"
|
590
|
-
" * Ruby v.%s\n * Iodine v.%s \n"
|
591
|
-
" * %lu max concurrent connections / open files\n"
|
592
|
-
"\n",
|
593
|
-
StringValueCStr(ruby_version), StringValueCStr(iodine_version),
|
594
|
-
(size_t)sock_max_capacity());
|
595
|
-
|
596
|
-
// listen
|
597
|
-
return http_listen(port, address, .on_request = on_rack_request,
|
598
|
-
.log_static = iodine_http_request_logging,
|
599
|
-
.max_body_size = max_body_size,
|
600
|
-
.public_folder = public_folder, .timeout = timeout);
|
736
|
+
"The `port` property MUST be either a String or a Number");
|
737
|
+
if (RB_TYPE_P(port, T_FIXNUM))
|
738
|
+
port = rb_funcall2(port, iodine_to_s_method_id, 0, NULL);
|
739
|
+
} else
|
740
|
+
port = rb_str_new("3000", 4);
|
741
|
+
Registry.add(port);
|
742
|
+
|
743
|
+
if ((app != Qnil && app != Qfalse))
|
744
|
+
Registry.add(app);
|
745
|
+
else
|
746
|
+
app = 0;
|
747
|
+
|
748
|
+
iodine_http_settings_s *set = malloc(sizeof(*set));
|
749
|
+
*set = (iodine_http_settings_s){.app = app, .ping = ping, .max_msg = max_msg};
|
750
|
+
|
751
|
+
init_env_template(set, (www ? 1 : 0));
|
752
|
+
|
753
|
+
if (http_listen(StringValueCStr(port),
|
754
|
+
(address ? StringValueCStr(address) : NULL),
|
755
|
+
.on_request = on_rack_request, .udata = set,
|
756
|
+
.timeout = (tout ? FIX2INT(tout) : tout),
|
757
|
+
.on_finish = free_iodine_http, .log_static = log_http,
|
758
|
+
.max_body_size = max_body,
|
759
|
+
.public_folder = (www ? StringValueCStr(www) : NULL))) {
|
760
|
+
fprintf(stderr,
|
761
|
+
"ERROR: Failed to initialize a listening HTTP socket for port %s\n",
|
762
|
+
port ? StringValueCStr(port) : "3000");
|
763
|
+
return Qfalse;
|
764
|
+
}
|
765
|
+
|
766
|
+
if ((app == Qnil || app == Qfalse)) {
|
767
|
+
fprintf(stderr,
|
768
|
+
"* Iodine: (no app) the HTTP service on port %s will only serve "
|
769
|
+
"static files.\n",
|
770
|
+
(port ? StringValueCStr(port) : "3000"));
|
601
771
|
}
|
602
|
-
|
772
|
+
if (called_once)
|
773
|
+
defer(iodine_print_http_msg2, (www ? (void *)www : NULL), (void *)port);
|
774
|
+
else {
|
775
|
+
called_once = 1;
|
776
|
+
defer(iodine_print_http_msg1, (www ? (void *)www : NULL), (void *)port);
|
777
|
+
}
|
778
|
+
|
779
|
+
return Qtrue;
|
780
|
+
(void)self;
|
603
781
|
}
|
604
782
|
|
605
783
|
/* *****************************************************************************
|
606
|
-
|
784
|
+
Initialization
|
785
|
+
*****************************************************************************
|
607
786
|
*/
|
608
787
|
|
609
|
-
void
|
788
|
+
void Iodine_init_http(void) {
|
789
|
+
|
790
|
+
rb_define_module_function(Iodine, "listen2http", iodine_http_listen, 1);
|
791
|
+
|
610
792
|
rack_autoset(REQUEST_METHOD);
|
611
793
|
rack_autoset(PATH_INFO);
|
612
794
|
rack_autoset(QUERY_STRING);
|
@@ -625,9 +807,9 @@ void Init_iodine_http(void) {
|
|
625
807
|
rack_set(XSENDFILE, "X-Sendfile");
|
626
808
|
rack_set(CONTENT_LENGTH_HEADER, "Content-Length");
|
627
809
|
|
628
|
-
rack_set(
|
629
|
-
rack_set(
|
630
|
-
rack_set(
|
810
|
+
rack_set(IODINE_R_HIJACK_IO, "rack.hijack_io");
|
811
|
+
rack_set(IODINE_R_HIJACK, "rack.hijack");
|
812
|
+
rack_set(IODINE_R_HIJACK_CB, "iodine.hijack_cb");
|
631
813
|
|
632
814
|
rack_set(UPGRADE_TCP, "upgrade.tcp");
|
633
815
|
rack_set(UPGRADE_WEBSOCKET, "upgrade.websocket");
|
@@ -635,27 +817,13 @@ void Init_iodine_http(void) {
|
|
635
817
|
rack_set(UPGRADE_TCP_Q, "upgrade.tcp?");
|
636
818
|
rack_set(UPGRADE_WEBSOCKET_Q, "upgrade.websocket?");
|
637
819
|
|
638
|
-
/* backwards compatability, temp */
|
639
|
-
rack_set(IODINE_UPGRADE, "iodine.upgrade");
|
640
|
-
rack_set(IODINE_WEBSOCKET, "iodine.websocket");
|
641
|
-
|
642
820
|
rack_set(QUERY_ESTRING, "");
|
643
821
|
rack_set(QUERY_ESTRING, "");
|
644
822
|
|
645
823
|
hijack_func_sym = ID2SYM(rb_intern("_hijack"));
|
646
|
-
to_fixnum_func_id = rb_intern("to_i");
|
647
824
|
close_method_id = rb_intern("close");
|
648
825
|
each_method_id = rb_intern("each");
|
826
|
+
attach_method_id = rb_intern("attach_fd");
|
649
827
|
|
650
|
-
|
651
|
-
RackIO.init();
|
652
|
-
Init_iodine_websocket();
|
828
|
+
IodineRackIO.init();
|
653
829
|
}
|
654
|
-
|
655
|
-
// REQUEST_METHOD
|
656
|
-
// PATH_INFO
|
657
|
-
// QUERY_STRING
|
658
|
-
// SERVER_NAME
|
659
|
-
// SERVER_PORT
|
660
|
-
// CONTENT_LENGTH
|
661
|
-
// rack.url_scheme rack.input rack.hijack rack.hijack_io HTTP_ Variables
|