iodine 0.1.21 → 0.2.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/.gitignore +3 -2
- data/.travis.yml +23 -2
- data/CHANGELOG.md +9 -2
- data/README.md +232 -179
- data/Rakefile +13 -1
- data/bin/config.ru +63 -0
- data/bin/console +6 -0
- data/bin/echo +42 -32
- data/bin/http-hello +62 -0
- data/bin/http-playground +124 -0
- data/bin/playground +62 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/bin/raw-rbhttp +35 -0
- data/bin/raw_broadcast +66 -0
- data/bin/test_with_faye +40 -0
- data/bin/ws-broadcast +108 -0
- data/bin/ws-echo +108 -0
- data/exe/iodine +59 -0
- data/ext/iodine/base64.c +264 -0
- data/ext/iodine/base64.h +72 -0
- data/ext/iodine/bscrypt-common.h +109 -0
- data/ext/iodine/bscrypt.h +49 -0
- data/ext/iodine/extconf.rb +41 -0
- data/ext/iodine/hex.c +123 -0
- data/ext/iodine/hex.h +70 -0
- data/ext/iodine/http.c +200 -0
- data/ext/iodine/http.h +128 -0
- data/ext/iodine/http1.c +402 -0
- data/ext/iodine/http1.h +56 -0
- data/ext/iodine/http1_simple_parser.c +473 -0
- data/ext/iodine/http1_simple_parser.h +59 -0
- data/ext/iodine/http_request.h +128 -0
- data/ext/iodine/http_response.c +1606 -0
- data/ext/iodine/http_response.h +393 -0
- data/ext/iodine/http_response_http1.h +374 -0
- data/ext/iodine/iodine_core.c +641 -0
- data/ext/iodine/iodine_core.h +70 -0
- data/ext/iodine/iodine_http.c +615 -0
- data/ext/iodine/iodine_http.h +19 -0
- data/ext/iodine/iodine_websocket.c +430 -0
- data/ext/iodine/iodine_websocket.h +21 -0
- data/ext/iodine/libasync.c +552 -0
- data/ext/iodine/libasync.h +117 -0
- data/ext/iodine/libreact.c +347 -0
- data/ext/iodine/libreact.h +244 -0
- data/ext/iodine/libserver.c +912 -0
- data/ext/iodine/libserver.h +435 -0
- data/ext/iodine/libsock.c +950 -0
- data/ext/iodine/libsock.h +478 -0
- data/ext/iodine/misc.c +181 -0
- data/ext/iodine/misc.h +76 -0
- data/ext/iodine/random.c +193 -0
- data/ext/iodine/random.h +48 -0
- data/ext/iodine/rb-call.c +127 -0
- data/ext/iodine/rb-call.h +60 -0
- data/ext/iodine/rb-libasync.h +79 -0
- data/ext/iodine/rb-rack-io.c +389 -0
- data/ext/iodine/rb-rack-io.h +17 -0
- data/ext/iodine/rb-registry.c +213 -0
- data/ext/iodine/rb-registry.h +33 -0
- data/ext/iodine/sha1.c +359 -0
- data/ext/iodine/sha1.h +85 -0
- data/ext/iodine/sha2.c +825 -0
- data/ext/iodine/sha2.h +138 -0
- data/ext/iodine/siphash.c +136 -0
- data/ext/iodine/siphash.h +15 -0
- data/ext/iodine/spnlock.h +235 -0
- data/ext/iodine/websockets.c +696 -0
- data/ext/iodine/websockets.h +120 -0
- data/ext/iodine/xor-crypt.c +189 -0
- data/ext/iodine/xor-crypt.h +107 -0
- data/iodine.gemspec +25 -18
- data/lib/iodine.rb +57 -58
- data/lib/iodine/http.rb +0 -189
- data/lib/iodine/protocol.rb +36 -245
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +145 -2
- metadata +115 -37
- data/bin/core_http_test +0 -51
- data/bin/em playground +0 -56
- data/bin/hello_world +0 -75
- data/bin/setup +0 -7
- data/lib/iodine/client.rb +0 -5
- data/lib/iodine/core.rb +0 -102
- data/lib/iodine/core_init.rb +0 -143
- data/lib/iodine/http/hpack.rb +0 -553
- data/lib/iodine/http/http1.rb +0 -251
- data/lib/iodine/http/http2.rb +0 -507
- data/lib/iodine/http/rack_support.rb +0 -108
- data/lib/iodine/http/request.rb +0 -462
- data/lib/iodine/http/response.rb +0 -474
- data/lib/iodine/http/session.rb +0 -143
- data/lib/iodine/http/websocket_client.rb +0 -335
- data/lib/iodine/http/websocket_handler.rb +0 -101
- data/lib/iodine/http/websockets.rb +0 -336
- data/lib/iodine/io.rb +0 -56
- data/lib/iodine/logging.rb +0 -46
- data/lib/iodine/settings.rb +0 -158
- data/lib/iodine/ssl_connector.rb +0 -48
- data/lib/iodine/timers.rb +0 -95
@@ -0,0 +1,641 @@
|
|
1
|
+
#include "iodine_core.h"
|
2
|
+
#include "iodine_http.h"
|
3
|
+
#include <ruby/io.h>
|
4
|
+
|
5
|
+
/* *****************************************************************************
|
6
|
+
Core data
|
7
|
+
*/
|
8
|
+
|
9
|
+
/* these should be made globally accessible to any Iodine module */
|
10
|
+
rb_encoding *BinaryEncoding;
|
11
|
+
rb_encoding *UTF8Encoding;
|
12
|
+
int BinaryEncodingIndex;
|
13
|
+
int UTF8EncodingIndex;
|
14
|
+
VALUE Iodine;
|
15
|
+
VALUE IodineBase;
|
16
|
+
VALUE Iodine_Version;
|
17
|
+
const char *Iodine_Version_Str;
|
18
|
+
ID call_proc_id;
|
19
|
+
ID on_start_func_id;
|
20
|
+
ID on_finish_func_id;
|
21
|
+
ID new_func_id;
|
22
|
+
ID on_open_func_id;
|
23
|
+
ID on_message_func_id;
|
24
|
+
ID on_data_func_id;
|
25
|
+
ID on_ready_func_id;
|
26
|
+
ID on_shutdown_func_id;
|
27
|
+
ID on_close_func_id;
|
28
|
+
ID ping_func_id;
|
29
|
+
ID buff_var_id;
|
30
|
+
ID fd_var_id;
|
31
|
+
ID timeout_var_id;
|
32
|
+
ID to_s_method_id;
|
33
|
+
|
34
|
+
/* local core data variables */
|
35
|
+
static VALUE DynamicProtocol;
|
36
|
+
static VALUE DynamicProtocolClass;
|
37
|
+
/* *****************************************************************************
|
38
|
+
The Core dynamic Iodine protocol
|
39
|
+
*/
|
40
|
+
|
41
|
+
static const char *iodine_protocol_service = "IodineDynamicProtocol";
|
42
|
+
|
43
|
+
/* *****************************************************************************
|
44
|
+
The Core dynamic Iodine protocol methods and helpers
|
45
|
+
*/
|
46
|
+
|
47
|
+
/**
|
48
|
+
Reads `n` bytes from the network connection.
|
49
|
+
The number of bytes to be read (n) is:
|
50
|
+
- the number of bytes set in the optional `buffer_or_length` argument.
|
51
|
+
- the String capacity (not length) of the String passed as the optional
|
52
|
+
`buffer_or_length` argument.
|
53
|
+
- 1024 Bytes (1Kb) if the optional `buffer_or_length` is either missing or
|
54
|
+
contains a String who's capacity is less then 1Kb.
|
55
|
+
Returns a String (either the same one used as the buffer or a new one) on a
|
56
|
+
successful read. Returns `nil` if no data was available.
|
57
|
+
*/
|
58
|
+
static VALUE dyn_read(int argc, VALUE *argv, VALUE self) {
|
59
|
+
if (argc > 1) {
|
60
|
+
rb_raise(
|
61
|
+
rb_eArgError,
|
62
|
+
"read accepts only one argument - a Fixnum (buffer length) or a String "
|
63
|
+
"(it's capacity - or 1Kb, whichever's the higher - will be used as "
|
64
|
+
"buffer's length).");
|
65
|
+
return Qnil;
|
66
|
+
}
|
67
|
+
VALUE buffer = (argc == 1 ? argv[0] : Qnil);
|
68
|
+
if (buffer != Qnil && TYPE(buffer) != T_FIXNUM && TYPE(buffer) != T_STRING) {
|
69
|
+
rb_raise(rb_eTypeError,
|
70
|
+
"buffer should either be a length (a new string will be created) "
|
71
|
+
"or a string (reading will be limited to the original string's "
|
72
|
+
"capacity or 1Kb - whichever the larger).");
|
73
|
+
return Qnil;
|
74
|
+
}
|
75
|
+
VALUE str;
|
76
|
+
long len;
|
77
|
+
intptr_t fd = iodine_get_fd(self);
|
78
|
+
if (buffer == Qnil) {
|
79
|
+
buffer = LONG2FIX(1024);
|
80
|
+
}
|
81
|
+
if (TYPE(buffer) == T_FIXNUM) {
|
82
|
+
len = FIX2LONG(buffer);
|
83
|
+
if (len <= 0)
|
84
|
+
len = 1024;
|
85
|
+
str = rb_str_buf_new(len);
|
86
|
+
// create a rb_String with X length and take it's pointer
|
87
|
+
// rb_str_resize(VALUE str, long len)
|
88
|
+
// RSTRING_PTR(str)
|
89
|
+
} else {
|
90
|
+
// take the string's pointer and length
|
91
|
+
len = rb_str_capacity(buffer);
|
92
|
+
// make sure the string is modifiable
|
93
|
+
rb_str_modify(buffer);
|
94
|
+
// resize te string if needed.
|
95
|
+
if (len < 1024)
|
96
|
+
rb_str_resize(buffer, (len = 1024));
|
97
|
+
str = buffer;
|
98
|
+
}
|
99
|
+
ssize_t in = sock_read(fd, RSTRING_PTR(str), len);
|
100
|
+
// make sure it's binary encoded
|
101
|
+
rb_enc_associate_index(str, BinaryEncodingIndex);
|
102
|
+
// set actual size....
|
103
|
+
if (in > 0)
|
104
|
+
rb_str_set_len(str, (long)in);
|
105
|
+
else {
|
106
|
+
rb_str_set_len(str, 0);
|
107
|
+
str = Qnil;
|
108
|
+
}
|
109
|
+
// return empty string? or fix above if to return Qnil?
|
110
|
+
return str;
|
111
|
+
}
|
112
|
+
|
113
|
+
/**
|
114
|
+
Writes data to the connection. Returns `false` on error and `self` on success.
|
115
|
+
*/
|
116
|
+
static VALUE dyn_write(VALUE self, VALUE data) {
|
117
|
+
intptr_t fd = iodine_get_fd(self);
|
118
|
+
if (sock_write(fd, RSTRING_PTR(data), RSTRING_LEN(data)))
|
119
|
+
return Qfalse;
|
120
|
+
return self;
|
121
|
+
}
|
122
|
+
|
123
|
+
/**
|
124
|
+
Writes data to the connection. The data will be sent as soon as possible without
|
125
|
+
fragmantation of previously scheduled data.
|
126
|
+
|
127
|
+
Returns `false` on error and `self` on success.
|
128
|
+
*/
|
129
|
+
static VALUE dyn_write_urgent(VALUE self, VALUE data) {
|
130
|
+
intptr_t fd = iodine_get_fd(self);
|
131
|
+
if (sock_write2(.fduuid = fd, .buffer = RSTRING(data),
|
132
|
+
.length = RSTRING_LEN(data), .urgent = 1))
|
133
|
+
return Qfalse;
|
134
|
+
return self;
|
135
|
+
}
|
136
|
+
|
137
|
+
/**
|
138
|
+
Closes a connection.
|
139
|
+
|
140
|
+
The connection will be closed only once all the data was sent.
|
141
|
+
|
142
|
+
Returns self.
|
143
|
+
*/
|
144
|
+
static VALUE dyn_close(VALUE self) {
|
145
|
+
intptr_t fd = iodine_get_fd(self);
|
146
|
+
sock_close(fd);
|
147
|
+
return self;
|
148
|
+
}
|
149
|
+
|
150
|
+
/* *****************************************************************************
|
151
|
+
The Core dynamic Iodine protocol task implementation
|
152
|
+
*/
|
153
|
+
|
154
|
+
static void dyn_perform_defer(intptr_t uuid, protocol_s *protocol, void *arg) {
|
155
|
+
RubyCaller.call((VALUE)arg, call_proc_id);
|
156
|
+
Registry.remove((VALUE)arg);
|
157
|
+
}
|
158
|
+
static void dyn_defer_fallback(intptr_t uuid, void *arg) {
|
159
|
+
Registry.remove((VALUE)arg);
|
160
|
+
};
|
161
|
+
|
162
|
+
/**
|
163
|
+
Runs the required block later (defers the blocks execution).
|
164
|
+
|
165
|
+
Unlike {Iodine#run}, the block will **not* run concurrently with any other
|
166
|
+
callback
|
167
|
+
for this object (except `ping` and `on_ready`).
|
168
|
+
|
169
|
+
Also, unlike {Iodine#run}, the block will **not** be called unless the
|
170
|
+
connection remains open at the time it's execution is scheduled.
|
171
|
+
|
172
|
+
Always returns `self`.
|
173
|
+
*/
|
174
|
+
static VALUE dyn_defer(VALUE self) {
|
175
|
+
// requires a block to be passed
|
176
|
+
rb_need_block();
|
177
|
+
VALUE block = rb_block_proc();
|
178
|
+
if (block == Qnil)
|
179
|
+
return Qfalse;
|
180
|
+
Registry.add(block);
|
181
|
+
intptr_t fd = iodine_get_fd(self);
|
182
|
+
server_task(fd, dyn_perform_defer, (void *)block, dyn_defer_fallback);
|
183
|
+
return self;
|
184
|
+
}
|
185
|
+
|
186
|
+
static void dyn_perform_each_task(intptr_t fd, protocol_s *protocol,
|
187
|
+
void *data) {
|
188
|
+
RubyCaller.call2((VALUE)data, call_proc_id, 1,
|
189
|
+
&(dyn_prot(protocol)->handler));
|
190
|
+
}
|
191
|
+
static void dyn_finish_each_task(intptr_t fd, protocol_s *protocol,
|
192
|
+
void *data) {
|
193
|
+
Registry.remove((VALUE)data);
|
194
|
+
}
|
195
|
+
|
196
|
+
void iodine_run_each(intptr_t origin, const char *service, VALUE block) {
|
197
|
+
server_each(origin, service, dyn_perform_each_task, (void *)block,
|
198
|
+
dyn_finish_each_task);
|
199
|
+
}
|
200
|
+
|
201
|
+
/**
|
202
|
+
Runs the required block for each dynamic protocol connection except this one.
|
203
|
+
|
204
|
+
Tasks will be performed within each connections lock, so no connection will have
|
205
|
+
more then one task being performed at the same time (similar to {#defer}).
|
206
|
+
|
207
|
+
Also, unlike {Iodine.run}, the block will **not** be called unless the
|
208
|
+
connection remains open at the time it's execution is scheduled.
|
209
|
+
|
210
|
+
Always returns `self`.
|
211
|
+
*/
|
212
|
+
static VALUE dyn_each(VALUE self) {
|
213
|
+
// requires a block to be passed
|
214
|
+
rb_need_block();
|
215
|
+
VALUE block = rb_block_proc();
|
216
|
+
if (block == Qnil)
|
217
|
+
return Qfalse;
|
218
|
+
Registry.add(block);
|
219
|
+
intptr_t fd = iodine_get_fd(self);
|
220
|
+
server_each(fd, iodine_protocol_service, dyn_perform_each_task, (void *)block,
|
221
|
+
dyn_finish_each_task);
|
222
|
+
return self;
|
223
|
+
}
|
224
|
+
|
225
|
+
/**
|
226
|
+
Runs the required block for each dynamic protocol connection.
|
227
|
+
|
228
|
+
Tasks will be performed within each connections lock, so no connection will have
|
229
|
+
more then one task being performed at the same time (similar to {#defer}).
|
230
|
+
|
231
|
+
Also, unlike {Iodine.run}, the block will **not** be called unless the
|
232
|
+
connection remains open at the time it's execution is scheduled.
|
233
|
+
|
234
|
+
Always returns `self`.
|
235
|
+
*/
|
236
|
+
static VALUE dyn_class_each(VALUE self) {
|
237
|
+
// requires a block to be passed
|
238
|
+
rb_need_block();
|
239
|
+
VALUE block = rb_block_proc();
|
240
|
+
if (block == Qnil)
|
241
|
+
return Qfalse;
|
242
|
+
Registry.add(block);
|
243
|
+
server_each(-1, iodine_protocol_service, dyn_perform_each_task, (void *)block,
|
244
|
+
dyn_finish_each_task);
|
245
|
+
return self;
|
246
|
+
}
|
247
|
+
|
248
|
+
/* will be defined in detail later, after some more functions were written */
|
249
|
+
VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler);
|
250
|
+
|
251
|
+
VALUE dyn_upgrade(VALUE self, VALUE handler) {
|
252
|
+
return iodine_upgrade2basic(iodine_get_fd(self), handler);
|
253
|
+
}
|
254
|
+
|
255
|
+
/* *****************************************************************************
|
256
|
+
The Core dynamic Iodine protocol bridge
|
257
|
+
*/
|
258
|
+
|
259
|
+
/** Implement this callback to handle the event. The default implementation will
|
260
|
+
* close the connection. */
|
261
|
+
static VALUE not_implemented_ping(VALUE self) {
|
262
|
+
sock_close(iodine_get_fd(self));
|
263
|
+
return Qnil;
|
264
|
+
}
|
265
|
+
/** implement this callback to handle the event. */
|
266
|
+
static VALUE not_implemented(VALUE self) { return Qnil; }
|
267
|
+
/** implement this callback to handle the event. */
|
268
|
+
static VALUE not_implemented2(VALUE self, VALUE data) { return Qnil; }
|
269
|
+
|
270
|
+
/**
|
271
|
+
A default on_data implementation will read up to 1Kb into a reusable buffer from
|
272
|
+
the socket and call the `on_message` callback.
|
273
|
+
|
274
|
+
It is recommended that you implement this callback if messages might require
|
275
|
+
more then 1Kb of space.
|
276
|
+
*/
|
277
|
+
static VALUE default_on_data(VALUE self) {
|
278
|
+
VALUE buff = rb_ivar_get(self, buff_var_id);
|
279
|
+
if (buff == Qnil) {
|
280
|
+
rb_ivar_set(self, buff_var_id, (buff = rb_str_buf_new(1024)));
|
281
|
+
}
|
282
|
+
do {
|
283
|
+
dyn_read(1, &buff, self);
|
284
|
+
if (!RSTRING_LEN(buff))
|
285
|
+
return Qnil;
|
286
|
+
rb_funcall(self, on_message_func_id, 1, buff);
|
287
|
+
} while (RSTRING_LEN(buff) == rb_str_capacity(buff));
|
288
|
+
return Qnil;
|
289
|
+
}
|
290
|
+
|
291
|
+
/** called when a data is available, but will not run concurrently */
|
292
|
+
static void dyn_protocol_on_data(intptr_t fduuid, protocol_s *protocol) {
|
293
|
+
RubyCaller.call(dyn_prot(protocol)->handler, on_data_func_id);
|
294
|
+
}
|
295
|
+
/** called when the socket is ready to be written to. */
|
296
|
+
static void dyn_protocol_on_ready(intptr_t fduuid, protocol_s *protocol) {
|
297
|
+
RubyCaller.call(dyn_prot(protocol)->handler, on_ready_func_id);
|
298
|
+
}
|
299
|
+
/** called when the server is shutting down,
|
300
|
+
* but before closing the connection. */
|
301
|
+
static void dyn_protocol_on_shutdown(intptr_t fduuid, protocol_s *protocol) {
|
302
|
+
RubyCaller.call(dyn_prot(protocol)->handler, on_shutdown_func_id);
|
303
|
+
}
|
304
|
+
/** called when the connection was closed, but will not run concurrently */
|
305
|
+
static void dyn_protocol_on_close(protocol_s *protocol) {
|
306
|
+
RubyCaller.call(dyn_prot(protocol)->handler, on_close_func_id);
|
307
|
+
Registry.remove(dyn_prot(protocol)->handler);
|
308
|
+
free(protocol);
|
309
|
+
}
|
310
|
+
/** called when a connection's timeout was reached */
|
311
|
+
static void dyn_protocol_ping(intptr_t fduuid, protocol_s *protocol) {
|
312
|
+
RubyCaller.call(dyn_prot(protocol)->handler, ping_func_id);
|
313
|
+
}
|
314
|
+
|
315
|
+
static inline protocol_s *dyn_set_protocol(intptr_t fduuid, VALUE handler,
|
316
|
+
uint8_t timeout) {
|
317
|
+
Registry.add(handler);
|
318
|
+
iodine_set_fd(handler, fduuid);
|
319
|
+
dyn_protocol_s *protocol = malloc(sizeof(*protocol));
|
320
|
+
if (protocol == NULL) {
|
321
|
+
Registry.remove(handler);
|
322
|
+
return NULL;
|
323
|
+
}
|
324
|
+
server_set_timeout(fduuid, timeout);
|
325
|
+
*protocol = (dyn_protocol_s){
|
326
|
+
.handler = handler,
|
327
|
+
.protocol.on_data = dyn_protocol_on_data,
|
328
|
+
.protocol.on_close = dyn_protocol_on_close,
|
329
|
+
.protocol.on_shutdown = dyn_protocol_on_shutdown,
|
330
|
+
.protocol.on_ready = dyn_protocol_on_ready,
|
331
|
+
.protocol.ping = dyn_protocol_ping,
|
332
|
+
.protocol.service = iodine_protocol_service,
|
333
|
+
};
|
334
|
+
RubyCaller.call(handler, on_open_func_id);
|
335
|
+
return (protocol_s *)protocol;
|
336
|
+
}
|
337
|
+
static protocol_s *on_open_dyn_protocol(intptr_t fduuid, void *udata) {
|
338
|
+
VALUE rb_tout = rb_ivar_get((VALUE)udata, timeout_var_id);
|
339
|
+
uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
340
|
+
VALUE handler = RubyCaller.call((VALUE)udata, new_func_id);
|
341
|
+
if (handler == Qnil)
|
342
|
+
return NULL;
|
343
|
+
return dyn_set_protocol(fduuid, handler, timeout);
|
344
|
+
}
|
345
|
+
|
346
|
+
/** called once, when Iodine starts running. */
|
347
|
+
static void on_server_start_for_handler(void *udata) {
|
348
|
+
RubyCaller.call((VALUE)udata, on_start_func_id);
|
349
|
+
}
|
350
|
+
/** called once, when Iodine stops running. */
|
351
|
+
static void on_server_on_finish_for_handler(void *udata) {
|
352
|
+
RubyCaller.call((VALUE)udata, on_finish_func_id);
|
353
|
+
}
|
354
|
+
|
355
|
+
void Init_DynamicProtocol(void) {
|
356
|
+
/**
|
357
|
+
The Protocol module is included in any object or class that handles an Iodine
|
358
|
+
connection using a custom / dynamic protocol (not the Websockets or HTTP
|
359
|
+
protocols that Iodine supports natively).
|
360
|
+
*/
|
361
|
+
DynamicProtocolClass = rb_define_module_under(IodineBase, "ProtocolClass");
|
362
|
+
rb_define_method(DynamicProtocolClass, "on_start", not_implemented, 0);
|
363
|
+
rb_define_method(DynamicProtocolClass, "on_finish", not_implemented, 0);
|
364
|
+
rb_define_method(DynamicProtocolClass, "each", dyn_class_each, 0);
|
365
|
+
|
366
|
+
DynamicProtocol = rb_define_module_under(Iodine, "Protocol");
|
367
|
+
rb_define_method(DynamicProtocol, "on_open", not_implemented, 0);
|
368
|
+
rb_define_method(DynamicProtocol, "on_close", not_implemented, 0);
|
369
|
+
rb_define_method(DynamicProtocol, "on_message", not_implemented2, 1);
|
370
|
+
rb_define_method(DynamicProtocol, "on_data", default_on_data, 0);
|
371
|
+
rb_define_method(DynamicProtocol, "on_ready", not_implemented, 0);
|
372
|
+
rb_define_method(DynamicProtocol, "on_shutdown", not_implemented, 0);
|
373
|
+
rb_define_method(DynamicProtocol, "ping", not_implemented_ping, 0);
|
374
|
+
|
375
|
+
// helper methods
|
376
|
+
rb_define_method(DynamicProtocol, "read", dyn_read, -1);
|
377
|
+
rb_define_method(DynamicProtocol, "write", dyn_write, 1);
|
378
|
+
rb_define_method(DynamicProtocol, "write_urgent", dyn_write_urgent, 1);
|
379
|
+
rb_define_method(DynamicProtocol, "close", dyn_close, 0);
|
380
|
+
rb_define_method(DynamicProtocol, "defer", dyn_defer, 0);
|
381
|
+
rb_define_method(DynamicProtocol, "each", dyn_each, 0);
|
382
|
+
rb_define_method(DynamicProtocol, "upgrade", dyn_upgrade, 1);
|
383
|
+
}
|
384
|
+
|
385
|
+
/* *****************************************************************************
|
386
|
+
Iodine functions
|
387
|
+
*/
|
388
|
+
|
389
|
+
/** Sets up a listenning socket. Conncetions received at the assigned port will
|
390
|
+
be handled by the assigned handler.
|
391
|
+
|
392
|
+
Multiple services (listenning sockets) can be registered before starting the
|
393
|
+
Iodine event loop. */
|
394
|
+
static VALUE iodine_listen_dyn_protocol(VALUE self, VALUE port, VALUE handler) {
|
395
|
+
// validate that the handler is a class and include the Iodine::Protocol
|
396
|
+
if (TYPE(handler) == T_CLASS) {
|
397
|
+
// include the Protocol module
|
398
|
+
// // do we neet to check?
|
399
|
+
// if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
|
400
|
+
rb_include_module(handler, DynamicProtocol);
|
401
|
+
rb_extend_object(handler, DynamicProtocolClass);
|
402
|
+
} else {
|
403
|
+
rb_raise(rb_eTypeError, "The connection handler MUST be of type Class.");
|
404
|
+
return Qnil;
|
405
|
+
}
|
406
|
+
if (TYPE(port) != T_FIXNUM && TYPE(port) != T_STRING)
|
407
|
+
rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
|
408
|
+
if (TYPE(port) == T_FIXNUM)
|
409
|
+
port = rb_funcall2(port, to_s_method_id, 0, NULL);
|
410
|
+
// listen
|
411
|
+
server_listen(.port = StringValueCStr(port), .udata = (void *)handler,
|
412
|
+
.on_open = on_open_dyn_protocol,
|
413
|
+
.on_start = on_server_start_for_handler,
|
414
|
+
.on_finish = on_server_on_finish_for_handler);
|
415
|
+
return self;
|
416
|
+
}
|
417
|
+
|
418
|
+
VALUE iodine_upgrade2basic(intptr_t fduuid, VALUE handler) {
|
419
|
+
uint8_t timeout;
|
420
|
+
if (TYPE(handler) == T_CLASS) {
|
421
|
+
// get the timeout
|
422
|
+
VALUE rb_tout = rb_ivar_get(handler, timeout_var_id);
|
423
|
+
timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
424
|
+
// include the Protocol module
|
425
|
+
// // do we neet to check?
|
426
|
+
// if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
|
427
|
+
rb_include_module(handler, DynamicProtocol);
|
428
|
+
handler = RubyCaller.call(handler, new_func_id);
|
429
|
+
} else {
|
430
|
+
// include the Protocol module in the object's class
|
431
|
+
VALUE p_class = rb_obj_class(handler);
|
432
|
+
// // do we neet to check?
|
433
|
+
// if (rb_mod_include_p(p_class, rDynProtocol) == Qfalse)
|
434
|
+
rb_include_module(p_class, DynamicProtocol);
|
435
|
+
// get the timeout
|
436
|
+
VALUE rb_tout = rb_ivar_get(p_class, timeout_var_id);
|
437
|
+
if (rb_tout == Qnil)
|
438
|
+
rb_tout = rb_ivar_get(handler, timeout_var_id);
|
439
|
+
timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
440
|
+
}
|
441
|
+
protocol_s *protocol = dyn_set_protocol(fduuid, handler, timeout);
|
442
|
+
if (protocol) {
|
443
|
+
if (server_switch_protocol(fduuid, protocol))
|
444
|
+
dyn_protocol_on_close(protocol);
|
445
|
+
return handler;
|
446
|
+
}
|
447
|
+
return Qfalse;
|
448
|
+
}
|
449
|
+
|
450
|
+
/* *****************************************************************************
|
451
|
+
Iodine Task Management
|
452
|
+
*/
|
453
|
+
|
454
|
+
static void iodine_run_once(void *block) {
|
455
|
+
RubyCaller.call((VALUE)block, call_proc_id);
|
456
|
+
Registry.remove((VALUE)block);
|
457
|
+
}
|
458
|
+
|
459
|
+
static void iodine_run_always(void *block) {
|
460
|
+
RubyCaller.call((VALUE)block, call_proc_id);
|
461
|
+
}
|
462
|
+
|
463
|
+
/**
|
464
|
+
Runs the required block later. The block might run concurrently with the
|
465
|
+
existing code (depending on the amount and availability of worker threads).
|
466
|
+
|
467
|
+
Returns the block object. The block will run only while Iodine is running (run
|
468
|
+
will be delayed until Iodine.start is called, unless Iodine's event loop is
|
469
|
+
active).
|
470
|
+
*/
|
471
|
+
static VALUE iodine_run_async(VALUE self) {
|
472
|
+
// requires a block to be passed
|
473
|
+
rb_need_block();
|
474
|
+
VALUE block = rb_block_proc();
|
475
|
+
if (block == Qnil)
|
476
|
+
return Qfalse;
|
477
|
+
Registry.add(block);
|
478
|
+
if (async_run(iodine_run_once, (void *)block)) {
|
479
|
+
server_run_after(1, iodine_run_once, (void *)block);
|
480
|
+
;
|
481
|
+
}
|
482
|
+
return block;
|
483
|
+
}
|
484
|
+
|
485
|
+
/**
|
486
|
+
Runs the required block after the specified number of milliseconds have passed.
|
487
|
+
Time is counted only once Iodine started running (using {Iodine.start}).
|
488
|
+
|
489
|
+
Always returns a copy of the block object.
|
490
|
+
*/
|
491
|
+
static VALUE iodine_run_after(VALUE self, VALUE milliseconds) {
|
492
|
+
if (TYPE(milliseconds) != T_FIXNUM) {
|
493
|
+
rb_raise(rb_eTypeError, "milliseconds must be a number");
|
494
|
+
return Qnil;
|
495
|
+
}
|
496
|
+
size_t milli = FIX2UINT(milliseconds);
|
497
|
+
// requires a block to be passed
|
498
|
+
rb_need_block();
|
499
|
+
VALUE block = rb_block_proc();
|
500
|
+
if (block == Qnil)
|
501
|
+
return Qfalse;
|
502
|
+
Registry.add(block);
|
503
|
+
server_run_after(milli, iodine_run_once, (void *)block);
|
504
|
+
return block;
|
505
|
+
}
|
506
|
+
/**
|
507
|
+
Runs the required block after the specified number of milliseconds have passed.
|
508
|
+
Time is counted only once Iodine started running (using {Iodine.start}).
|
509
|
+
|
510
|
+
Accepts:
|
511
|
+
|
512
|
+
milliseconds:: the number of milliseconds between event repetitions.
|
513
|
+
|
514
|
+
repetitions:: the number of event repetitions. Defaults to 0 (never ending).
|
515
|
+
|
516
|
+
block:: (required) a block is required, as otherwise there is nothing to
|
517
|
+
perform.
|
518
|
+
|
519
|
+
The event will repeat itself until the number of repetitions had been delpeted.
|
520
|
+
|
521
|
+
Always returns a copy of the block object.
|
522
|
+
*/
|
523
|
+
static VALUE iodine_run_every(int argc, VALUE *argv, VALUE self) {
|
524
|
+
VALUE milliseconds, repetitions, block;
|
525
|
+
|
526
|
+
rb_scan_args(argc, argv, "11&", &milliseconds, &repetitions, &block);
|
527
|
+
|
528
|
+
if (TYPE(milliseconds) != T_FIXNUM) {
|
529
|
+
rb_raise(rb_eTypeError, "milliseconds must be a number.");
|
530
|
+
return Qnil;
|
531
|
+
}
|
532
|
+
if (repetitions != Qnil && TYPE(repetitions) != T_FIXNUM) {
|
533
|
+
rb_raise(rb_eTypeError, "repetitions must be a number or `nil`.");
|
534
|
+
return Qnil;
|
535
|
+
}
|
536
|
+
|
537
|
+
size_t milli = FIX2UINT(milliseconds);
|
538
|
+
size_t repeat = (repetitions == Qnil) ? 0 : FIX2UINT(repetitions);
|
539
|
+
// requires a block to be passed
|
540
|
+
rb_need_block();
|
541
|
+
Registry.add(block);
|
542
|
+
server_run_every(milli, repeat, iodine_run_always, (void *)block,
|
543
|
+
(void (*)(void *))Registry.remove);
|
544
|
+
return block;
|
545
|
+
}
|
546
|
+
|
547
|
+
static VALUE iodine_count(VALUE self) { return ULONG2NUM(server_count(NULL)); }
|
548
|
+
/* *****************************************************************************
|
549
|
+
Running the server
|
550
|
+
*/
|
551
|
+
|
552
|
+
static void *srv_start_no_gvl(void *_) {
|
553
|
+
VALUE rb_th_i = rb_iv_get(Iodine, "@threads");
|
554
|
+
VALUE rb_pr_i = rb_iv_get(Iodine, "@processes");
|
555
|
+
size_t threads = (TYPE(rb_th_i) == T_FIXNUM) ? FIX2ULONG(rb_th_i) : 0;
|
556
|
+
size_t processes = (TYPE(rb_pr_i) == T_FIXNUM) ? FIX2ULONG(rb_pr_i) : 0;
|
557
|
+
server_run(.threads = threads, .processes = processes);
|
558
|
+
return NULL;
|
559
|
+
}
|
560
|
+
|
561
|
+
static void unblck(void *_) { server_stop(); }
|
562
|
+
/**
|
563
|
+
Starts the Iodine event loop. This will hang the thread until an interrupt
|
564
|
+
(`^C`) signal is received.
|
565
|
+
|
566
|
+
Returns the Iodine module.
|
567
|
+
*/
|
568
|
+
static VALUE iodine_start(VALUE self) {
|
569
|
+
if (iodine_http_review() == -1) {
|
570
|
+
perror("Iodine couldn't start HTTP service... port busy? ");
|
571
|
+
return Qnil;
|
572
|
+
}
|
573
|
+
rb_thread_call_without_gvl2(srv_start_no_gvl, (void *)self, unblck, NULL);
|
574
|
+
|
575
|
+
return self;
|
576
|
+
}
|
577
|
+
|
578
|
+
/* *****************************************************************************
|
579
|
+
Initializing the library
|
580
|
+
*/
|
581
|
+
|
582
|
+
////////////////////////////////////////////////////////////////////////
|
583
|
+
// Ruby loads the library and invokes the Init_<lib_name> function...
|
584
|
+
//
|
585
|
+
// Here we connect all the C code to the Ruby interface, completing the bridge
|
586
|
+
// between Lib-Server and Ruby.
|
587
|
+
void Init_iodine(void) {
|
588
|
+
// initialize globally used IDs, for faster access to the Ruby layer.
|
589
|
+
call_proc_id = rb_intern("call");
|
590
|
+
new_func_id = rb_intern("new");
|
591
|
+
on_start_func_id = rb_intern("on_start");
|
592
|
+
on_finish_func_id = rb_intern("on_finish");
|
593
|
+
on_open_func_id = rb_intern("on_open");
|
594
|
+
on_message_func_id = rb_intern("on_message");
|
595
|
+
on_data_func_id = rb_intern("on_data");
|
596
|
+
on_shutdown_func_id = rb_intern("on_shutdown");
|
597
|
+
on_close_func_id = rb_intern("on_close");
|
598
|
+
on_ready_func_id = rb_intern("on_ready");
|
599
|
+
ping_func_id = rb_intern("ping");
|
600
|
+
buff_var_id = rb_intern("scrtbuffer");
|
601
|
+
fd_var_id = rb_intern("scrtfd");
|
602
|
+
timeout_var_id = rb_intern("@timeout");
|
603
|
+
to_s_method_id = rb_intern("to_s");
|
604
|
+
|
605
|
+
BinaryEncodingIndex = rb_enc_find_index("binary"); // sets encoding for data
|
606
|
+
UTF8EncodingIndex = rb_enc_find_index("UTF-8"); // sets encoding for data
|
607
|
+
BinaryEncoding = rb_enc_find("binary"); // sets encoding for data
|
608
|
+
UTF8Encoding = rb_enc_find("UTF-8"); // sets encoding for data
|
609
|
+
|
610
|
+
// The core Iodine module wraps libserver functionality and little more.
|
611
|
+
Iodine = rb_define_module("Iodine");
|
612
|
+
|
613
|
+
// get-set version
|
614
|
+
{
|
615
|
+
Iodine_Version = rb_const_get(Iodine, rb_intern("VERSION"));
|
616
|
+
if (Iodine_Version == Qnil)
|
617
|
+
Iodine_Version_Str = "0.2.0";
|
618
|
+
else
|
619
|
+
Iodine_Version_Str = StringValueCStr(Iodine_Version);
|
620
|
+
}
|
621
|
+
// the Iodine singleton functions
|
622
|
+
rb_define_module_function(Iodine, "listen", iodine_listen_dyn_protocol, 2);
|
623
|
+
rb_define_module_function(Iodine, "start", iodine_start, 0);
|
624
|
+
rb_define_module_function(Iodine, "count", iodine_count, 0);
|
625
|
+
rb_define_module_function(Iodine, "run", iodine_run_async, 0);
|
626
|
+
rb_define_module_function(Iodine, "run_after", iodine_run_after, 1);
|
627
|
+
rb_define_module_function(Iodine, "run_every", iodine_run_every, -1);
|
628
|
+
|
629
|
+
// Every Protocol (and Server?) instance will hold a reference to the server
|
630
|
+
// define the Server Ruby class.
|
631
|
+
IodineBase = rb_define_module_under(Iodine, "Base");
|
632
|
+
|
633
|
+
// Initialize the registry under the Iodine core
|
634
|
+
Registry.init(Iodine);
|
635
|
+
// Initialize the Dynamic Protocol
|
636
|
+
Init_DynamicProtocol();
|
637
|
+
|
638
|
+
// initialize the Http server
|
639
|
+
// must be done only after all the globals, including BinaryEncoding, are set
|
640
|
+
Init_iodine_http();
|
641
|
+
}
|