iodine 0.7.60 → 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.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/.clang-format +15 -0
  3. data/.rspec +1 -0
  4. data/.yardopts +1 -3
  5. data/CHANGELOG.md +132 -12
  6. data/Gemfile +5 -7
  7. data/LICENSE.txt +1 -1
  8. data/README.md +147 -671
  9. data/Rakefile +9 -16
  10. data/examples/chatroom.nru +9 -0
  11. data/examples/client +74 -0
  12. data/examples/config-hello.nru +9 -0
  13. data/examples/config-hello.ru +11 -0
  14. data/examples/config-hijack.ru +13 -0
  15. data/examples/config-shootout.ru +43 -0
  16. data/examples/config.nru +120 -0
  17. data/examples/config.rb +37 -0
  18. data/examples/config.ru +42 -53
  19. data/examples/fake +24 -0
  20. data/examples/hello_neorack +137 -0
  21. data/examples/routes.nru +29 -0
  22. data/examples/routes.ru +42 -0
  23. data/exe/iodine +218 -258
  24. data/ext/iodine/extconf.rb +27 -100
  25. data/ext/iodine/fio-stl.h +128336 -0
  26. data/ext/iodine/fio-stl.md +26396 -0
  27. data/ext/iodine/iodine.c +213 -1393
  28. data/ext/iodine/iodine.h +250 -55
  29. data/ext/iodine/iodine_arg_helper.h +329 -0
  30. data/ext/iodine/iodine_caller.h +234 -25
  31. data/ext/iodine/iodine_cli.h +434 -0
  32. data/ext/iodine/iodine_compression.h +312 -0
  33. data/ext/iodine/iodine_connection.h +3484 -34
  34. data/ext/iodine/iodine_core.h +363 -0
  35. data/ext/iodine/iodine_crypto.h +1441 -0
  36. data/ext/iodine/iodine_defer.h +225 -4
  37. data/ext/iodine/iodine_json.h +738 -4
  38. data/ext/iodine/iodine_listener.h +263 -0
  39. data/ext/iodine/iodine_log.h +297 -0
  40. data/ext/iodine/iodine_minimap.h +543 -0
  41. data/ext/iodine/iodine_mustache.h +461 -4
  42. data/ext/iodine/iodine_pubsub_eng.h +729 -0
  43. data/ext/iodine/iodine_pubsub_history.h +547 -0
  44. data/ext/iodine/iodine_pubsub_msg.h +228 -0
  45. data/ext/iodine/iodine_redis.h +322 -0
  46. data/ext/iodine/iodine_store.h +418 -16
  47. data/ext/iodine/iodine_threads.h +279 -0
  48. data/ext/iodine/iodine_tls.h +515 -6
  49. data/ext/iodine/iodine_utils.h +923 -0
  50. data/iodine.gemspec +37 -42
  51. data/lib/iodine/benchmark.rb +1690 -0
  52. data/lib/iodine/documentation.rb +3085 -0
  53. data/lib/iodine/version.rb +17 -1
  54. data/lib/iodine.rb +54 -255
  55. data/logo.svg +36 -0
  56. data/mustache.md +55 -0
  57. data/sig/iodine.rbs +4 -0
  58. metadata +58 -184
  59. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  60. data/.github/workflows/ruby.yml +0 -42
  61. data/.gitignore +0 -21
  62. data/LIMITS.md +0 -41
  63. data/SPEC-PubSub-Draft.md +0 -159
  64. data/SPEC-WebSocket-Draft.md +0 -239
  65. data/bin/console +0 -22
  66. data/bin/info.md +0 -353
  67. data/bin/mustache_bench.rb +0 -100
  68. data/bin/poc/Gemfile.lock +0 -23
  69. data/bin/poc/README.md +0 -37
  70. data/bin/poc/config.ru +0 -66
  71. data/bin/poc/gemfile +0 -1
  72. data/bin/poc/www/index.html +0 -57
  73. data/examples/async_task.ru +0 -92
  74. data/examples/bates/README.md +0 -3
  75. data/examples/bates/config.ru +0 -342
  76. data/examples/bates/david+bold.pdf +0 -0
  77. data/examples/bates/public/drop-pdf.png +0 -0
  78. data/examples/bates/public/index.html +0 -600
  79. data/examples/echo.ru +0 -59
  80. data/examples/etag.ru +0 -16
  81. data/examples/hello.ru +0 -29
  82. data/examples/pubsub_engine.ru +0 -81
  83. data/examples/rack3.ru +0 -12
  84. data/examples/redis.ru +0 -70
  85. data/examples/shootout.ru +0 -73
  86. data/examples/sub-protocols.ru +0 -90
  87. data/examples/tcp_client.rb +0 -66
  88. data/examples/x-sendfile.ru +0 -14
  89. data/ext/iodine/fio.c +0 -11995
  90. data/ext/iodine/fio.h +0 -6371
  91. data/ext/iodine/fio_cli.c +0 -431
  92. data/ext/iodine/fio_cli.h +0 -189
  93. data/ext/iodine/fio_json_parser.h +0 -688
  94. data/ext/iodine/fio_siphash.c +0 -157
  95. data/ext/iodine/fio_siphash.h +0 -37
  96. data/ext/iodine/fio_tls.h +0 -129
  97. data/ext/iodine/fio_tls_missing.c +0 -649
  98. data/ext/iodine/fio_tls_openssl.c +0 -1106
  99. data/ext/iodine/fio_tmpfile.h +0 -50
  100. data/ext/iodine/fiobj.h +0 -44
  101. data/ext/iodine/fiobj4fio.h +0 -21
  102. data/ext/iodine/fiobj_ary.c +0 -333
  103. data/ext/iodine/fiobj_ary.h +0 -139
  104. data/ext/iodine/fiobj_data.c +0 -1187
  105. data/ext/iodine/fiobj_data.h +0 -167
  106. data/ext/iodine/fiobj_hash.c +0 -409
  107. data/ext/iodine/fiobj_hash.h +0 -176
  108. data/ext/iodine/fiobj_json.c +0 -622
  109. data/ext/iodine/fiobj_json.h +0 -68
  110. data/ext/iodine/fiobj_mem.h +0 -71
  111. data/ext/iodine/fiobj_mustache.c +0 -317
  112. data/ext/iodine/fiobj_mustache.h +0 -62
  113. data/ext/iodine/fiobj_numbers.c +0 -344
  114. data/ext/iodine/fiobj_numbers.h +0 -127
  115. data/ext/iodine/fiobj_str.c +0 -433
  116. data/ext/iodine/fiobj_str.h +0 -172
  117. data/ext/iodine/fiobject.c +0 -620
  118. data/ext/iodine/fiobject.h +0 -655
  119. data/ext/iodine/hpack.h +0 -1923
  120. data/ext/iodine/http.c +0 -2743
  121. data/ext/iodine/http.h +0 -1002
  122. data/ext/iodine/http1.c +0 -910
  123. data/ext/iodine/http1.h +0 -29
  124. data/ext/iodine/http1_parser.h +0 -878
  125. data/ext/iodine/http_internal.c +0 -1279
  126. data/ext/iodine/http_internal.h +0 -237
  127. data/ext/iodine/http_mime_parser.h +0 -350
  128. data/ext/iodine/iodine_caller.c +0 -230
  129. data/ext/iodine/iodine_connection.c +0 -941
  130. data/ext/iodine/iodine_defer.c +0 -420
  131. data/ext/iodine/iodine_fiobj2rb.h +0 -120
  132. data/ext/iodine/iodine_helpers.c +0 -282
  133. data/ext/iodine/iodine_helpers.h +0 -12
  134. data/ext/iodine/iodine_http.c +0 -1181
  135. data/ext/iodine/iodine_http.h +0 -23
  136. data/ext/iodine/iodine_json.c +0 -302
  137. data/ext/iodine/iodine_mustache.c +0 -567
  138. data/ext/iodine/iodine_pubsub.c +0 -580
  139. data/ext/iodine/iodine_pubsub.h +0 -26
  140. data/ext/iodine/iodine_rack_io.c +0 -273
  141. data/ext/iodine/iodine_rack_io.h +0 -20
  142. data/ext/iodine/iodine_store.c +0 -148
  143. data/ext/iodine/iodine_tcp.c +0 -346
  144. data/ext/iodine/iodine_tcp.h +0 -13
  145. data/ext/iodine/iodine_tls.c +0 -261
  146. data/ext/iodine/mustache_parser.h +0 -1546
  147. data/ext/iodine/redis_engine.c +0 -957
  148. data/ext/iodine/redis_engine.h +0 -79
  149. data/ext/iodine/resp_parser.h +0 -317
  150. data/ext/iodine/websocket_parser.h +0 -506
  151. data/ext/iodine/websockets.c +0 -743
  152. data/ext/iodine/websockets.h +0 -185
  153. data/lib/iodine/connection.rb +0 -61
  154. data/lib/iodine/json.rb +0 -42
  155. data/lib/iodine/mustache.rb +0 -113
  156. data/lib/iodine/pubsub.rb +0 -55
  157. data/lib/iodine/rack_utils.rb +0 -43
  158. data/lib/iodine/tls.rb +0 -16
  159. data/lib/rack/handler/iodine.rb +0 -33
  160. data/logo.png +0 -0
@@ -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
- }
@@ -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
@@ -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