iodine 0.7.59 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -20
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -146
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
data/ext/iodine/iodine_tcp.c
DELETED
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
#include "iodine.h"
|
|
2
|
-
#include <ruby/encoding.h>
|
|
3
|
-
#include <ruby/io.h>
|
|
4
|
-
|
|
5
|
-
#define FIO_INCLUDE_STR
|
|
6
|
-
#include "fio.h"
|
|
7
|
-
|
|
8
|
-
/* *****************************************************************************
|
|
9
|
-
Static stuff
|
|
10
|
-
***************************************************************************** */
|
|
11
|
-
static ID call_id;
|
|
12
|
-
static ID on_closed_id;
|
|
13
|
-
static rb_encoding *IodineBinaryEncoding;
|
|
14
|
-
|
|
15
|
-
static VALUE port_id;
|
|
16
|
-
static VALUE address_id;
|
|
17
|
-
static VALUE handler_id;
|
|
18
|
-
static VALUE timeout_id;
|
|
19
|
-
|
|
20
|
-
/* *****************************************************************************
|
|
21
|
-
Raw TCP/IP Protocol
|
|
22
|
-
***************************************************************************** */
|
|
23
|
-
|
|
24
|
-
#define IODINE_MAX_READ 8192
|
|
25
|
-
|
|
26
|
-
typedef struct {
|
|
27
|
-
fio_protocol_s p;
|
|
28
|
-
VALUE io;
|
|
29
|
-
} iodine_protocol_s;
|
|
30
|
-
|
|
31
|
-
typedef struct {
|
|
32
|
-
VALUE io;
|
|
33
|
-
ssize_t len;
|
|
34
|
-
char buffer[IODINE_MAX_READ];
|
|
35
|
-
} iodine_buffer_s;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Converts an iodine_buffer_s pointer to a Ruby string.
|
|
39
|
-
*/
|
|
40
|
-
static void *iodine_tcp_on_data_in_GIL(void *b_) {
|
|
41
|
-
iodine_buffer_s *b = b_;
|
|
42
|
-
if (!b) {
|
|
43
|
-
FIO_LOG_FATAL("(iodine->tcp/ip->on_data->GIL) WTF?!\n");
|
|
44
|
-
exit(-1);
|
|
45
|
-
}
|
|
46
|
-
VALUE data = IodineStore.add(rb_str_new(b->buffer, b->len));
|
|
47
|
-
rb_enc_associate(data, IodineBinaryEncoding);
|
|
48
|
-
iodine_connection_fire_event(b->io, IODINE_CONNECTION_ON_MESSAGE, data);
|
|
49
|
-
IodineStore.remove(data);
|
|
50
|
-
return NULL;
|
|
51
|
-
// return (void *)IodineStore.add(rb_usascii_str_new((const char *)b->buffer,
|
|
52
|
-
// b->len));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Called when a data is available, but will not run concurrently */
|
|
56
|
-
static void iodine_tcp_on_data(intptr_t uuid, fio_protocol_s *protocol) {
|
|
57
|
-
iodine_buffer_s buffer;
|
|
58
|
-
buffer.len = fio_read(uuid, buffer.buffer, IODINE_MAX_READ);
|
|
59
|
-
if (buffer.len <= 0) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
buffer.io = ((iodine_protocol_s *)protocol)->io;
|
|
63
|
-
IodineCaller.enterGVL(iodine_tcp_on_data_in_GIL, &buffer);
|
|
64
|
-
if (buffer.len == IODINE_MAX_READ) {
|
|
65
|
-
fio_force_event(uuid, FIO_EVENT_ON_DATA);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** called when the socket is ready to be written to. */
|
|
70
|
-
static void iodine_tcp_on_ready(intptr_t uuid, fio_protocol_s *protocol) {
|
|
71
|
-
iodine_protocol_s *p = (iodine_protocol_s *)protocol;
|
|
72
|
-
iodine_connection_fire_event(p->io, IODINE_CONNECTION_ON_DRAINED, Qnil);
|
|
73
|
-
(void)uuid;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Called when the server is shutting down, immediately before closing the
|
|
78
|
-
* connection.
|
|
79
|
-
*/
|
|
80
|
-
static uint8_t iodine_tcp_on_shutdown(intptr_t uuid, fio_protocol_s *protocol) {
|
|
81
|
-
iodine_protocol_s *p = (iodine_protocol_s *)protocol;
|
|
82
|
-
iodine_connection_fire_event(p->io, IODINE_CONNECTION_ON_SHUTDOWN, Qnil);
|
|
83
|
-
return 0;
|
|
84
|
-
(void)uuid;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Called when the connection was closed, but will not run concurrently */
|
|
88
|
-
|
|
89
|
-
static void iodine_tcp_on_close(intptr_t uuid, fio_protocol_s *protocol) {
|
|
90
|
-
iodine_protocol_s *p = (iodine_protocol_s *)protocol;
|
|
91
|
-
iodine_connection_fire_event(p->io, IODINE_CONNECTION_ON_CLOSE, Qnil);
|
|
92
|
-
free(p);
|
|
93
|
-
(void)uuid;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** called when a connection's timeout was reached */
|
|
97
|
-
static void iodine_tcp_ping(intptr_t uuid, fio_protocol_s *protocol) {
|
|
98
|
-
iodine_protocol_s *p = (iodine_protocol_s *)protocol;
|
|
99
|
-
iodine_connection_fire_event(p->io, IODINE_CONNECTION_PING, Qnil);
|
|
100
|
-
(void)uuid;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** fio_listen callback, called when a connection opens */
|
|
104
|
-
static void iodine_tcp_on_open(intptr_t uuid, void *udata) {
|
|
105
|
-
if (!fio_is_valid(uuid))
|
|
106
|
-
return;
|
|
107
|
-
VALUE handler = IodineCaller.call((VALUE)udata, call_id);
|
|
108
|
-
IodineStore.add(handler);
|
|
109
|
-
iodine_tcp_attch_uuid(uuid, handler);
|
|
110
|
-
IodineStore.remove(handler);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/** called when the listening socket is destroyed */
|
|
114
|
-
static void iodine_tcp_on_finish(intptr_t uuid, void *udata) {
|
|
115
|
-
IodineStore.remove((VALUE)udata);
|
|
116
|
-
(void)uuid;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* The `on_connect` callback should either call `fio_attach` or close the
|
|
121
|
-
* connection.
|
|
122
|
-
*/
|
|
123
|
-
static void iodine_tcp_on_connect(intptr_t uuid, void *udata) {
|
|
124
|
-
iodine_tcp_attch_uuid(uuid, (VALUE)udata);
|
|
125
|
-
IodineStore.remove((VALUE)udata);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* The `on_fail` is called when a socket fails to connect. The old sock UUID
|
|
130
|
-
* is passed along.
|
|
131
|
-
*/
|
|
132
|
-
static void iodine_tcp_on_fail(intptr_t uuid, void *udata) {
|
|
133
|
-
IodineStore.remove((VALUE)udata);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/* *****************************************************************************
|
|
137
|
-
The Ruby API implementation
|
|
138
|
-
***************************************************************************** */
|
|
139
|
-
|
|
140
|
-
// clang-format off
|
|
141
|
-
/**
|
|
142
|
-
The {listen} method instructs iodine to listen to incoming connections using either TCP/IP or Unix sockets.
|
|
143
|
-
|
|
144
|
-
The method accepts a single Hash argument with the following optional keys:
|
|
145
|
-
|
|
146
|
-
:port :: The port to listen to, deafults to nil (using a Unix socket)
|
|
147
|
-
:address :: The address to listen to, which could be a Unix Socket path as well as an IPv4 / IPv6 address. Deafults to 0.0.0.0 (or the IPv6 equivelant).
|
|
148
|
-
:handler :: An object that answers the `call` method (i.e., a Proc).
|
|
149
|
-
|
|
150
|
-
The method also accepts an optional block.
|
|
151
|
-
|
|
152
|
-
Either a block or the :handler key MUST be present.
|
|
153
|
-
|
|
154
|
-
The handler Proc (or object) should return a connection callback object that supports the following callbacks (see also {Iodine::Connection}):
|
|
155
|
-
|
|
156
|
-
on_open(client) :: called after a connection was established
|
|
157
|
-
on_message(client, data) :: called when incoming data is available. Data may be fragmented.
|
|
158
|
-
on_drained(client) :: called when all the pending `client.write` events have been processed (see {Iodine::Connection#pending}).
|
|
159
|
-
ping(client) :: called whenever a timeout has occured (see {Iodine::Connection#timeout=}).
|
|
160
|
-
on_shutdown(client) :: called if the server is shutting down. This is called before the connection is closed.
|
|
161
|
-
on_close(client) :: called when the connection with the client was closed.
|
|
162
|
-
|
|
163
|
-
The `client` argument is an {Iodine::Connection} instance that represents the connection / the client.
|
|
164
|
-
|
|
165
|
-
Here's a telnet based chat-room example:
|
|
166
|
-
|
|
167
|
-
require 'iodine'
|
|
168
|
-
# define the protocol for our service
|
|
169
|
-
module ChatHandler
|
|
170
|
-
def self.on_open(client)
|
|
171
|
-
# Set a connection timeout
|
|
172
|
-
client.timeout = 10
|
|
173
|
-
# subscribe to the chat channel.
|
|
174
|
-
client.subscribe :chat
|
|
175
|
-
# Write a welcome message
|
|
176
|
-
client.publish :chat, "new member entered the chat\r\n"
|
|
177
|
-
end
|
|
178
|
-
# this is called for incoming data - note data might be fragmented.
|
|
179
|
-
def self.on_message(client, data)
|
|
180
|
-
# publish the data we received
|
|
181
|
-
client.publish :chat, data
|
|
182
|
-
# close the connection when the time comes
|
|
183
|
-
client.close if data =~ /^bye[\n\r]/
|
|
184
|
-
end
|
|
185
|
-
# called whenever timeout occurs.
|
|
186
|
-
def self.ping(client)
|
|
187
|
-
client.write "System: quite, isn't it...?\r\n"
|
|
188
|
-
end
|
|
189
|
-
# called if the connection is still open and the server is shutting down.
|
|
190
|
-
def self.on_shutdown(client)
|
|
191
|
-
# write the data we received
|
|
192
|
-
client.write "Chat server going away. Try again later.\r\n"
|
|
193
|
-
end
|
|
194
|
-
# returns the callback object (self).
|
|
195
|
-
def self.call
|
|
196
|
-
self
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
# we use can both the `handler` keyword or a block, anything that answers #call.
|
|
200
|
-
Iodine.listen(port: "3000", handler: ChatHandler)
|
|
201
|
-
# start the service
|
|
202
|
-
Iodine.threads = 1
|
|
203
|
-
Iodine.start
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
Returns the handler object used.
|
|
208
|
-
*/
|
|
209
|
-
intptr_t iodine_tcp_listen(iodine_connection_args_s args) {
|
|
210
|
-
// clang-format on
|
|
211
|
-
IodineStore.add(args.handler);
|
|
212
|
-
#ifdef __MINGW32__
|
|
213
|
-
return fio_listen(.port = args.port.data, .address = args.address.data,
|
|
214
|
-
.on_open = iodine_tcp_on_open,
|
|
215
|
-
.on_finish = iodine_tcp_on_finish,
|
|
216
|
-
.udata = (void *)args.handler);
|
|
217
|
-
#else
|
|
218
|
-
return fio_listen(.port = args.port.data, .address = args.address.data,
|
|
219
|
-
.on_open = iodine_tcp_on_open,
|
|
220
|
-
.on_finish = iodine_tcp_on_finish, .tls = args.tls,
|
|
221
|
-
.udata = (void *)args.handler);
|
|
222
|
-
#endif
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// clang-format off
|
|
226
|
-
/**
|
|
227
|
-
The {connect} method instructs iodine to connect to a server using either TCP/IP or Unix sockets.
|
|
228
|
-
|
|
229
|
-
The method accepts a single Hash argument with the following optional keys:
|
|
230
|
-
|
|
231
|
-
:port :: The port to listen to, deafults to 0 (using a Unix socket)
|
|
232
|
-
:address :: The address to listen to, which could be a Unix Socket path as well as an IPv4 / IPv6 address. Deafults to 0.0.0.0 (or the IPv6 equivelant).
|
|
233
|
-
:handler :: A connection callback object that supports the following same callbacks listen in the {listen} method's documentation.
|
|
234
|
-
:timeout :: An integer timeout for connection establishment (doen't effect the new connection's timeout. Should be in the rand of 0..255.
|
|
235
|
-
:tls :: An {Iodine::TLS} object (optional) for secure connections.
|
|
236
|
-
|
|
237
|
-
The method also accepts an optional block.
|
|
238
|
-
|
|
239
|
-
Either a block or the :handler key MUST be present.
|
|
240
|
-
|
|
241
|
-
If the connection fails, only the `on_close` callback will be called (with a `nil` client).
|
|
242
|
-
|
|
243
|
-
Returns the handler object used.
|
|
244
|
-
*/
|
|
245
|
-
intptr_t iodine_tcp_connect(iodine_connection_args_s args){
|
|
246
|
-
// clang-format on
|
|
247
|
-
IodineStore.add(args.handler);
|
|
248
|
-
#ifdef __MINGW32__
|
|
249
|
-
return fio_connect(.port = args.port.data, .address = args.address.data,
|
|
250
|
-
.on_connect = iodine_tcp_on_connect,
|
|
251
|
-
.on_fail = iodine_tcp_on_fail, .timeout = args.ping,
|
|
252
|
-
.udata = (void *)args.handler);
|
|
253
|
-
#else
|
|
254
|
-
return fio_connect(.port = args.port.data, .address = args.address.data,
|
|
255
|
-
.on_connect = iodine_tcp_on_connect, .tls = args.tls,
|
|
256
|
-
.on_fail = iodine_tcp_on_fail, .timeout = args.ping,
|
|
257
|
-
.udata = (void *)args.handler);
|
|
258
|
-
#endif
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// clang-format off
|
|
262
|
-
/**
|
|
263
|
-
The {attach_fd} method instructs iodine to attach a socket to the server using it's numerical file descriptor.
|
|
264
|
-
|
|
265
|
-
This is faster than attaching a Ruby IO object since it allows iodine to directly call the system's read/write methods. However, this doesn't support TLS/SSL connections.
|
|
266
|
-
|
|
267
|
-
This method requires two objects, a file descriptor (`fd`) and a callback object.
|
|
268
|
-
|
|
269
|
-
See {listen} for details about the callback object.
|
|
270
|
-
|
|
271
|
-
Returns the callback object (handler) used.
|
|
272
|
-
*/
|
|
273
|
-
static VALUE iodine_tcp_attach_fd(VALUE self, VALUE fd, VALUE handler) {
|
|
274
|
-
// clang-format on
|
|
275
|
-
Check_Type(fd, T_FIXNUM);
|
|
276
|
-
if (handler == Qnil || handler == Qfalse || handler == Qtrue) {
|
|
277
|
-
rb_raise(rb_eArgError, "A callback object must be provided.");
|
|
278
|
-
}
|
|
279
|
-
IodineStore.add(handler);
|
|
280
|
-
int other = dup(NUM2INT(fd));
|
|
281
|
-
if (other == -1) {
|
|
282
|
-
rb_raise(rb_eIOError, "invalid fd.");
|
|
283
|
-
}
|
|
284
|
-
intptr_t uuid = fio_fd2uuid(other);
|
|
285
|
-
iodine_tcp_attch_uuid(uuid, handler);
|
|
286
|
-
IodineStore.remove(handler);
|
|
287
|
-
return handler;
|
|
288
|
-
(void)self;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/* *****************************************************************************
|
|
292
|
-
Add the Ruby API methods to the Iodine object
|
|
293
|
-
***************************************************************************** */
|
|
294
|
-
|
|
295
|
-
void iodine_init_tcp_connections(void) {
|
|
296
|
-
call_id = rb_intern2("call", 4);
|
|
297
|
-
port_id = IodineStore.add(rb_id2sym(rb_intern("port")));
|
|
298
|
-
address_id = IodineStore.add(rb_id2sym(rb_intern("address")));
|
|
299
|
-
handler_id = IodineStore.add(rb_id2sym(rb_intern("handler")));
|
|
300
|
-
timeout_id = IodineStore.add(rb_id2sym(rb_intern("timeout")));
|
|
301
|
-
on_closed_id = rb_intern("on_closed");
|
|
302
|
-
|
|
303
|
-
IodineBinaryEncoding = rb_enc_find("binary");
|
|
304
|
-
|
|
305
|
-
rb_define_module_function(IodineModule, "attach_fd", iodine_tcp_attach_fd, 2);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/* *****************************************************************************
|
|
309
|
-
Allow uuid attachment
|
|
310
|
-
***************************************************************************** */
|
|
311
|
-
|
|
312
|
-
/** assigns a protocol and IO object to a handler */
|
|
313
|
-
void iodine_tcp_attch_uuid(intptr_t uuid, VALUE handler) {
|
|
314
|
-
FIO_LOG_DEBUG("Iodine attaching handler %p to uuid %p", (void *)handler,
|
|
315
|
-
(void *)uuid);
|
|
316
|
-
if (handler == Qnil || handler == Qfalse || handler == Qtrue) {
|
|
317
|
-
fio_close(uuid);
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
/* temporary, in case `iodine_connection_new` invokes the GC */
|
|
321
|
-
iodine_protocol_s *p = malloc(sizeof(*p));
|
|
322
|
-
FIO_ASSERT_ALLOC(p);
|
|
323
|
-
*p = (iodine_protocol_s){
|
|
324
|
-
.p =
|
|
325
|
-
{
|
|
326
|
-
.on_data = iodine_tcp_on_data,
|
|
327
|
-
.on_ready = NULL /* set only after the on_open callback */,
|
|
328
|
-
.on_shutdown = iodine_tcp_on_shutdown,
|
|
329
|
-
.on_close = iodine_tcp_on_close,
|
|
330
|
-
.ping = iodine_tcp_ping,
|
|
331
|
-
},
|
|
332
|
-
.io = iodine_connection_new(.type = IODINE_CONNECTION_RAW, .uuid = uuid,
|
|
333
|
-
.arg = p, .handler = handler),
|
|
334
|
-
};
|
|
335
|
-
/* clear away (remember the connection object manages these concerns) */
|
|
336
|
-
fio_attach(uuid, &p->p);
|
|
337
|
-
if (fio_is_valid(uuid)) {
|
|
338
|
-
iodine_connection_fire_event(p->io, IODINE_CONNECTION_ON_OPEN, Qnil);
|
|
339
|
-
p->p.on_ready = iodine_tcp_on_ready;
|
|
340
|
-
fio_force_event(uuid, FIO_EVENT_ON_READY);
|
|
341
|
-
} else {
|
|
342
|
-
FIO_LOG_DEBUG(
|
|
343
|
-
"Iodine couldn't attach handler %p to uuid %p - invalid uuid.",
|
|
344
|
-
(void *)handler, (void *)uuid);
|
|
345
|
-
}
|
|
346
|
-
}
|
data/ext/iodine/iodine_tcp.h
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#ifndef H_IODINE_RAW_TCP_IP_H
|
|
2
|
-
#define H_IODINE_RAW_TCP_IP_H
|
|
3
|
-
|
|
4
|
-
#include "ruby.h"
|
|
5
|
-
|
|
6
|
-
#include "iodine.h"
|
|
7
|
-
|
|
8
|
-
void iodine_init_tcp_connections(void);
|
|
9
|
-
void iodine_tcp_attch_uuid(intptr_t uuid, VALUE handler);
|
|
10
|
-
intptr_t iodine_tcp_listen(iodine_connection_args_s args);
|
|
11
|
-
intptr_t iodine_tcp_connect(iodine_connection_args_s args);
|
|
12
|
-
|
|
13
|
-
#endif
|
data/ext/iodine/iodine_tls.c
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
#ifdef __MINGW32__
|
|
2
|
-
// make pedantic compiler happy
|
|
3
|
-
typedef struct {
|
|
4
|
-
int bogus;
|
|
5
|
-
} bogus_s;
|
|
6
|
-
|
|
7
|
-
#else
|
|
8
|
-
#include <ruby.h>
|
|
9
|
-
|
|
10
|
-
#include <fio.h>
|
|
11
|
-
#include <fio_tls.h>
|
|
12
|
-
#include <iodine.h>
|
|
13
|
-
|
|
14
|
-
static VALUE server_name_sym = Qnil, certificate_sym = Qnil,
|
|
15
|
-
private_key_sym = Qnil, password_sym = Qnil;
|
|
16
|
-
VALUE IodineTLSClass;
|
|
17
|
-
/* *****************************************************************************
|
|
18
|
-
C <=> Ruby Data allocation
|
|
19
|
-
***************************************************************************** */
|
|
20
|
-
|
|
21
|
-
static size_t iodine_tls_data_size(const void *c_) {
|
|
22
|
-
return sizeof(fio_tls_s *);
|
|
23
|
-
(void)c_;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
static void iodine_tls_data_free(void *c_) { fio_tls_destroy(c_); }
|
|
27
|
-
|
|
28
|
-
static const rb_data_type_t iodine_tls_data_type = {
|
|
29
|
-
.wrap_struct_name = "IodineTLSData",
|
|
30
|
-
.function =
|
|
31
|
-
{
|
|
32
|
-
.dmark = NULL,
|
|
33
|
-
.dfree = iodine_tls_data_free,
|
|
34
|
-
.dsize = iodine_tls_data_size,
|
|
35
|
-
},
|
|
36
|
-
.data = NULL,
|
|
37
|
-
// .flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/* Iodine::PubSub::Engine.allocate */
|
|
41
|
-
static VALUE iodine_tls_data_alloc_c(VALUE klass) {
|
|
42
|
-
fio_tls_s *tls = fio_tls_new(NULL, NULL, NULL, NULL);
|
|
43
|
-
FIO_ASSERT_ALLOC(tls);
|
|
44
|
-
return TypedData_Wrap_Struct(klass, &iodine_tls_data_type, tls);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/* *****************************************************************************
|
|
48
|
-
ALPN selection callback
|
|
49
|
-
***************************************************************************** */
|
|
50
|
-
|
|
51
|
-
FIO_FUNC void iodine_tls_alpn_cb(intptr_t uuid, void *udata, void *block_) {
|
|
52
|
-
if (!fio_is_valid(uuid)) {
|
|
53
|
-
FIO_LOG_DEBUG("ALPN callback called for invalid connetion. SSL/TLS error?");
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
VALUE new_handler = IodineCaller.call((VALUE)block_, iodine_call_id);
|
|
57
|
-
if (!new_handler || new_handler == Qnil || new_handler == Qtrue ||
|
|
58
|
-
new_handler == Qfalse) {
|
|
59
|
-
fio_close(uuid);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
iodine_tcp_attch_uuid(uuid, new_handler);
|
|
64
|
-
|
|
65
|
-
(void)udata; /* we can't use `udata`, since it's different in HTTP vs. TCP */
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/* *****************************************************************************
|
|
69
|
-
C API
|
|
70
|
-
***************************************************************************** */
|
|
71
|
-
|
|
72
|
-
fio_tls_s *iodine_tls2c(VALUE self) {
|
|
73
|
-
fio_tls_s *c = NULL;
|
|
74
|
-
if (self == Qnil || self == Qfalse)
|
|
75
|
-
return NULL;
|
|
76
|
-
TypedData_Get_Struct(self, fio_tls_s, &iodine_tls_data_type, c);
|
|
77
|
-
if (!c) {
|
|
78
|
-
rb_raise(rb_eTypeError, "Iodine::TLS error - not an Iodine::TLS object?");
|
|
79
|
-
}
|
|
80
|
-
return c;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/* *****************************************************************************
|
|
84
|
-
Ruby API
|
|
85
|
-
***************************************************************************** */
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
Assigns the TLS context a public sertificate, allowing remote parties to
|
|
89
|
-
validate the connection's identity.
|
|
90
|
-
|
|
91
|
-
A self signed certificate is automatically created if the `server_name` argument
|
|
92
|
-
is specified and either (or both) of the `certificate` or `private_ket`
|
|
93
|
-
arguments are missing.
|
|
94
|
-
|
|
95
|
-
Some implementations allow servers to have more than a single certificate, which
|
|
96
|
-
will be selected using the SNI extension. I believe the existing OpenSSL
|
|
97
|
-
implementation supports this option (untested).
|
|
98
|
-
|
|
99
|
-
Iodine::TLS#use_certificate(server_name,
|
|
100
|
-
certificate = nil,
|
|
101
|
-
private_key = nil,
|
|
102
|
-
password = nil)
|
|
103
|
-
|
|
104
|
-
Certificates and keys should be String objects leading to a PEM file.
|
|
105
|
-
|
|
106
|
-
This method also accepts named arguments. i.e.:
|
|
107
|
-
|
|
108
|
-
tls = Iodine::TLS.new
|
|
109
|
-
tls.use_certificate server_name: "example.com"
|
|
110
|
-
tls.use_certificate certificate: "my_cert.pem", private_key: "my_key.pem"
|
|
111
|
-
|
|
112
|
-
Since TLS setup is crucial for security, a missing file will result in Iodine
|
|
113
|
-
crashing with an error message. This is expected behavior.
|
|
114
|
-
|
|
115
|
-
*/
|
|
116
|
-
static VALUE iodine_tls_use_certificate(int argc, VALUE *argv, VALUE self) {
|
|
117
|
-
VALUE server_name = Qnil, certificate = Qnil, private_key = Qnil,
|
|
118
|
-
password = Qnil;
|
|
119
|
-
if (argc == 1 && RB_TYPE_P(argv[0], T_HASH)) {
|
|
120
|
-
/* named arguments */
|
|
121
|
-
server_name = rb_hash_aref(argv[0], server_name_sym);
|
|
122
|
-
certificate = rb_hash_aref(argv[0], certificate_sym);
|
|
123
|
-
private_key = rb_hash_aref(argv[0], private_key_sym);
|
|
124
|
-
password = rb_hash_aref(argv[0], password_sym);
|
|
125
|
-
} else {
|
|
126
|
-
/* regular arguments */
|
|
127
|
-
switch (argc) {
|
|
128
|
-
case 4: /* overflow */
|
|
129
|
-
password = argv[3];
|
|
130
|
-
Check_Type(password, T_STRING);
|
|
131
|
-
case 3: /* overflow */
|
|
132
|
-
private_key = argv[1];
|
|
133
|
-
Check_Type(private_key, T_STRING);
|
|
134
|
-
case 2: /* overflow */
|
|
135
|
-
certificate = argv[2];
|
|
136
|
-
Check_Type(certificate, T_STRING);
|
|
137
|
-
case 1: /* overflow */
|
|
138
|
-
server_name = argv[0];
|
|
139
|
-
Check_Type(server_name, T_STRING);
|
|
140
|
-
break;
|
|
141
|
-
default:
|
|
142
|
-
rb_raise(rb_eArgError, "expecting 1..4 arguments or named arguments.");
|
|
143
|
-
return self;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
fio_tls_s *t = iodine_tls2c(self);
|
|
148
|
-
char *srvname = (server_name == Qnil ? NULL : StringValueCStr(server_name));
|
|
149
|
-
char *pubcert = (certificate == Qnil ? NULL : StringValueCStr(certificate));
|
|
150
|
-
char *prvkey = (private_key == Qnil ? NULL : StringValueCStr(private_key));
|
|
151
|
-
char *pass = (password == Qnil ? NULL : StringValueCStr(password));
|
|
152
|
-
|
|
153
|
-
fio_tls_cert_add(t, srvname, pubcert, prvkey, pass);
|
|
154
|
-
return self;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
Adds a certificate PEM file to the list of trusted certificates and enforces
|
|
159
|
-
peer verification.
|
|
160
|
-
|
|
161
|
-
This is extremely important when using {Iodine::TLS} for client connections,
|
|
162
|
-
since adding the target server's
|
|
163
|
-
|
|
164
|
-
It is enough to add the Certificate Authority's (CA) certificate, there's no
|
|
165
|
-
need to add each client or server certificate.
|
|
166
|
-
|
|
167
|
-
When {trust} is used on a server TLS, only trusted clients will be allowed to
|
|
168
|
-
connect.
|
|
169
|
-
|
|
170
|
-
Since TLS setup is crucial for security, a missing file will result in Iodine
|
|
171
|
-
crashing with an error message. This is expected behavior.
|
|
172
|
-
*/
|
|
173
|
-
static VALUE iodine_tls_trust(VALUE self, VALUE certificate) {
|
|
174
|
-
Check_Type(certificate, T_STRING);
|
|
175
|
-
fio_tls_s *t = iodine_tls2c(self);
|
|
176
|
-
char *pubcert =
|
|
177
|
-
(certificate == Qnil ? NULL : IODINE_RSTRINFO(certificate).data);
|
|
178
|
-
fio_tls_trust(t, pubcert);
|
|
179
|
-
return self;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
Adds an ALPN protocol callback for the named protocol, the required block must
|
|
184
|
-
return the handler for that protocol.
|
|
185
|
-
|
|
186
|
-
The first protocol added will be the default protocol in cases where ALPN
|
|
187
|
-
failed.
|
|
188
|
-
|
|
189
|
-
i.e.:
|
|
190
|
-
|
|
191
|
-
tls.on_protocol("http/1.1") { HTTPConnection.new }
|
|
192
|
-
|
|
193
|
-
When implementing TLS clients, this identifies the protocol(s) that should be
|
|
194
|
-
requested by the client.
|
|
195
|
-
|
|
196
|
-
When implementing TLS servers, this identifies the protocol(s) offered by the
|
|
197
|
-
server.
|
|
198
|
-
|
|
199
|
-
More than a single protocol can be set, but iodine doesn't offer, at this
|
|
200
|
-
moment, a way to handle these changes or to detect which protocol was selected
|
|
201
|
-
except by assigning a different callback per protocol.
|
|
202
|
-
|
|
203
|
-
This is implemented using the ALPN extension to TLS.
|
|
204
|
-
*/
|
|
205
|
-
static VALUE iodine_tls_alpn(VALUE self, VALUE protocol_name) {
|
|
206
|
-
Check_Type(protocol_name, T_STRING);
|
|
207
|
-
rb_need_block();
|
|
208
|
-
fio_tls_s *t = iodine_tls2c(self);
|
|
209
|
-
char *prname =
|
|
210
|
-
(protocol_name == Qnil ? NULL : IODINE_RSTRINFO(protocol_name).data);
|
|
211
|
-
VALUE block = IodineStore.add(rb_block_proc());
|
|
212
|
-
fio_tls_alpn_add(t, prname, iodine_tls_alpn_cb, (void *)block,
|
|
213
|
-
(void (*)(void *))IodineStore.remove);
|
|
214
|
-
return self;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
Creates a new {Iodine::TLS} object and calles the {#use_certificate} method with
|
|
219
|
-
the supplied arguments.
|
|
220
|
-
*/
|
|
221
|
-
static VALUE iodine_tls_new(int argc, VALUE *argv, VALUE self) {
|
|
222
|
-
if (argc) {
|
|
223
|
-
iodine_tls_use_certificate(argc, argv, self);
|
|
224
|
-
}
|
|
225
|
-
return self;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/* *****************************************************************************
|
|
229
|
-
Initialize Iodine::TLS
|
|
230
|
-
***************************************************************************** */
|
|
231
|
-
#define IODINE_MAKE_SYM(name) \
|
|
232
|
-
do { \
|
|
233
|
-
name##_sym = rb_id2sym(rb_intern(#name)); \
|
|
234
|
-
rb_global_variable(&name##_sym); \
|
|
235
|
-
} while (0)
|
|
236
|
-
|
|
237
|
-
void iodine_init_tls(void) {
|
|
238
|
-
|
|
239
|
-
IODINE_MAKE_SYM(server_name);
|
|
240
|
-
IODINE_MAKE_SYM(certificate);
|
|
241
|
-
IODINE_MAKE_SYM(private_key);
|
|
242
|
-
IODINE_MAKE_SYM(password);
|
|
243
|
-
|
|
244
|
-
IodineTLSClass = rb_define_class_under(IodineModule, "TLS", rb_cObject);
|
|
245
|
-
rb_define_alloc_func(IodineTLSClass, iodine_tls_data_alloc_c);
|
|
246
|
-
rb_define_method(IodineTLSClass, "initialize", iodine_tls_new, -1);
|
|
247
|
-
rb_define_method(IodineTLSClass, "use_certificate",
|
|
248
|
-
iodine_tls_use_certificate, -1);
|
|
249
|
-
rb_define_method(IodineTLSClass, "trust", iodine_tls_trust, 1);
|
|
250
|
-
rb_define_method(IodineTLSClass, "on_protocol", iodine_tls_alpn, 1);
|
|
251
|
-
|
|
252
|
-
#if HAVE_OPENSSL
|
|
253
|
-
rb_const_set(IodineTLSClass, rb_intern("SUPPORTED"), Qtrue);
|
|
254
|
-
#elif HAVE_BEARSSL
|
|
255
|
-
rb_const_set(IodineTLSClass, rb_intern("SUPPORTED"), Qfalse);
|
|
256
|
-
#else
|
|
257
|
-
rb_const_set(IodineTLSClass, rb_intern("SUPPORTED"), Qfalse);
|
|
258
|
-
#endif
|
|
259
|
-
}
|
|
260
|
-
#undef IODINE_MAKE_SYM
|
|
261
|
-
#endif
|