iodine 0.7.13 → 0.7.14
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +78 -12
- data/ext/iodine/fio.c +6 -4
- data/ext/iodine/fio.h +13 -3
- data/ext/iodine/iodine_connection.c +61 -0
- data/ext/iodine/iodine_store.c +1 -1
- data/ext/iodine/iodine_tcp.c +27 -6
- data/ext/iodine/mustache_parser.h +23 -61
- data/lib/iodine/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d3f8b237b265c8151aa3fe966a9c863fa3c053e4fd97289cfe498b77818e564
|
4
|
+
data.tar.gz: 389b80f9feff3abf97ba7fdaf08d0a2986521093fb0365ab10c5e4b3af3b2de5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8b220410c1ebdecf221bde09d48f64ed0c69dc50a0f0ecba69a3e530dc2b9cace643c9a1d8bc7ce73ecf9f1274ffc1162a21db6db273673de84318f9ea81cb3
|
7
|
+
data.tar.gz: 94f71023813326cb0edfabecbbd862e566ec37882de35ca6b6f14a2759088b54ed52df9bb943888b3c58f3441881c67a4c82197550272593050eaca6fc66b3e8
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,16 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
6
6
|
|
7
7
|
## Changes:
|
8
8
|
|
9
|
+
#### Change log v.0.7.14
|
10
|
+
|
11
|
+
**Fix**: (`facil.io`) fixed superfluous ping event.
|
12
|
+
|
13
|
+
**Fix**: (`iodine_tcp`) fixed responsiveness to the argument name `timeout` (a spelling mistake was testing for `timout`).
|
14
|
+
|
15
|
+
**Fix**: (`iodine_store`) fixed missing EOL marker in DEBUG messages when reporting iodine's GC guard activity.
|
16
|
+
|
17
|
+
**Update**: (`iodine`) added support for dynamic (hot) connection callback switching.
|
18
|
+
|
9
19
|
#### Change log v.0.7.13
|
10
20
|
|
11
21
|
**Fix**: (`mustache`) added support for padding in template partials.
|
data/README.md
CHANGED
@@ -427,6 +427,8 @@ Iodine is written in C and allows some compile-time customizations, such as:
|
|
427
427
|
|
428
428
|
* `FIO_ENGINE_POLL` - prefer the `poll` system call over `epoll` or `kqueue` (not recommended).
|
429
429
|
|
430
|
+
* `FIO_LOG_LENGTH_LIMIT` - sets the limit on iodine's logging messages (uses stack memory, so limits must be reasonable. Defaults to 2048.
|
431
|
+
|
430
432
|
These options can be used, for example, like so:
|
431
433
|
|
432
434
|
```bash
|
@@ -501,35 +503,99 @@ Iodine allows custom TCP/IP server authoring, for those cases where we need raw
|
|
501
503
|
Here's a short and sweet echo server - No HTTP, just use `telnet`:
|
502
504
|
|
503
505
|
```ruby
|
504
|
-
|
505
506
|
require 'iodine'
|
506
507
|
|
507
508
|
# an echo protocol with asynchronous notifications.
|
508
509
|
class EchoProtocol
|
509
510
|
# `on_message` is an optional alternative to the `on_data` callback.
|
510
511
|
# `on_message` has a 1Kb buffer that recycles itself for memory optimization.
|
511
|
-
def on_message buffer
|
512
|
+
def on_message client, buffer
|
512
513
|
# writing will never block and will use a buffer written in C when needed.
|
513
|
-
write buffer
|
514
|
+
client.write buffer
|
514
515
|
# close will be performed only once all the data in the write buffer
|
515
516
|
# was sent. use `force_close` to close early.
|
516
|
-
close if buffer =~ /^bye[\r\n]/i
|
517
|
-
#
|
518
|
-
|
519
|
-
# run asynchronous tasks with ease
|
520
|
-
run do
|
517
|
+
client.close if buffer =~ /^bye[\r\n]/i
|
518
|
+
# run asynchronous tasks using the thread pool
|
519
|
+
Iodine.run do
|
521
520
|
sleep 1
|
522
|
-
puts "Echoed data: #{
|
521
|
+
puts "Echoed data: #{buffer}"
|
523
522
|
end
|
524
523
|
end
|
525
524
|
end
|
526
525
|
|
527
526
|
# listen on port 3000 for the echo protocol.
|
528
|
-
Iodine.listen 3000
|
529
|
-
Iodine.threads =
|
530
|
-
Iodine.
|
527
|
+
Iodine.listen(port: "3000") { EchoProtocol.new }
|
528
|
+
Iodine.threads = 4
|
529
|
+
Iodine.workers = 1
|
531
530
|
Iodine.start
|
531
|
+
```
|
532
|
+
|
533
|
+
Or a nice plain text chat room (connect using `telnet` or `nc` ):
|
534
|
+
|
535
|
+
```ruby
|
536
|
+
require 'iodine'
|
532
537
|
|
538
|
+
# a chat protocol with asynchronous notifications.
|
539
|
+
class ChatProtocol
|
540
|
+
def initialize nickname = "guest"
|
541
|
+
@nickname = nickname
|
542
|
+
end
|
543
|
+
def on_open client
|
544
|
+
client.subscribe :chat
|
545
|
+
client.publish :chat, "#{@nickname} joined chat.\n"
|
546
|
+
client.timeout = 40
|
547
|
+
end
|
548
|
+
def on_close client
|
549
|
+
client.publish :chat, "#{@nickname} left chat.\n"
|
550
|
+
end
|
551
|
+
def on_shutdown client
|
552
|
+
client.write "Server is shutting down... try reconnecting later.\n"
|
553
|
+
end
|
554
|
+
def on_message client, buffer
|
555
|
+
if(buffer[-1] == "\n")
|
556
|
+
client.publish :chat, "#{@nickname}: #{buffer}"
|
557
|
+
else
|
558
|
+
client.publish :chat, "#{@nickname}: #{buffer}\n"
|
559
|
+
end
|
560
|
+
# close will be performed only once all the data in the outgoing buffer
|
561
|
+
client.close if buffer =~ /^bye[\r\n]/i
|
562
|
+
end
|
563
|
+
def ping client
|
564
|
+
client.write "(ping) Are you there, #{@nickname}...?\n"
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
#an initial login protocol
|
569
|
+
class LoginProtocol
|
570
|
+
def on_open client
|
571
|
+
client.write "Enter nickname to log in to chat room:\n"
|
572
|
+
client.timeout = 10
|
573
|
+
end
|
574
|
+
def ping client
|
575
|
+
client.write "Time's up... goodbye.\n"
|
576
|
+
client.close
|
577
|
+
end
|
578
|
+
def on_message client, buffer
|
579
|
+
# validate nickname and switch connection callback to ChatProtocol
|
580
|
+
nickname = buffer.split("\n")[0]
|
581
|
+
while (nickname && nickname.length() > 0 && (nickname[-1] == '\n' || nickname[-1] == '\r'))
|
582
|
+
nickname = nickname.slice(0, nickname.length() -1)
|
583
|
+
end
|
584
|
+
if(nickname && nickname.length() > 0 && buffer.split("\n").length() == 1)
|
585
|
+
chat = ChatProtocol.new(nickname)
|
586
|
+
client.handler = chat
|
587
|
+
else
|
588
|
+
client.write "Nickname error, try again.\n"
|
589
|
+
on_open client
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
# listen on port 3000
|
595
|
+
Iodine.listen(port: 3000) { LoginProtocol.new }
|
596
|
+
Iodine.threads = 1
|
597
|
+
Iodine.workers = 1
|
598
|
+
Iodine.start
|
533
599
|
```
|
534
600
|
|
535
601
|
### Why not EventMachine?
|
data/ext/iodine/fio.c
CHANGED
@@ -2009,7 +2009,7 @@ static void mock_ping(intptr_t uuid, fio_protocol_s *protocol) {
|
|
2009
2009
|
static void mock_ping2(intptr_t uuid, fio_protocol_s *protocol) {
|
2010
2010
|
(void)protocol;
|
2011
2011
|
|
2012
|
-
|
2012
|
+
touchfd(fio_uuid2fd(uuid));
|
2013
2013
|
if (uuid_data(uuid).timeout == 255)
|
2014
2014
|
return;
|
2015
2015
|
protocol->ping = mock_ping;
|
@@ -2046,7 +2046,7 @@ static void deferred_on_shutdown(void *arg, void *arg2) {
|
|
2046
2046
|
return;
|
2047
2047
|
goto postpone;
|
2048
2048
|
}
|
2049
|
-
|
2049
|
+
touchfd(fio_uuid2fd(arg));
|
2050
2050
|
uint8_t r = pr->on_shutdown ? pr->on_shutdown((intptr_t)arg, pr) : 0;
|
2051
2051
|
if (r) {
|
2052
2052
|
if (r == 255) {
|
@@ -2733,6 +2733,7 @@ ssize_t fio_write2_fn(intptr_t uuid, fio_write_args_s options) {
|
|
2733
2733
|
fio_unlock(&uuid_data(uuid).sock_lock);
|
2734
2734
|
|
2735
2735
|
if (was_empty) {
|
2736
|
+
touchfd(fio_uuid2fd(uuid));
|
2736
2737
|
fio_defer_push_urgent(deferred_on_ready, (void *)uuid, NULL);
|
2737
2738
|
}
|
2738
2739
|
return 0;
|
@@ -2893,6 +2894,7 @@ test_errno:
|
|
2893
2894
|
|
2894
2895
|
return -1;
|
2895
2896
|
flushed:
|
2897
|
+
touchfd(fio_uuid2fd(uuid));
|
2896
2898
|
fio_unlock(&uuid_data(uuid).sock_lock);
|
2897
2899
|
return 1;
|
2898
2900
|
}
|
@@ -3045,7 +3047,7 @@ static int fio_attach__internal(void *uuid_, void *protocol_) {
|
|
3045
3047
|
fio_protocol_s *old_pr = uuid_data(uuid).protocol;
|
3046
3048
|
uuid_data(uuid).open = 1;
|
3047
3049
|
uuid_data(uuid).protocol = protocol;
|
3048
|
-
|
3050
|
+
touchfd(fio_uuid2fd(uuid));
|
3049
3051
|
fio_unlock(&uuid_data(uuid).protocol_lock);
|
3050
3052
|
if (old_pr) {
|
3051
3053
|
fio_defer_push_task(deferred_on_close, (void *)uuid, old_pr);
|
@@ -3082,7 +3084,7 @@ void fio_attach_fd(int fd, fio_protocol_s *protocol) {
|
|
3082
3084
|
/** Sets a timeout for a specific connection (only when running and valid). */
|
3083
3085
|
void fio_timeout_set(intptr_t uuid, uint8_t timeout) {
|
3084
3086
|
if (uuid_is_valid(uuid)) {
|
3085
|
-
|
3087
|
+
touchfd(fio_uuid2fd(uuid));
|
3086
3088
|
uuid_data(uuid).timeout = timeout;
|
3087
3089
|
} else {
|
3088
3090
|
FIO_LOG_DEBUG("Called fio_timeout_set for invalid uuid %p", (void *)uuid);
|
data/ext/iodine/fio.h
CHANGED
@@ -180,6 +180,15 @@ Version and helper macros
|
|
180
180
|
#define FIO_PUBSUB_SUPPORT 1
|
181
181
|
#endif
|
182
182
|
|
183
|
+
#ifndef FIO_LOG_LENGTH_LIMIT
|
184
|
+
/**
|
185
|
+
* Since logging uses stack memory rather than dynamic allocation, it's memory
|
186
|
+
* usage must be limited to avoid exploding the stack. The following sets the
|
187
|
+
* memory used for a logging event.
|
188
|
+
*/
|
189
|
+
#define FIO_LOG_LENGTH_LIMIT 2048
|
190
|
+
#endif
|
191
|
+
|
183
192
|
#ifndef FIO_IGNORE_MACRO
|
184
193
|
/**
|
185
194
|
* This is used internally to ignore macros that shadow functions (avoiding
|
@@ -427,9 +436,10 @@ extern int FIO_LOG_LEVEL;
|
|
427
436
|
#define FIO_LOG_PRINT(level, ...) \
|
428
437
|
do { \
|
429
438
|
if (level <= FIO_LOG_LEVEL) { \
|
430
|
-
char tmp___log[
|
431
|
-
int len___log =
|
432
|
-
|
439
|
+
char tmp___log[FIO_LOG_LENGTH_LIMIT]; \
|
440
|
+
int len___log = \
|
441
|
+
snprintf(tmp___log, FIO_LOG_LENGTH_LIMIT - 2, __VA_ARGS__); \
|
442
|
+
if (len___log <= 0 || len___log >= FIO_LOG_LENGTH_LIMIT - 2) { \
|
433
443
|
fwrite("ERROR: log line output too long (can't write).", 46, 1, \
|
434
444
|
stderr); \
|
435
445
|
break; \
|
@@ -319,6 +319,63 @@ static VALUE iodine_connection_env(VALUE self) {
|
|
319
319
|
return Qnil;
|
320
320
|
}
|
321
321
|
|
322
|
+
/**
|
323
|
+
* Returns the client's current callback object.
|
324
|
+
*/
|
325
|
+
static VALUE iodine_connection_handler_get(VALUE self) {
|
326
|
+
iodine_connection_data_s *data = iodine_connection_validate_data(self);
|
327
|
+
if (!data) {
|
328
|
+
FIO_LOG_DEBUG("(iodine) requested connection handler for "
|
329
|
+
"an invalid connection: %p",
|
330
|
+
(void *)self);
|
331
|
+
return Qnil;
|
332
|
+
}
|
333
|
+
return data->info.handler;
|
334
|
+
}
|
335
|
+
|
336
|
+
// clang-format off
|
337
|
+
/**
|
338
|
+
* Sets the client's callback object, so future events will use the new object's callbacks.
|
339
|
+
*
|
340
|
+
* @Note this will fire the `on_close` callback in the old handler and the `on_open` callback on the new handler. However, existing subscriptions will remain intact.
|
341
|
+
*/
|
342
|
+
static VALUE iodine_connection_handler_set(VALUE self, VALUE handler) {
|
343
|
+
// clang-format on
|
344
|
+
iodine_connection_data_s *data = iodine_connection_validate_data(self);
|
345
|
+
if (!data) {
|
346
|
+
FIO_LOG_DEBUG("(iodine) attempted to set a connection handler for "
|
347
|
+
"an invalid connection: %p",
|
348
|
+
(void *)self);
|
349
|
+
return Qnil;
|
350
|
+
}
|
351
|
+
if (handler == Qnil || handler == Qfalse) {
|
352
|
+
FIO_LOG_DEBUG(
|
353
|
+
"(iodine) called client.handler = nil, closing connection: %p",
|
354
|
+
(void *)self);
|
355
|
+
iodine_connection_close(self);
|
356
|
+
return Qnil;
|
357
|
+
}
|
358
|
+
if (data->info.handler != handler) {
|
359
|
+
uint8_t answers_on_open = (rb_respond_to(handler, on_open_id) != 0);
|
360
|
+
if(data->answers_on_close)
|
361
|
+
IodineCaller.call2(data->info.handler, on_close_id, 1, &self);
|
362
|
+
fio_lock(&data->lock);
|
363
|
+
data->info.handler = handler;
|
364
|
+
data->answers_on_open = answers_on_open,
|
365
|
+
data->answers_on_message = (rb_respond_to(handler, on_message_id) != 0),
|
366
|
+
data->answers_ping = (rb_respond_to(handler, ping_id) != 0),
|
367
|
+
data->answers_on_drained = (rb_respond_to(handler, on_drained_id) != 0),
|
368
|
+
data->answers_on_shutdown = (rb_respond_to(handler, on_shutdown_id) != 0),
|
369
|
+
data->answers_on_close = (rb_respond_to(handler, on_close_id) != 0),
|
370
|
+
fio_unlock(&data->lock);
|
371
|
+
if (answers_on_open) {
|
372
|
+
iodine_connection_fire_event(self, IODINE_CONNECTION_ON_OPEN, Qnil);
|
373
|
+
}
|
374
|
+
FIO_LOG_DEBUG("(iodine) switched handlers for connection: %p",
|
375
|
+
(void *)self);
|
376
|
+
}
|
377
|
+
return handler;
|
378
|
+
}
|
322
379
|
/* *****************************************************************************
|
323
380
|
Pub/Sub Callbacks (internal implementation)
|
324
381
|
***************************************************************************** */
|
@@ -837,6 +894,10 @@ void iodine_connection_init(void) {
|
|
837
894
|
1);
|
838
895
|
rb_define_method(ConnectionKlass, "env", iodine_connection_env, 0);
|
839
896
|
|
897
|
+
rb_define_method(ConnectionKlass, "handler", iodine_connection_handler_get,
|
898
|
+
0);
|
899
|
+
rb_define_method(ConnectionKlass, "handler=", iodine_connection_handler_set,
|
900
|
+
1);
|
840
901
|
rb_define_method(ConnectionKlass, "subscribe", iodine_pubsub_subscribe, -1);
|
841
902
|
rb_define_method(ConnectionKlass, "unsubscribe", iodine_pubsub_unsubscribe,
|
842
903
|
1);
|
data/ext/iodine/iodine_store.c
CHANGED
@@ -65,7 +65,7 @@ static void storage_print(void) {
|
|
65
65
|
fprintf(stderr, "Total of %" PRIuPTR " objects protected form GC\n", index);
|
66
66
|
fprintf(stderr,
|
67
67
|
"Storage uses %" PRIuPTR " Hash bins for %" PRIuPTR " objects\n"
|
68
|
-
"The largest collection was %zu objects
|
68
|
+
"The largest collection was %zu objects.\n",
|
69
69
|
iodine_storage.capa, iodine_storage.count, iodine_storage_count_max);
|
70
70
|
fio_unlock(&iodine_storage_lock);
|
71
71
|
}
|
data/ext/iodine/iodine_tcp.c
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
#include <ruby/encoding.h>
|
3
3
|
#include <ruby/io.h>
|
4
4
|
|
5
|
+
#define FIO_INCLUDE_STR
|
5
6
|
#include "fio.h"
|
6
7
|
|
7
8
|
/* *****************************************************************************
|
@@ -40,6 +41,7 @@ static void *iodine_tcp_on_data_in_GIL(void *b_) {
|
|
40
41
|
iodine_buffer_s *b = b_;
|
41
42
|
if (!b) {
|
42
43
|
FIO_LOG_FATAL("(iodine->tcp/ip->on_data->GIL) WTF?!\n");
|
44
|
+
exit(-1);
|
43
45
|
}
|
44
46
|
VALUE data = IodineStore.add(rb_str_new(b->buffer, b->len));
|
45
47
|
rb_enc_associate(data, IodineBinaryEncoding);
|
@@ -148,7 +150,7 @@ The {listen} method instructs iodine to listen to incoming connections using eit
|
|
148
150
|
|
149
151
|
The method accepts a single Hash argument with the following optional keys:
|
150
152
|
|
151
|
-
:port :: The port to listen to, deafults to
|
153
|
+
:port :: The port to listen to, deafults to nil (using a Unix socket)
|
152
154
|
: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).
|
153
155
|
:handler :: An object that answers the `call` method (i.e., a Proc).
|
154
156
|
|
@@ -217,6 +219,7 @@ static VALUE iodine_tcp_listen(VALUE self, VALUE args) {
|
|
217
219
|
VALUE rb_port = rb_hash_aref(args, port_id);
|
218
220
|
VALUE rb_address = rb_hash_aref(args, address_id);
|
219
221
|
VALUE rb_handler = rb_hash_aref(args, handler_id);
|
222
|
+
fio_str_s port = FIO_STR_INIT;
|
220
223
|
if (rb_handler == Qnil || rb_handler == Qfalse || rb_handler == Qtrue) {
|
221
224
|
rb_need_block();
|
222
225
|
rb_handler = rb_block_proc();
|
@@ -225,10 +228,19 @@ static VALUE iodine_tcp_listen(VALUE self, VALUE args) {
|
|
225
228
|
if (rb_address != Qnil) {
|
226
229
|
Check_Type(rb_address, T_STRING);
|
227
230
|
}
|
231
|
+
|
228
232
|
if (rb_port != Qnil) {
|
229
|
-
|
233
|
+
if (rb_port == Qfalse) {
|
234
|
+
fio_str_write_i(&port, 0);
|
235
|
+
} else if (RB_TYPE_P(rb_port, T_STRING))
|
236
|
+
fio_str_write(&port, RSTRING_PTR(rb_port), RSTRING_LEN(rb_port));
|
237
|
+
else if (RB_TYPE_P(rb_port, T_FIXNUM))
|
238
|
+
fio_str_write_i(&port, FIX2LONG(rb_port));
|
239
|
+
else
|
240
|
+
rb_raise(rb_eTypeError,
|
241
|
+
"The `port` property MUST be either a String or a Number");
|
230
242
|
}
|
231
|
-
if (fio_listen(.port = (
|
243
|
+
if (fio_listen(.port = fio_str_info(&port).data,
|
232
244
|
.address =
|
233
245
|
(rb_address == Qnil ? NULL : StringValueCStr(rb_address)),
|
234
246
|
.on_open = iodine_tcp_on_open,
|
@@ -269,6 +281,7 @@ static VALUE iodine_tcp_connect(VALUE self, VALUE args) {
|
|
269
281
|
VALUE rb_handler = rb_hash_aref(args, handler_id);
|
270
282
|
VALUE rb_timeout = rb_hash_aref(args, timeout_id);
|
271
283
|
uint8_t timeout = 0;
|
284
|
+
fio_str_s port = FIO_STR_INIT;
|
272
285
|
if (rb_handler == Qnil || rb_handler == Qfalse || rb_handler == Qtrue) {
|
273
286
|
rb_raise(rb_eArgError, "A callback object (:handler) must be provided.");
|
274
287
|
}
|
@@ -277,13 +290,21 @@ static VALUE iodine_tcp_connect(VALUE self, VALUE args) {
|
|
277
290
|
Check_Type(rb_address, T_STRING);
|
278
291
|
}
|
279
292
|
if (rb_port != Qnil) {
|
280
|
-
|
293
|
+
if (rb_port == Qfalse) {
|
294
|
+
fio_str_write_i(&port, 0);
|
295
|
+
} else if (RB_TYPE_P(rb_port, T_STRING))
|
296
|
+
fio_str_write(&port, RSTRING_PTR(rb_port), RSTRING_LEN(rb_port));
|
297
|
+
else if (RB_TYPE_P(rb_port, T_FIXNUM))
|
298
|
+
fio_str_write_i(&port, FIX2LONG(rb_port));
|
299
|
+
else
|
300
|
+
rb_raise(rb_eTypeError,
|
301
|
+
"The `port` property MUST be either a String or a Number");
|
281
302
|
}
|
282
303
|
if (rb_timeout != Qnil) {
|
283
304
|
Check_Type(rb_timeout, T_FIXNUM);
|
284
305
|
timeout = NUM2USHORT(rb_timeout);
|
285
306
|
}
|
286
|
-
fio_connect(.port = (
|
307
|
+
fio_connect(.port = fio_str_info(&port).data,
|
287
308
|
.address =
|
288
309
|
(rb_address == Qnil ? NULL : StringValueCStr(rb_address)),
|
289
310
|
.on_connect = iodine_tcp_on_connect,
|
@@ -332,7 +353,7 @@ void iodine_init_tcp_connections(void) {
|
|
332
353
|
port_id = IodineStore.add(rb_id2sym(rb_intern("port")));
|
333
354
|
address_id = IodineStore.add(rb_id2sym(rb_intern("address")));
|
334
355
|
handler_id = IodineStore.add(rb_id2sym(rb_intern("handler")));
|
335
|
-
timeout_id = IodineStore.add(rb_id2sym(rb_intern("
|
356
|
+
timeout_id = IodineStore.add(rb_id2sym(rb_intern("timeout")));
|
336
357
|
on_closed_id = rb_intern("on_closed");
|
337
358
|
|
338
359
|
IodineBinaryEncoding = rb_enc_find("binary");
|
@@ -8,47 +8,6 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
8
8
|
/**
|
9
9
|
* A mustache parser using a callback systems that allows this implementation to
|
10
10
|
* be framework agnostic (i.e., can be used with any JSON library).
|
11
|
-
*
|
12
|
-
* When including the mustache parser within an iumplementation file,
|
13
|
-
* `INCLUDE_MUSTACHE_IMPLEMENTATION` must be defined as 1. This allows the
|
14
|
-
* header's types to be exposed within a containing header.
|
15
|
-
*
|
16
|
-
* The API has three functions:
|
17
|
-
*
|
18
|
-
* 1. `mustache_load` loads a template file, converting it to instruction data.
|
19
|
-
* 2. `mustache_build` calls any callbacks according to the loaded instructions.
|
20
|
-
* 3. `mustache_free` frees the instruction and data memory (the template).
|
21
|
-
*
|
22
|
-
* The template is loaded and converted to an instruction array using
|
23
|
-
* `mustache_load`. This loads any nested templates / partials as well.
|
24
|
-
*
|
25
|
-
* The resulting instruction array (`mustache_s *`) is composed of three memory
|
26
|
-
* segments: header segment, instruction array segment and data segment.
|
27
|
-
*
|
28
|
-
* The instruction array (`mustache_s *`) can be used to build actual output
|
29
|
-
* data using the `mustache_build` function.
|
30
|
-
*
|
31
|
-
* The `mustache_build` function accepts two opaque pointers for user data
|
32
|
-
* (`udata1` and `udata2`) that can be used by the callbacks for data input and
|
33
|
-
* data output.
|
34
|
-
*
|
35
|
-
* The `mustache_build` function is thread safe and many threads can build
|
36
|
-
* content based on the same template.
|
37
|
-
*
|
38
|
-
* While the build function is performed, the following callback might be
|
39
|
-
* called:
|
40
|
-
*
|
41
|
-
* * `mustache_on_arg` - called to output an argument's value .
|
42
|
-
* * `mustache_on_text` - called to output raw text.
|
43
|
-
* * `mustache_on_section_test` - called when a section is tested for validity.
|
44
|
-
* * `mustache_on_section_start` - called when entering a named section.
|
45
|
-
* * `mustache_on_formatting_error` - called when a formatting error occurred.
|
46
|
-
*
|
47
|
-
* Once the template is no longer needed, it's easy to free the template using
|
48
|
-
* the `mustache_free` function (which, at the moment, simply calls `free`).
|
49
|
-
*
|
50
|
-
* For details about mustache templating scheme, see: https://mustache.github.io
|
51
|
-
*
|
52
11
|
*/
|
53
12
|
#define H_MUSTACHE_LOADR_H
|
54
13
|
|
@@ -524,26 +483,28 @@ static inline int mustache__write_padding(mustache__builder_stack_s *s) {
|
|
524
483
|
*/
|
525
484
|
static int mustache__write_escaped(mustache__builder_stack_s *s, char *text,
|
526
485
|
uint32_t len) {
|
486
|
+
#define MUSTACHE_ESCAPE_BUFFER_SIZE 4096
|
527
487
|
/** HTML ecape table, created using the following Ruby Script:
|
528
488
|
|
529
|
-
a = (0..255).to_a.map {|i| i.chr }
|
530
|
-
|
531
|
-
|
532
|
-
('
|
533
|
-
('
|
534
|
-
|
535
|
-
a['
|
536
|
-
a['
|
537
|
-
a['
|
538
|
-
a['"
|
539
|
-
a[
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
489
|
+
a = (0..255).to_a.map {|i| i.chr }
|
490
|
+
100.times {|i| a[i] = "&\##{i.to_s(10)};"}
|
491
|
+
('a'.ord..'z'.ord).each {|i| a[i] = i.chr }
|
492
|
+
('A'.ord..'Z'.ord).each {|i| a[i] = i.chr }
|
493
|
+
('0'.ord..'9'.ord).each {|i| a[i] = i.chr }
|
494
|
+
a['<'.ord] = "<"
|
495
|
+
a['>'.ord] = ">"
|
496
|
+
a['&'.ord] = "&"
|
497
|
+
a['"'.ord] = """
|
498
|
+
a["\'".ord] = "'"
|
499
|
+
a['|'.ord] = "&\##{'|'.ord.to_s(10)};"
|
500
|
+
|
501
|
+
b = a.map {|s| s.length }
|
502
|
+
puts "static char *html_escape_strs[] = {",
|
503
|
+
a.to_s.slice(1..-2) ,"};",
|
504
|
+
"static uint8_t html_escape_len[] = {",
|
505
|
+
b.to_s.slice(1..-2),"};"
|
545
506
|
*/
|
546
|
-
static char *html_escape_strs[] = {
|
507
|
+
static const char *html_escape_strs[] = {
|
547
508
|
"�", "", "", "", "", "", "", "",
|
548
509
|
"", "	", " ", "", "", " ", "", "",
|
549
510
|
"", "", "", "", "", "", "", "",
|
@@ -576,7 +537,7 @@ puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
|
|
576
537
|
"\xE8", "\xE9", "\xEA", "\xEB", "\xEC", "\xED", "\xEE", "\xEF",
|
577
538
|
"\xF0", "\xF1", "\xF2", "\xF3", "\xF4", "\xF5", "\xF6", "\xF7",
|
578
539
|
"\xF8", "\xF9", "\xFA", "\xFB", "\xFC", "\xFD", "\xFE", "\xFF"};
|
579
|
-
static uint8_t html_escape_len[] = {
|
540
|
+
static const uint8_t html_escape_len[] = {
|
580
541
|
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
581
542
|
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5,
|
582
543
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 4, 5, 4, 5, 5, 1, 1, 1, 1, 1, 1, 1,
|
@@ -588,7 +549,7 @@ puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
|
|
588
549
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
589
550
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
590
551
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
591
|
-
char buffer[
|
552
|
+
char buffer[MUSTACHE_ESCAPE_BUFFER_SIZE];
|
592
553
|
size_t pos = 0;
|
593
554
|
const char *end = text + len;
|
594
555
|
while (text < end) {
|
@@ -604,7 +565,7 @@ puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
|
|
604
565
|
memcpy(buffer + pos, html_escape_strs[(uint8_t)text[0]],
|
605
566
|
html_escape_len[(uint8_t)text[0]]);
|
606
567
|
pos += html_escape_len[(uint8_t)text[0]];
|
607
|
-
if (pos >=
|
568
|
+
if (pos >= (MUSTACHE_ESCAPE_BUFFER_SIZE - 6)) {
|
608
569
|
buffer[pos] = 0;
|
609
570
|
if (mustache_on_text(&s->stack[s->index].sec, buffer, pos) == -1)
|
610
571
|
return -1;
|
@@ -619,6 +580,7 @@ puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
|
|
619
580
|
return -1;
|
620
581
|
}
|
621
582
|
return 0;
|
583
|
+
#undef MUSTACHE_ESCAPE_BUFFER_SIZE
|
622
584
|
}
|
623
585
|
/**
|
624
586
|
* This helper function should be used to write text to the output
|
data/lib/iodine/version.rb
CHANGED
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.7.
|
4
|
+
version: 0.7.14
|
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-12-
|
11
|
+
date: 2018-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -210,7 +210,7 @@ licenses:
|
|
210
210
|
- MIT
|
211
211
|
metadata:
|
212
212
|
allowed_push_host: https://rubygems.org
|
213
|
-
post_install_message: 'Thank you for installing Iodine 0.7.
|
213
|
+
post_install_message: 'Thank you for installing Iodine 0.7.14.
|
214
214
|
|
215
215
|
'
|
216
216
|
rdoc_options: []
|