iodine 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +63 -100
- data/bin/raw-rbhttp +12 -7
- data/examples/config.ru +8 -7
- data/examples/echo.ru +8 -7
- data/examples/info.md +41 -35
- data/examples/pubsub_engine.ru +12 -12
- data/examples/redis.ru +10 -12
- data/examples/shootout.ru +19 -42
- data/exe/iodine +116 -1
- data/ext/iodine/defer.c +1 -1
- data/ext/iodine/facil.c +12 -8
- data/ext/iodine/facil.h +2 -2
- data/ext/iodine/iodine.c +177 -343
- data/ext/iodine/iodine.h +18 -72
- data/ext/iodine/iodine_caller.c +132 -0
- data/ext/iodine/iodine_caller.h +21 -0
- data/ext/iodine/iodine_connection.c +841 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +391 -0
- data/ext/iodine/iodine_defer.h +7 -0
- data/ext/iodine/{rb-fiobj2rb.h → iodine_fiobj2rb.h} +6 -6
- data/ext/iodine/iodine_helpers.c +51 -5
- data/ext/iodine/iodine_helpers.h +2 -3
- data/ext/iodine/iodine_http.c +284 -141
- data/ext/iodine/iodine_http.h +2 -2
- data/ext/iodine/iodine_json.c +13 -13
- data/ext/iodine/iodine_json.h +1 -1
- data/ext/iodine/iodine_pubsub.c +573 -823
- data/ext/iodine/iodine_pubsub.h +15 -27
- data/ext/iodine/{rb-rack-io.c → iodine_rack_io.c} +30 -8
- data/ext/iodine/{rb-rack-io.h → iodine_rack_io.h} +1 -0
- data/ext/iodine/iodine_store.c +136 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +385 -0
- data/ext/iodine/iodine_tcp.h +9 -0
- data/lib/iodine.rb +73 -171
- data/lib/iodine/connection.rb +34 -0
- data/lib/iodine/pubsub.rb +5 -18
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +1 -182
- metadata +17 -18
- data/ext/iodine/iodine_protocol.c +0 -689
- data/ext/iodine/iodine_protocol.h +0 -13
- data/ext/iodine/iodine_websockets.c +0 -550
- data/ext/iodine/iodine_websockets.h +0 -17
- data/ext/iodine/rb-call.c +0 -156
- data/ext/iodine/rb-call.h +0 -70
- data/ext/iodine/rb-defer.c +0 -124
- data/ext/iodine/rb-registry.c +0 -150
- data/ext/iodine/rb-registry.h +0 -34
- data/lib/iodine/cli.rb +0 -89
- data/lib/iodine/monkeypatch.rb +0 -46
- data/lib/iodine/protocol.rb +0 -42
- data/lib/iodine/websocket.rb +0 -16
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iodine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-05-
|
11
|
+
date: 2018-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -214,28 +214,29 @@ files:
|
|
214
214
|
- ext/iodine/http_mime_parser.h
|
215
215
|
- ext/iodine/iodine.c
|
216
216
|
- ext/iodine/iodine.h
|
217
|
+
- ext/iodine/iodine_caller.c
|
218
|
+
- ext/iodine/iodine_caller.h
|
219
|
+
- ext/iodine/iodine_connection.c
|
220
|
+
- ext/iodine/iodine_connection.h
|
221
|
+
- ext/iodine/iodine_defer.c
|
222
|
+
- ext/iodine/iodine_defer.h
|
223
|
+
- ext/iodine/iodine_fiobj2rb.h
|
217
224
|
- ext/iodine/iodine_helpers.c
|
218
225
|
- ext/iodine/iodine_helpers.h
|
219
226
|
- ext/iodine/iodine_http.c
|
220
227
|
- ext/iodine/iodine_http.h
|
221
228
|
- ext/iodine/iodine_json.c
|
222
229
|
- ext/iodine/iodine_json.h
|
223
|
-
- ext/iodine/iodine_protocol.c
|
224
|
-
- ext/iodine/iodine_protocol.h
|
225
230
|
- ext/iodine/iodine_pubsub.c
|
226
231
|
- ext/iodine/iodine_pubsub.h
|
227
|
-
- ext/iodine/
|
228
|
-
- ext/iodine/
|
232
|
+
- ext/iodine/iodine_rack_io.c
|
233
|
+
- ext/iodine/iodine_rack_io.h
|
234
|
+
- ext/iodine/iodine_store.c
|
235
|
+
- ext/iodine/iodine_store.h
|
236
|
+
- ext/iodine/iodine_tcp.c
|
237
|
+
- ext/iodine/iodine_tcp.h
|
229
238
|
- ext/iodine/pubsub.c
|
230
239
|
- ext/iodine/pubsub.h
|
231
|
-
- ext/iodine/rb-call.c
|
232
|
-
- ext/iodine/rb-call.h
|
233
|
-
- ext/iodine/rb-defer.c
|
234
|
-
- ext/iodine/rb-fiobj2rb.h
|
235
|
-
- ext/iodine/rb-rack-io.c
|
236
|
-
- ext/iodine/rb-rack-io.h
|
237
|
-
- ext/iodine/rb-registry.c
|
238
|
-
- ext/iodine/rb-registry.h
|
239
240
|
- ext/iodine/redis_engine.c
|
240
241
|
- ext/iodine/redis_engine.h
|
241
242
|
- ext/iodine/resp_parser.h
|
@@ -247,13 +248,11 @@ files:
|
|
247
248
|
- ext/iodine/websockets.h
|
248
249
|
- iodine.gemspec
|
249
250
|
- lib/iodine.rb
|
250
|
-
- lib/iodine/
|
251
|
+
- lib/iodine/connection.rb
|
251
252
|
- lib/iodine/json.rb
|
252
|
-
- lib/iodine/monkeypatch.rb
|
253
|
-
- lib/iodine/protocol.rb
|
254
253
|
- lib/iodine/pubsub.rb
|
254
|
+
- lib/iodine/rack_utils.rb
|
255
255
|
- lib/iodine/version.rb
|
256
|
-
- lib/iodine/websocket.rb
|
257
256
|
- lib/rack/handler/iodine.rb
|
258
257
|
- logo.png
|
259
258
|
homepage: https://github.com/boazsegev/iodine
|
@@ -1,689 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
Copyright: Boaz segev, 2016-2017
|
3
|
-
License: MIT
|
4
|
-
|
5
|
-
Feel free to copy, use and enjoy according to the license provided.
|
6
|
-
*/
|
7
|
-
#include "iodine_protocol.h"
|
8
|
-
#include "iodine_pubsub.h"
|
9
|
-
|
10
|
-
#include "fio_llist.h"
|
11
|
-
#include "fiobj4sock.h"
|
12
|
-
#include "pubsub.h"
|
13
|
-
|
14
|
-
#include <ruby/io.h>
|
15
|
-
|
16
|
-
static ID iodine_close_func_id;
|
17
|
-
|
18
|
-
/* *****************************************************************************
|
19
|
-
The protocol object
|
20
|
-
***************************************************************************** */
|
21
|
-
|
22
|
-
typedef struct {
|
23
|
-
protocol_s protocol;
|
24
|
-
VALUE handler;
|
25
|
-
fio_ls_s subscriptions;
|
26
|
-
} iodine_protocol_s;
|
27
|
-
|
28
|
-
VALUE IodineProtocol;
|
29
|
-
|
30
|
-
static char *iodine_protocol_service = "Iodine Custom Protocol";
|
31
|
-
|
32
|
-
/* *****************************************************************************
|
33
|
-
Internal helpers
|
34
|
-
***************************************************************************** */
|
35
|
-
|
36
|
-
static void iodine_clear_task(intptr_t origin, void *block_) {
|
37
|
-
Registry.remove((VALUE)block_);
|
38
|
-
(void)origin;
|
39
|
-
}
|
40
|
-
|
41
|
-
// static void iodine_perform_task(intptr_t uuid, protocol_s *pr, void *block_)
|
42
|
-
// {
|
43
|
-
// if (pr->service == iodine_protocol_service) {
|
44
|
-
// RubyCaller.call2((VALUE)block_, iodine_call_proc_id, 1, (VALUE *)(pr +
|
45
|
-
// 1));
|
46
|
-
// }
|
47
|
-
// (void)uuid;
|
48
|
-
// }
|
49
|
-
|
50
|
-
static void iodine_perform_task_and_free(intptr_t uuid, protocol_s *pr,
|
51
|
-
void *block_) {
|
52
|
-
if (pr->service == iodine_protocol_service) {
|
53
|
-
RubyCaller.call2((VALUE)block_, iodine_call_proc_id, 1, (VALUE *)(pr + 1));
|
54
|
-
}
|
55
|
-
Registry.remove((VALUE)block_);
|
56
|
-
(void)uuid;
|
57
|
-
}
|
58
|
-
|
59
|
-
static void remove_from_registry(intptr_t uuid, void *val) {
|
60
|
-
Registry.remove((VALUE)val);
|
61
|
-
(void)uuid;
|
62
|
-
}
|
63
|
-
/* *****************************************************************************
|
64
|
-
Function placeholders
|
65
|
-
***************************************************************************** */
|
66
|
-
|
67
|
-
/** Override this callback to handle the event. The default implementation will
|
68
|
-
* close the connection. */
|
69
|
-
static VALUE not_implemented_ping(VALUE self) {
|
70
|
-
sock_close(iodine_get_fd(self));
|
71
|
-
return Qnil;
|
72
|
-
}
|
73
|
-
/** Override this callback to handle the event. */
|
74
|
-
static VALUE not_implemented(VALUE self) {
|
75
|
-
(void)(self);
|
76
|
-
return Qnil;
|
77
|
-
}
|
78
|
-
|
79
|
-
/** DEPRECATED! Please override {on_drained} instead. */
|
80
|
-
static VALUE not_implemented_on_ready(VALUE self, VALUE data) {
|
81
|
-
(void)(self);
|
82
|
-
(void)(data);
|
83
|
-
return Qnil;
|
84
|
-
}
|
85
|
-
|
86
|
-
/** Override this callback to handle the event. */
|
87
|
-
static VALUE not_implemented_drained(VALUE self) {
|
88
|
-
RubyCaller.call(self, rb_intern2("on_ready", 8));
|
89
|
-
(void)(self);
|
90
|
-
return Qnil;
|
91
|
-
}
|
92
|
-
|
93
|
-
/** Override this callback to handle the event. */
|
94
|
-
static VALUE not_implemented2(VALUE self, VALUE data) {
|
95
|
-
(void)(self);
|
96
|
-
(void)(data);
|
97
|
-
return Qnil;
|
98
|
-
}
|
99
|
-
|
100
|
-
static VALUE dyn_read(int argc, VALUE *argv, VALUE self);
|
101
|
-
/**
|
102
|
-
A default on_data implementation will read up to 1Kb into a reusable buffer from
|
103
|
-
the socket and call the `on_message` callback.
|
104
|
-
|
105
|
-
It is recommended that you implement this callback if messages might require
|
106
|
-
more then 1Kb of space.
|
107
|
-
*/
|
108
|
-
static VALUE default_on_data(VALUE self) {
|
109
|
-
VALUE buff = rb_ivar_get(self, iodine_buff_var_id);
|
110
|
-
if (buff == Qnil) {
|
111
|
-
rb_ivar_set(self, iodine_buff_var_id, (buff = rb_str_buf_new(1024)));
|
112
|
-
}
|
113
|
-
do {
|
114
|
-
dyn_read(1, &buff, self);
|
115
|
-
if (!RSTRING_LEN(buff))
|
116
|
-
return Qnil;
|
117
|
-
rb_funcall(self, iodine_on_message_func_id, 1, buff);
|
118
|
-
} while (RSTRING_LEN(buff) == (ssize_t)rb_str_capacity(buff));
|
119
|
-
return Qnil;
|
120
|
-
}
|
121
|
-
|
122
|
-
/* *****************************************************************************
|
123
|
-
Pub/Sub functions
|
124
|
-
***************************************************************************** */
|
125
|
-
|
126
|
-
// clang-format off
|
127
|
-
/**
|
128
|
-
Subscribes the connection to a Pub/Sub channel.
|
129
|
-
|
130
|
-
Since this connection's data packaging is unknown, a block (or handler) is required to handle pub/sub events.
|
131
|
-
|
132
|
-
The method accepts 1-2 arguments and an optional block. These are all valid ways
|
133
|
-
to call the method:
|
134
|
-
|
135
|
-
subscribe("my_stream") {|from, msg| p msg }
|
136
|
-
subscribe("my_stream", match: :redis) {|from, msg| p msg }
|
137
|
-
subscribe(to: "my_stream") {|from, msg| p msg }
|
138
|
-
subscribe to: "my_stream", match: :redis, handler: MyProc
|
139
|
-
|
140
|
-
The first argument must be either a String or a Hash.
|
141
|
-
|
142
|
-
The second, optional, argument must be a Hash (if given).
|
143
|
-
|
144
|
-
The options Hash supports the following possible keys (other keys are ignored, all keys are Symbols):
|
145
|
-
|
146
|
-
:match :: The channel / subject name matching type to be used. Valid value is: `:redis`. Future versions hope to support `:nats` and `:rabbit` patern matching as well.
|
147
|
-
|
148
|
-
:to :: The channel / subject to subscribe to.
|
149
|
-
|
150
|
-
:handler :: and Proc like object, must answer to `call(from, msg)`.
|
151
|
-
|
152
|
-
Returns an {Iodine::PubSub::Subscription} object that answers to:
|
153
|
-
|
154
|
-
close :: closes the connection.
|
155
|
-
to_s :: returns the subscription's target (stream / channel / subject).
|
156
|
-
==(str) :: returns true if the string is an exact match for the target (even if the target itself is a pattern).
|
157
|
-
|
158
|
-
*/
|
159
|
-
static VALUE iodine_proto_subscribe(int argc, VALUE *argv, VALUE self) {
|
160
|
-
// clang-format on
|
161
|
-
intptr_t uuid = iodine_get_fd(self);
|
162
|
-
if (!uuid || (VALUE)uuid == Qnil || uuid < 0)
|
163
|
-
return Qfalse;
|
164
|
-
VALUE sub = iodine_subscribe(argc, argv, NULL, IODINE_PUBSUB_GLOBAL);
|
165
|
-
if (sub == Qnil || sub == Qfalse)
|
166
|
-
return Qfalse;
|
167
|
-
Registry.add(sub);
|
168
|
-
|
169
|
-
iodine_protocol_s *pr = iodine_get_cdata(self);
|
170
|
-
|
171
|
-
fio_ls_push(&pr->subscriptions, (void *)sub);
|
172
|
-
return sub;
|
173
|
-
}
|
174
|
-
|
175
|
-
/* *****************************************************************************
|
176
|
-
Published functions
|
177
|
-
***************************************************************************** */
|
178
|
-
|
179
|
-
/**
|
180
|
-
Reads up to `n` bytes from the network connection.
|
181
|
-
The number of bytes to be read (n) is:
|
182
|
-
- the number of bytes set in the optional `buffer_or_length` argument.
|
183
|
-
- the String capacity (not length) of the String passed as the optional
|
184
|
-
`buffer_or_length` argument.
|
185
|
-
- 1024 Bytes (1Kb) if the optional `buffer_or_length` is either missing or
|
186
|
-
contains a String with a capacity less then 1Kb.
|
187
|
-
Returns a String (either the same one used as the buffer or a new one) on a
|
188
|
-
successful read.
|
189
|
-
Returns `nil` if no data was available.
|
190
|
-
*/
|
191
|
-
static VALUE dyn_read(int argc, VALUE *argv, VALUE self) {
|
192
|
-
if (argc > 1) {
|
193
|
-
rb_raise(
|
194
|
-
rb_eArgError,
|
195
|
-
"read accepts only one argument - a Fixnum (buffer length) or a String "
|
196
|
-
"(it's capacity - or 1Kb, whichever's the higher - will be used as "
|
197
|
-
"buffer's length).");
|
198
|
-
return Qnil;
|
199
|
-
}
|
200
|
-
VALUE buffer = (argc == 1 ? argv[0] : Qnil);
|
201
|
-
if (buffer != Qnil && TYPE(buffer) != T_FIXNUM && TYPE(buffer) != T_STRING) {
|
202
|
-
rb_raise(rb_eTypeError,
|
203
|
-
"buffer should either be a length (a new string will be created) "
|
204
|
-
"or a string (reading will be limited to the original string's "
|
205
|
-
"capacity or 1Kb - whichever the larger).");
|
206
|
-
return Qnil;
|
207
|
-
}
|
208
|
-
VALUE str;
|
209
|
-
long len;
|
210
|
-
intptr_t fd = iodine_get_fd(self);
|
211
|
-
if (buffer == Qnil) {
|
212
|
-
buffer = LONG2FIX(1024);
|
213
|
-
}
|
214
|
-
if (TYPE(buffer) == T_FIXNUM) {
|
215
|
-
len = FIX2LONG(buffer);
|
216
|
-
if (len <= 0)
|
217
|
-
len = 1024;
|
218
|
-
str = rb_str_buf_new(len);
|
219
|
-
// create a rb_String with X length and take it's pointer
|
220
|
-
// rb_str_resize(VALUE str, long len)
|
221
|
-
// RSTRING_PTR(str)
|
222
|
-
} else {
|
223
|
-
// take the string's pointer and length
|
224
|
-
len = rb_str_capacity(buffer);
|
225
|
-
// make sure the string is modifiable
|
226
|
-
rb_str_modify(buffer);
|
227
|
-
// resize the string if needed.
|
228
|
-
if (len < 1024)
|
229
|
-
rb_str_resize(buffer, (len = 1024));
|
230
|
-
str = buffer;
|
231
|
-
}
|
232
|
-
ssize_t in = sock_read(fd, RSTRING_PTR(str), len);
|
233
|
-
// make sure it's binary encoded
|
234
|
-
rb_enc_associate_index(str, IodineBinaryEncodingIndex);
|
235
|
-
// set actual size....
|
236
|
-
if (in > 0)
|
237
|
-
rb_str_set_len(str, (long)in);
|
238
|
-
else {
|
239
|
-
rb_str_set_len(str, 0);
|
240
|
-
str = Qnil;
|
241
|
-
}
|
242
|
-
// return empty string? or fix above if to return Qnil?
|
243
|
-
return str;
|
244
|
-
}
|
245
|
-
|
246
|
-
/**
|
247
|
-
Writes data to the connection. Returns `false` on error and `self` on success.
|
248
|
-
*/
|
249
|
-
static VALUE dyn_write(VALUE self, VALUE data) {
|
250
|
-
Check_Type(data, T_STRING);
|
251
|
-
intptr_t fd = iodine_get_fd(self);
|
252
|
-
if (sock_write(fd, RSTRING_PTR(data), RSTRING_LEN(data))) {
|
253
|
-
return Qfalse;
|
254
|
-
}
|
255
|
-
return self;
|
256
|
-
}
|
257
|
-
|
258
|
-
/**
|
259
|
-
Moves a String to iodine's socket's buffer. This is a zero-copy write and
|
260
|
-
requires that the string remain unchanged during the process.
|
261
|
-
|
262
|
-
For example, Strings received by `on_message` can't be used, because they use a
|
263
|
-
recyclable buffer and they will be destroyed once `on_message` returns.
|
264
|
-
*/
|
265
|
-
static VALUE dyn_write_move(VALUE self, VALUE data) {
|
266
|
-
Check_Type(data, T_STRING);
|
267
|
-
Registry.add(data);
|
268
|
-
intptr_t fd = iodine_get_fd(self);
|
269
|
-
if (sock_write2(.uuid = fd, .buffer = RSTRING_PTR(data),
|
270
|
-
.length = RSTRING_LEN(data),
|
271
|
-
.dealloc = (void (*)(void *))Registry.remove))
|
272
|
-
return Qfalse;
|
273
|
-
return self;
|
274
|
-
}
|
275
|
-
|
276
|
-
/**
|
277
|
-
Writes data to the connection. The data will be sent as soon as possible without
|
278
|
-
fragmantation of previously scheduled data.
|
279
|
-
|
280
|
-
Returns `false` on error and `self` on success.
|
281
|
-
*/
|
282
|
-
static VALUE dyn_write_urgent(VALUE self, VALUE data) {
|
283
|
-
Check_Type(data, T_STRING);
|
284
|
-
intptr_t fd = iodine_get_fd(self);
|
285
|
-
Registry.add(data);
|
286
|
-
if (sock_write(fd, RSTRING_PTR(data), RSTRING_LEN(data))) {
|
287
|
-
return Qfalse;
|
288
|
-
}
|
289
|
-
return self;
|
290
|
-
}
|
291
|
-
|
292
|
-
/**
|
293
|
-
Update's a connection's timeout.
|
294
|
-
|
295
|
-
Returns self.
|
296
|
-
*/
|
297
|
-
static VALUE dyn_set_timeout(VALUE self, VALUE timeout) {
|
298
|
-
intptr_t fd = iodine_get_fd(self);
|
299
|
-
unsigned int tout = FIX2UINT(timeout);
|
300
|
-
if (tout > 255)
|
301
|
-
tout = 255;
|
302
|
-
facil_set_timeout(fd, tout);
|
303
|
-
return self;
|
304
|
-
}
|
305
|
-
|
306
|
-
/**
|
307
|
-
Returns the connection's timeout.
|
308
|
-
*/
|
309
|
-
static VALUE dyn_get_timeout(VALUE self) {
|
310
|
-
intptr_t fd = iodine_get_fd(self);
|
311
|
-
uint8_t tout = facil_get_timeout(fd);
|
312
|
-
unsigned int tout_int = tout;
|
313
|
-
return UINT2NUM(tout_int);
|
314
|
-
}
|
315
|
-
|
316
|
-
/**
|
317
|
-
Closes a connection.
|
318
|
-
|
319
|
-
The connection will be closed only once all the data was sent.
|
320
|
-
|
321
|
-
Returns self.
|
322
|
-
*/
|
323
|
-
static VALUE dyn_close(VALUE self) {
|
324
|
-
intptr_t fd = iodine_get_fd(self);
|
325
|
-
sock_close(fd);
|
326
|
-
return self;
|
327
|
-
}
|
328
|
-
|
329
|
-
/**
|
330
|
-
Returns a connection's localized ID which is valid for *this process* (not a
|
331
|
-
machine or internet unique value).
|
332
|
-
|
333
|
-
Once the connection is closed and the `on_close` callback was called, this
|
334
|
-
method returns `nil`.
|
335
|
-
|
336
|
-
This can be used together with a true process wide UUID to uniquely identify a
|
337
|
-
connection across the internet.
|
338
|
-
*/
|
339
|
-
static VALUE dyn_uuid(VALUE self) {
|
340
|
-
intptr_t uuid = iodine_get_fd(self);
|
341
|
-
if (!uuid || uuid == -1)
|
342
|
-
return Qnil;
|
343
|
-
return LONG2FIX(uuid);
|
344
|
-
}
|
345
|
-
|
346
|
-
/**
|
347
|
-
Returns true if the connection is open and false if closed.
|
348
|
-
*/
|
349
|
-
static VALUE dyn_is_open(VALUE self) {
|
350
|
-
intptr_t uuid = iodine_get_fd(self);
|
351
|
-
if (uuid && sock_isvalid(uuid))
|
352
|
-
return Qtrue;
|
353
|
-
return Qfalse;
|
354
|
-
}
|
355
|
-
|
356
|
-
/**
|
357
|
-
Schedules a block to execute (defers the blocks execution).
|
358
|
-
|
359
|
-
When this function is called by a Protocol instance, a lock on the connection
|
360
|
-
will be used to prevent multiple tasks / callbacks from running concurrently.
|
361
|
-
i.e.
|
362
|
-
|
363
|
-
defer { write "this will run in a lock" }
|
364
|
-
|
365
|
-
Otherwise, the deferred task will run acconrding to the requested concurrency
|
366
|
-
model.
|
367
|
-
|
368
|
-
Iodine.defer { puts "this will run concurrently" }
|
369
|
-
Iodine.run { puts "this will run concurrently" }
|
370
|
-
|
371
|
-
Tasks scheduled before calling {Iodine.start} will run once for every process.
|
372
|
-
|
373
|
-
Returns the block given (or `false`).
|
374
|
-
|
375
|
-
**Notice**: There's a possibility that the rask will never be called if it was
|
376
|
-
associated with a specific connection (the method was called as an instance
|
377
|
-
method) and the connection was closed before the deferred task was performed.
|
378
|
-
*/
|
379
|
-
static VALUE dyn_defer(int argc, VALUE *argv, VALUE self) {
|
380
|
-
rb_need_block();
|
381
|
-
intptr_t fd;
|
382
|
-
// check arguments.
|
383
|
-
if (argc > 1)
|
384
|
-
rb_raise(rb_eArgError, "this function expects no more then 1 (optional) "
|
385
|
-
"argument.");
|
386
|
-
else if (argc == 1) {
|
387
|
-
Check_Type(*argv, T_FIXNUM);
|
388
|
-
fd = FIX2LONG(*argv);
|
389
|
-
} else
|
390
|
-
fd = iodine_get_fd(self);
|
391
|
-
|
392
|
-
if (!sock_isvalid(fd))
|
393
|
-
return Qfalse;
|
394
|
-
|
395
|
-
VALUE block = rb_block_proc();
|
396
|
-
if (block == Qnil)
|
397
|
-
return Qfalse;
|
398
|
-
Registry.add(block);
|
399
|
-
facil_defer(.uuid = fd, .task = iodine_perform_task_and_free,
|
400
|
-
.type = FIO_PR_LOCK_TASK, .arg = (void *)block,
|
401
|
-
.fallback = iodine_clear_task);
|
402
|
-
return block;
|
403
|
-
}
|
404
|
-
|
405
|
-
/* *****************************************************************************
|
406
|
-
Connection management
|
407
|
-
***************************************************************************** */
|
408
|
-
#define dyn_prot(protocol) ((iodine_protocol_s *)(protocol))
|
409
|
-
|
410
|
-
/** called when a data is available, but will not run concurrently */
|
411
|
-
static void dyn_protocol_on_data(intptr_t fduuid, protocol_s *protocol) {
|
412
|
-
(void)(fduuid);
|
413
|
-
RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_data_func_id);
|
414
|
-
}
|
415
|
-
/** called when the socket is ready to be written to. */
|
416
|
-
static void dyn_protocol_on_ready(intptr_t fduuid, protocol_s *protocol) {
|
417
|
-
(void)(fduuid);
|
418
|
-
RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_drained_func_id);
|
419
|
-
}
|
420
|
-
/** called when the server is shutting down,
|
421
|
-
* but before closing the connection. */
|
422
|
-
static void dyn_protocol_on_shutdown(intptr_t fduuid, protocol_s *protocol) {
|
423
|
-
(void)(fduuid);
|
424
|
-
RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_shutdown_func_id);
|
425
|
-
}
|
426
|
-
|
427
|
-
static void *clear_subscriptions_inGVL(void *pr_) {
|
428
|
-
iodine_protocol_s *pr = pr_;
|
429
|
-
while (fio_ls_any(&pr->subscriptions)) {
|
430
|
-
RubyCaller.call((VALUE)fio_ls_pop(&pr->subscriptions),
|
431
|
-
iodine_close_func_id);
|
432
|
-
}
|
433
|
-
return NULL;
|
434
|
-
}
|
435
|
-
|
436
|
-
/** called when the connection was closed, but will not run concurrently */
|
437
|
-
static void dyn_protocol_on_close(intptr_t uuid, protocol_s *protocol) {
|
438
|
-
RubyCaller.call(dyn_prot(protocol)->handler, iodine_on_close_func_id);
|
439
|
-
iodine_set_fd(dyn_prot(protocol)->handler, 0);
|
440
|
-
iodine_set_cdata(dyn_prot(protocol)->handler, NULL);
|
441
|
-
Registry.remove(dyn_prot(protocol)->handler);
|
442
|
-
RubyCaller.call_c(clear_subscriptions_inGVL, protocol);
|
443
|
-
free(protocol);
|
444
|
-
(void)uuid;
|
445
|
-
}
|
446
|
-
/** called when a connection's timeout was reached */
|
447
|
-
static void dyn_protocol_ping(intptr_t fduuid, protocol_s *protocol) {
|
448
|
-
(void)(fduuid);
|
449
|
-
RubyCaller.call(dyn_prot(protocol)->handler, iodine_ping_func_id);
|
450
|
-
}
|
451
|
-
|
452
|
-
/* *****************************************************************************
|
453
|
-
Connection management API
|
454
|
-
*****************************************************************************
|
455
|
-
*/
|
456
|
-
|
457
|
-
/** Update's a connection's handler and timeout. */
|
458
|
-
static inline protocol_s *dyn_set_protocol(intptr_t fduuid, VALUE handler,
|
459
|
-
uint8_t timeout) {
|
460
|
-
Registry.add(handler);
|
461
|
-
iodine_protocol_s *protocol = malloc(sizeof(*protocol));
|
462
|
-
if (protocol == NULL) {
|
463
|
-
Registry.remove(handler);
|
464
|
-
return NULL;
|
465
|
-
}
|
466
|
-
facil_set_timeout(fduuid, timeout);
|
467
|
-
*protocol = (iodine_protocol_s){
|
468
|
-
.protocol.on_data = dyn_protocol_on_data,
|
469
|
-
.protocol.on_close = dyn_protocol_on_close,
|
470
|
-
.protocol.on_shutdown = dyn_protocol_on_shutdown,
|
471
|
-
.protocol.on_ready = dyn_protocol_on_ready,
|
472
|
-
.protocol.ping = dyn_protocol_ping,
|
473
|
-
.protocol.service = iodine_protocol_service,
|
474
|
-
.handler = handler,
|
475
|
-
.subscriptions = FIO_LS_INIT(protocol->subscriptions),
|
476
|
-
};
|
477
|
-
iodine_set_fd(handler, fduuid);
|
478
|
-
iodine_set_cdata(handler, protocol);
|
479
|
-
RubyCaller.call(handler, iodine_on_open_func_id);
|
480
|
-
return &protocol->protocol;
|
481
|
-
}
|
482
|
-
|
483
|
-
static void on_open_dyn_protocol(intptr_t fduuid, void *udata) {
|
484
|
-
VALUE rb_tout = rb_ivar_get((VALUE)udata, iodine_timeout_var_id);
|
485
|
-
uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
486
|
-
VALUE handler = RubyCaller.call((VALUE)udata, iodine_new_func_id);
|
487
|
-
if (handler == Qnil) {
|
488
|
-
sock_close(fduuid);
|
489
|
-
return;
|
490
|
-
}
|
491
|
-
facil_attach(fduuid, dyn_set_protocol(fduuid, handler, timeout));
|
492
|
-
}
|
493
|
-
|
494
|
-
/** Sets up a listening socket. Conncetions received at the assigned port will
|
495
|
-
be handled by the assigned handler.
|
496
|
-
|
497
|
-
Multiple services (listening sockets) can be registered before starting the
|
498
|
-
Iodine event loop. */
|
499
|
-
static VALUE iodine_listen(VALUE self, VALUE port, VALUE handler) {
|
500
|
-
// validate that the handler is a class and include the Iodine::Protocol
|
501
|
-
if (TYPE(handler) == T_CLASS) {
|
502
|
-
// include the Protocol module
|
503
|
-
// // do we neet to check?
|
504
|
-
// if (rb_mod_include_p(protocol, rDynProtocol) == Qfalse)
|
505
|
-
rb_include_module(handler, IodineProtocol);
|
506
|
-
rb_extend_object(handler, IodineProtocol);
|
507
|
-
} else {
|
508
|
-
rb_raise(rb_eTypeError, "The connection handler MUST be of type Class.");
|
509
|
-
return Qnil;
|
510
|
-
}
|
511
|
-
if (TYPE(port) != T_FIXNUM && TYPE(port) != T_STRING)
|
512
|
-
rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
|
513
|
-
if (TYPE(port) == T_FIXNUM)
|
514
|
-
port = rb_funcall2(port, iodine_to_s_method_id, 0, NULL);
|
515
|
-
rb_ivar_set(self, rb_intern("_port"), port);
|
516
|
-
// listen
|
517
|
-
if (facil_listen(.port = StringValueCStr(port), .udata = (void *)handler,
|
518
|
-
.on_open = on_open_dyn_protocol) == -1)
|
519
|
-
return Qnil;
|
520
|
-
return self;
|
521
|
-
}
|
522
|
-
|
523
|
-
VALUE dyn_switch_prot(VALUE self, VALUE handler) {
|
524
|
-
uint8_t timeout;
|
525
|
-
intptr_t fd = iodine_get_fd(self);
|
526
|
-
if (TYPE(handler) == T_CLASS) {
|
527
|
-
// get the timeout
|
528
|
-
VALUE rb_tout = rb_ivar_get(handler, iodine_timeout_var_id);
|
529
|
-
timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
530
|
-
// include the Protocol module, preventing coder errors
|
531
|
-
rb_include_module(handler, IodineProtocol);
|
532
|
-
handler = RubyCaller.call(handler, iodine_new_func_id);
|
533
|
-
} else {
|
534
|
-
// include the Protocol module in the object's class
|
535
|
-
VALUE p_class = rb_obj_class(handler);
|
536
|
-
// include the Protocol module, preventing coder errors
|
537
|
-
rb_include_module(p_class, IodineProtocol);
|
538
|
-
// get the timeout
|
539
|
-
VALUE rb_tout = rb_ivar_get(p_class, iodine_timeout_var_id);
|
540
|
-
if (rb_tout == Qnil)
|
541
|
-
rb_tout = rb_ivar_get(handler, iodine_timeout_var_id);
|
542
|
-
timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
543
|
-
}
|
544
|
-
if (facil_attach(fd, dyn_set_protocol(fd, handler, timeout)))
|
545
|
-
return Qnil;
|
546
|
-
return handler;
|
547
|
-
}
|
548
|
-
|
549
|
-
static void on_open_dyn_protocol_instance(intptr_t fduuid, void *udata) {
|
550
|
-
VALUE rb_tout = rb_ivar_get((VALUE)udata, iodine_timeout_var_id);
|
551
|
-
uint8_t timeout = (TYPE(rb_tout) == T_FIXNUM) ? FIX2UINT(rb_tout) : 0;
|
552
|
-
protocol_s *pr = dyn_set_protocol(fduuid, (VALUE)udata, timeout);
|
553
|
-
Registry.remove((VALUE)udata);
|
554
|
-
facil_attach(fduuid, pr);
|
555
|
-
}
|
556
|
-
|
557
|
-
/**
|
558
|
-
Connects (as a TCP/IP client) to a remote TCP/IP server.
|
559
|
-
|
560
|
-
i.e.
|
561
|
-
|
562
|
-
Iodine.connect "example.com", 5000, MyProtocolClass.new
|
563
|
-
|
564
|
-
*/
|
565
|
-
static VALUE iodine_connect(VALUE self, VALUE address, VALUE port,
|
566
|
-
VALUE handler) {
|
567
|
-
if (TYPE(handler) == T_CLASS || TYPE(handler) == T_MODULE) {
|
568
|
-
// include the Protocol module, preventing coder errors
|
569
|
-
rb_include_module(handler, IodineProtocol);
|
570
|
-
handler = RubyCaller.call(handler, iodine_new_func_id);
|
571
|
-
} else {
|
572
|
-
// include the Protocol module in the object's class
|
573
|
-
VALUE p_class = rb_obj_class(handler);
|
574
|
-
// include the Protocol module, preventing coder errors
|
575
|
-
rb_include_module(p_class, IodineProtocol);
|
576
|
-
}
|
577
|
-
if (TYPE(port) != T_FIXNUM && TYPE(port) != T_STRING)
|
578
|
-
rb_raise(rb_eTypeError, "The port variable must be a Fixnum or a String.");
|
579
|
-
Registry.add(handler);
|
580
|
-
if (TYPE(port) == T_FIXNUM)
|
581
|
-
port = rb_funcall2(port, iodine_to_s_method_id, 0, NULL);
|
582
|
-
// connect
|
583
|
-
intptr_t uuid = facil_connect(.port = StringValueCStr(port),
|
584
|
-
.address = StringValueCStr(address),
|
585
|
-
.udata = (void *)handler,
|
586
|
-
.on_connect = on_open_dyn_protocol_instance,
|
587
|
-
.on_fail = remove_from_registry);
|
588
|
-
if (uuid == -1)
|
589
|
-
return Qnil;
|
590
|
-
iodine_set_fd(handler, uuid);
|
591
|
-
return handler;
|
592
|
-
(void)self;
|
593
|
-
}
|
594
|
-
|
595
|
-
/**
|
596
|
-
Attaches an existing file descriptor (`fd`) (i.e., a pipe, a unix socket,
|
597
|
-
etc') as if it were a regular connection.
|
598
|
-
|
599
|
-
i.e.
|
600
|
-
|
601
|
-
Iodine.attach my_io_obj.to_i, MyProtocolClass.new
|
602
|
-
|
603
|
-
*/
|
604
|
-
static VALUE iodine_attach_fd(VALUE self, VALUE rbfd, VALUE handler) {
|
605
|
-
Check_Type(rbfd, T_FIXNUM);
|
606
|
-
if (handler == Qnil || handler == Qfalse)
|
607
|
-
return Qfalse;
|
608
|
-
intptr_t uuid = FIX2INT(rbfd);
|
609
|
-
if (!uuid || uuid == -1)
|
610
|
-
return Qfalse;
|
611
|
-
/* make sure the uuid is connected to the sock library */
|
612
|
-
if (sock_fd2uuid(uuid) == -1)
|
613
|
-
sock_open(uuid);
|
614
|
-
if (TYPE(handler) == T_CLASS) {
|
615
|
-
// include the Protocol module, preventing coder errors
|
616
|
-
rb_include_module(handler, IodineProtocol);
|
617
|
-
handler = RubyCaller.call(handler, iodine_new_func_id);
|
618
|
-
} else {
|
619
|
-
// include the Protocol module in the object's class
|
620
|
-
VALUE p_class = rb_obj_class(handler);
|
621
|
-
// include the Protocol module, preventing coder errors
|
622
|
-
rb_include_module(p_class, IodineProtocol);
|
623
|
-
}
|
624
|
-
Registry.add(handler);
|
625
|
-
on_open_dyn_protocol_instance(uuid, (void *)handler);
|
626
|
-
return self;
|
627
|
-
}
|
628
|
-
/**
|
629
|
-
Attaches an existing IO object (i.e., a pipe, a unix socket, etc') as if it
|
630
|
-
were a regular connection.
|
631
|
-
|
632
|
-
i.e.
|
633
|
-
|
634
|
-
Iodine.attach my_io_obj, MyProtocolClass.new
|
635
|
-
|
636
|
-
*/
|
637
|
-
static VALUE iodine_attach_io(VALUE self, VALUE io, VALUE handler) {
|
638
|
-
return iodine_attach_fd(self, RubyCaller.call(io, iodine_to_i_func_id),
|
639
|
-
handler);
|
640
|
-
}
|
641
|
-
/* *****************************************************************************
|
642
|
-
Library Initialization
|
643
|
-
*****************************************************************************
|
644
|
-
*/
|
645
|
-
|
646
|
-
////////////////////////////////////////////////////////////////////////
|
647
|
-
// Ruby loads the library and invokes the Init_<lib_name> function...
|
648
|
-
//
|
649
|
-
// Here we connect all the C code to the Ruby interface, completing the bridge
|
650
|
-
// between Lib-Server and Ruby.
|
651
|
-
void Iodine_init_protocol(void) {
|
652
|
-
|
653
|
-
iodine_close_func_id = rb_intern("close");
|
654
|
-
|
655
|
-
/* add Iodine module functions */
|
656
|
-
rb_define_module_function(Iodine, "listen", iodine_listen, 2);
|
657
|
-
rb_define_module_function(Iodine, "connect", iodine_connect, 3);
|
658
|
-
rb_define_module_function(Iodine, "attach_io", iodine_attach_io, 2);
|
659
|
-
rb_define_module_function(Iodine, "attach_fd", iodine_attach_fd, 2);
|
660
|
-
|
661
|
-
/* Create the `Protocol` module and set stub functions */
|
662
|
-
IodineProtocol = rb_define_module_under(Iodine, "Protocol");
|
663
|
-
rb_define_method(IodineProtocol, "on_open", not_implemented, 0);
|
664
|
-
rb_define_method(IodineProtocol, "on_close", not_implemented, 0);
|
665
|
-
rb_define_method(IodineProtocol, "on_message", not_implemented2, 1);
|
666
|
-
rb_define_method(IodineProtocol, "on_data", default_on_data, 0);
|
667
|
-
rb_define_method(IodineProtocol, "on_ready", not_implemented_on_ready, 0);
|
668
|
-
rb_define_method(IodineProtocol, "on_drained", not_implemented_drained, 0);
|
669
|
-
rb_define_method(IodineProtocol, "on_shutdown", not_implemented, 0);
|
670
|
-
rb_define_method(IodineProtocol, "ping", not_implemented_ping, 0);
|
671
|
-
|
672
|
-
/* Add module functions */
|
673
|
-
rb_define_singleton_method(IodineProtocol, "defer", dyn_defer, -1);
|
674
|
-
|
675
|
-
/* Add module instance methods */
|
676
|
-
rb_define_method(IodineProtocol, "open?", dyn_is_open, 0);
|
677
|
-
rb_define_method(IodineProtocol, "conn_id", dyn_uuid, 0);
|
678
|
-
rb_define_method(IodineProtocol, "read", dyn_read, -1);
|
679
|
-
rb_define_method(IodineProtocol, "write", dyn_write, 1);
|
680
|
-
rb_define_method(IodineProtocol, "write!", dyn_write_move, 1);
|
681
|
-
rb_define_method(IodineProtocol, "write_urgent", dyn_write_urgent, 1);
|
682
|
-
rb_define_method(IodineProtocol, "close", dyn_close, 0);
|
683
|
-
rb_define_method(IodineProtocol, "defer", dyn_defer, -1);
|
684
|
-
rb_define_method(IodineProtocol, "switch_protocol", dyn_switch_prot, 1);
|
685
|
-
rb_define_method(IodineProtocol, "timeout=", dyn_set_timeout, 1);
|
686
|
-
rb_define_method(IodineProtocol, "timeout", dyn_get_timeout, 0);
|
687
|
-
rb_define_method(IodineProtocol, "subscribe", iodine_proto_subscribe, -1);
|
688
|
-
rb_define_method(IodineProtocol, "publish", iodine_publish, -1);
|
689
|
-
}
|