iodine 0.6.5 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +4 -4
- data/SPEC-Websocket-Draft.md +3 -6
- data/bin/mustache.rb +128 -0
- data/examples/test_template.mustache +16 -0
- data/ext/iodine/fio.c +9397 -0
- data/ext/iodine/fio.h +4723 -0
- data/ext/iodine/fio_ary.h +353 -54
- data/ext/iodine/fio_cli.c +351 -361
- data/ext/iodine/fio_cli.h +84 -105
- data/ext/iodine/fio_hashmap.h +70 -16
- data/ext/iodine/fio_json_parser.h +35 -24
- data/ext/iodine/fio_siphash.c +104 -4
- data/ext/iodine/fio_siphash.h +18 -2
- data/ext/iodine/fio_str.h +1218 -0
- data/ext/iodine/fio_tmpfile.h +1 -1
- data/ext/iodine/fiobj.h +13 -8
- data/ext/iodine/fiobj4sock.h +6 -8
- data/ext/iodine/fiobj_ary.c +107 -17
- data/ext/iodine/fiobj_ary.h +36 -4
- data/ext/iodine/fiobj_data.c +146 -127
- data/ext/iodine/fiobj_data.h +25 -23
- data/ext/iodine/fiobj_hash.c +7 -7
- data/ext/iodine/fiobj_hash.h +6 -5
- data/ext/iodine/fiobj_json.c +20 -17
- data/ext/iodine/fiobj_json.h +5 -5
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +310 -0
- data/ext/iodine/fiobj_mustache.h +40 -0
- data/ext/iodine/fiobj_numbers.c +199 -94
- data/ext/iodine/fiobj_numbers.h +7 -7
- data/ext/iodine/fiobj_str.c +142 -333
- data/ext/iodine/fiobj_str.h +65 -55
- data/ext/iodine/fiobject.c +49 -11
- data/ext/iodine/fiobject.h +40 -39
- data/ext/iodine/http.c +382 -190
- data/ext/iodine/http.h +124 -80
- data/ext/iodine/http1.c +99 -127
- data/ext/iodine/http1.h +5 -5
- data/ext/iodine/http1_parser.c +3 -2
- data/ext/iodine/http1_parser.h +2 -2
- data/ext/iodine/http_internal.c +14 -12
- data/ext/iodine/http_internal.h +25 -19
- data/ext/iodine/iodine.c +37 -18
- data/ext/iodine/iodine.h +4 -0
- data/ext/iodine/iodine_caller.c +9 -2
- data/ext/iodine/iodine_caller.h +2 -0
- data/ext/iodine/iodine_connection.c +82 -117
- data/ext/iodine/iodine_defer.c +57 -50
- data/ext/iodine/iodine_defer.h +0 -1
- data/ext/iodine/iodine_fiobj2rb.h +4 -2
- data/ext/iodine/iodine_helpers.c +4 -4
- data/ext/iodine/iodine_http.c +25 -32
- data/ext/iodine/iodine_json.c +2 -1
- data/ext/iodine/iodine_mustache.c +423 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +48 -153
- data/ext/iodine/iodine_pubsub.h +5 -4
- data/ext/iodine/iodine_rack_io.c +7 -5
- data/ext/iodine/iodine_store.c +16 -13
- data/ext/iodine/iodine_tcp.c +26 -34
- data/ext/iodine/mustache_parser.h +1085 -0
- data/ext/iodine/redis_engine.c +740 -646
- data/ext/iodine/redis_engine.h +13 -15
- data/ext/iodine/resp_parser.h +11 -5
- data/ext/iodine/websocket_parser.h +13 -13
- data/ext/iodine/websockets.c +240 -393
- data/ext/iodine/websockets.h +52 -113
- data/lib/iodine.rb +1 -1
- data/lib/iodine/mustache.rb +140 -0
- data/lib/iodine/version.rb +1 -1
- metadata +15 -28
- data/ext/iodine/defer.c +0 -566
- data/ext/iodine/defer.h +0 -148
- data/ext/iodine/evio.c +0 -26
- data/ext/iodine/evio.h +0 -161
- data/ext/iodine/evio_callbacks.c +0 -26
- data/ext/iodine/evio_epoll.c +0 -251
- data/ext/iodine/evio_kqueue.c +0 -194
- data/ext/iodine/facil.c +0 -2325
- data/ext/iodine/facil.h +0 -616
- data/ext/iodine/fio_base64.c +0 -277
- data/ext/iodine/fio_base64.h +0 -71
- data/ext/iodine/fio_llist.h +0 -257
- data/ext/iodine/fio_mem.c +0 -675
- data/ext/iodine/fio_mem.h +0 -143
- data/ext/iodine/fio_random.c +0 -248
- data/ext/iodine/fio_random.h +0 -45
- data/ext/iodine/fio_sha1.c +0 -362
- data/ext/iodine/fio_sha1.h +0 -107
- data/ext/iodine/fio_sha2.c +0 -842
- data/ext/iodine/fio_sha2.h +0 -169
- data/ext/iodine/pubsub.c +0 -867
- data/ext/iodine/pubsub.h +0 -221
- data/ext/iodine/sock.c +0 -1366
- data/ext/iodine/sock.h +0 -566
- data/ext/iodine/spnlock.inc +0 -111
data/ext/iodine/iodine_defer.h
CHANGED
@@ -18,7 +18,9 @@ typedef struct {
|
|
18
18
|
uint8_t str2sym;
|
19
19
|
} fiobj2rb_s;
|
20
20
|
|
21
|
-
typedef struct {
|
21
|
+
typedef struct {
|
22
|
+
uint8_t str2sym;
|
23
|
+
} fiobj2rb_settings_s;
|
22
24
|
|
23
25
|
static inline VALUE fiobj2rb(FIOBJ o, uint8_t str2sym) {
|
24
26
|
VALUE rb;
|
@@ -40,7 +42,7 @@ static inline VALUE fiobj2rb(FIOBJ o, uint8_t str2sym) {
|
|
40
42
|
case FIOBJ_T_DATA: /* fallthrough */
|
41
43
|
case FIOBJ_T_UNKNOWN: /* fallthrough */
|
42
44
|
case FIOBJ_T_STRING: {
|
43
|
-
|
45
|
+
fio_str_info_s tmp = fiobj_obj2cstr(o);
|
44
46
|
if (str2sym) {
|
45
47
|
rb = rb_intern2(tmp.data, tmp.len);
|
46
48
|
rb = ID2SYM(rb);
|
data/ext/iodine/iodine_helpers.c
CHANGED
@@ -144,9 +144,9 @@ static VALUE date_str(int argc, VALUE *argv, VALUE self) {
|
|
144
144
|
argv[0] = rb_funcallv(argv[0], iodine_to_i_func_id, 0, NULL);
|
145
145
|
Check_Type(argv[0], T_FIXNUM);
|
146
146
|
last_tick =
|
147
|
-
FIX2ULONG(argv[0]) ? FIX2ULONG(argv[0]) :
|
147
|
+
FIX2ULONG(argv[0]) ? FIX2ULONG(argv[0]) : fio_last_tick().tv_sec;
|
148
148
|
} else
|
149
|
-
last_tick =
|
149
|
+
last_tick = fio_last_tick().tv_sec;
|
150
150
|
VALUE str = rb_str_buf_new(32);
|
151
151
|
struct tm tm;
|
152
152
|
|
@@ -172,7 +172,7 @@ Since Iodine uses time caching within it's reactor, using the default value
|
|
172
172
|
static VALUE iodine_rfc2822(VALUE self, VALUE rtm) {
|
173
173
|
time_t last_tick;
|
174
174
|
rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
|
175
|
-
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) :
|
175
|
+
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : fio_last_tick().tv_sec;
|
176
176
|
VALUE str = rb_str_buf_new(34);
|
177
177
|
struct tm tm;
|
178
178
|
|
@@ -198,7 +198,7 @@ Since Iodine uses time caching within it's reactor, using the default value
|
|
198
198
|
static VALUE iodine_rfc2109(VALUE self, VALUE rtm) {
|
199
199
|
time_t last_tick;
|
200
200
|
rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
|
201
|
-
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) :
|
201
|
+
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : fio_last_tick().tv_sec;
|
202
202
|
VALUE str = rb_str_buf_new(32);
|
203
203
|
struct tm tm;
|
204
204
|
|
data/ext/iodine/iodine_http.c
CHANGED
@@ -6,9 +6,8 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
6
6
|
*/
|
7
7
|
#include "iodine.h"
|
8
8
|
|
9
|
-
#include "evio.h"
|
10
|
-
#include "fio_mem.h"
|
11
9
|
#include "http.h"
|
10
|
+
|
12
11
|
#include <ruby/encoding.h>
|
13
12
|
#include <ruby/io.h>
|
14
13
|
// #include "iodine_websockets.h"
|
@@ -131,13 +130,13 @@ static void *iodine_ws_fire_message(void *msg_) {
|
|
131
130
|
return NULL;
|
132
131
|
}
|
133
132
|
|
134
|
-
static void iodine_ws_on_message(ws_s *ws,
|
133
|
+
static void iodine_ws_on_message(ws_s *ws, fio_str_info_s data,
|
135
134
|
uint8_t is_text) {
|
136
135
|
iodine_msg2ruby_s msg = {
|
137
|
-
.data = data,
|
138
|
-
.size =
|
136
|
+
.data = data.data,
|
137
|
+
.size = data.len,
|
139
138
|
.is_text = is_text,
|
140
|
-
.io = (VALUE)
|
139
|
+
.io = (VALUE)websocket_udata_get(ws),
|
141
140
|
};
|
142
141
|
IodineCaller.enterGVL(iodine_ws_fire_message, &msg);
|
143
142
|
}
|
@@ -147,7 +146,7 @@ static void iodine_ws_on_message(ws_s *ws, char *data, size_t size,
|
|
147
146
|
* `on_message` events are raised before `on_open` returns.
|
148
147
|
*/
|
149
148
|
static void iodine_ws_on_open(ws_s *ws) {
|
150
|
-
VALUE h = (VALUE)
|
149
|
+
VALUE h = (VALUE)websocket_udata_get(ws);
|
151
150
|
iodine_connection_s *c = iodine_connection_CData(h);
|
152
151
|
c->arg = ws;
|
153
152
|
c->uuid = websocket_uuid(ws);
|
@@ -160,7 +159,7 @@ static void iodine_ws_on_open(ws_s *ws) {
|
|
160
159
|
* If the socket's buffer is never used, the callback is never called.
|
161
160
|
*/
|
162
161
|
static void iodine_ws_on_ready(ws_s *ws) {
|
163
|
-
iodine_connection_fire_event((VALUE)
|
162
|
+
iodine_connection_fire_event((VALUE)websocket_udata_get(ws),
|
164
163
|
IODINE_CONNECTION_ON_DRAINED, Qnil);
|
165
164
|
}
|
166
165
|
/**
|
@@ -169,7 +168,7 @@ static void iodine_ws_on_ready(ws_s *ws) {
|
|
169
168
|
* `on_close`).
|
170
169
|
*/
|
171
170
|
static void iodine_ws_on_shutdown(ws_s *ws) {
|
172
|
-
iodine_connection_fire_event((VALUE)
|
171
|
+
iodine_connection_fire_event((VALUE)websocket_udata_get(ws),
|
173
172
|
IODINE_CONNECTION_ON_SHUTDOWN, Qnil);
|
174
173
|
}
|
175
174
|
/**
|
@@ -195,7 +194,7 @@ static void iodine_ws_attach(http_s *h, VALUE handler, VALUE env) {
|
|
195
194
|
if (io == Qnil)
|
196
195
|
return;
|
197
196
|
|
198
|
-
http_upgrade2ws(
|
197
|
+
http_upgrade2ws(h, .on_message = iodine_ws_on_message,
|
199
198
|
.on_open = iodine_ws_on_open, .on_ready = iodine_ws_on_ready,
|
200
199
|
.on_shutdown = iodine_ws_on_shutdown,
|
201
200
|
.on_close = iodine_ws_on_close, .udata = (void *)io);
|
@@ -226,7 +225,7 @@ static void iodine_sse_on_open(http_sse_s *sse) {
|
|
226
225
|
c->uuid = http_sse2uuid(sse);
|
227
226
|
iodine_connection_fire_event(h, IODINE_CONNECTION_ON_OPEN, Qnil);
|
228
227
|
sse->on_ready = iodine_sse_on_ready;
|
229
|
-
|
228
|
+
fio_force_event(c->uuid, FIO_EVENT_ON_READY);
|
230
229
|
}
|
231
230
|
|
232
231
|
static void iodine_sse_attach(http_s *h, VALUE handler, VALUE env) {
|
@@ -250,7 +249,7 @@ Copying data from the C request to the Rack's ENV
|
|
250
249
|
int iodine_copy2env_task(FIOBJ o, void *env_) {
|
251
250
|
VALUE env = (VALUE)env_;
|
252
251
|
FIOBJ name = fiobj_hash_key_in_loop();
|
253
|
-
|
252
|
+
fio_str_info_s tmp = fiobj_obj2cstr(name);
|
254
253
|
VALUE hname = (VALUE)0;
|
255
254
|
if (tmp.len > 59) {
|
256
255
|
char *buf = fio_malloc(tmp.len + 5);
|
@@ -303,7 +302,7 @@ static inline VALUE copy2env(iodine_http_request_handle_s *handle) {
|
|
303
302
|
}
|
304
303
|
IodineStore.add(env);
|
305
304
|
|
306
|
-
|
305
|
+
fio_str_info_s tmp;
|
307
306
|
char *pos = NULL;
|
308
307
|
/* Copy basic data */
|
309
308
|
tmp = fiobj_obj2cstr(h->method);
|
@@ -330,17 +329,9 @@ static inline VALUE copy2env(iodine_http_request_handle_s *handle) {
|
|
330
329
|
}
|
331
330
|
|
332
331
|
{ // Support for Ruby web-console.
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
if (addrinfo.addrlen &&
|
337
|
-
inet_ntop(
|
338
|
-
addrinfo.addr->sa_family,
|
339
|
-
addrinfo.addr->sa_family == AF_INET
|
340
|
-
? (void *)&((struct sockaddr_in *)addrinfo.addr)->sin_addr
|
341
|
-
: (void *)&((struct sockaddr_in6 *)addrinfo.addr)->sin6_addr,
|
342
|
-
buf, 64)) {
|
343
|
-
rb_hash_aset(env, REMOTE_ADDR, rb_str_new(buf, strlen(buf)));
|
332
|
+
fio_str_info_s peer = http_peer_addr(h);
|
333
|
+
if (peer.len) {
|
334
|
+
rb_hash_aset(env, REMOTE_ADDR, rb_str_new(peer.data, peer.len));
|
344
335
|
}
|
345
336
|
}
|
346
337
|
|
@@ -477,7 +468,7 @@ static int for_each_header_data(VALUE key, VALUE val, VALUE h_) {
|
|
477
468
|
// make the headers lowercase
|
478
469
|
FIOBJ name = fiobj_str_new(key_s, key_len);
|
479
470
|
{
|
480
|
-
|
471
|
+
fio_str_info_s tmp = fiobj_obj2cstr(name);
|
481
472
|
for (int i = 0; i < key_len; ++i) {
|
482
473
|
tmp.data[i] = tolower(tmp.data[i]);
|
483
474
|
}
|
@@ -719,7 +710,7 @@ static inline void
|
|
719
710
|
iodine_perform_handle_action(iodine_http_request_handle_s handle) {
|
720
711
|
switch (handle.type) {
|
721
712
|
case IODINE_HTTP_SENDBODY: {
|
722
|
-
|
713
|
+
fio_str_info_s data = fiobj_obj2cstr(handle.body);
|
723
714
|
http_send_body(handle.h, data.data, data.len);
|
724
715
|
fiobj_free(handle.body);
|
725
716
|
break;
|
@@ -732,7 +723,7 @@ iodine_perform_handle_action(iodine_http_request_handle_s handle) {
|
|
732
723
|
.len == 7)
|
733
724
|
fiobj_hash_delete2(handle.h->private_data.out_headers,
|
734
725
|
fiobj_obj2hash(HTTP_HEADER_CONTENT_ENCODING));
|
735
|
-
|
726
|
+
fio_str_info_s data = fiobj_obj2cstr(handle.body);
|
736
727
|
if (http_sendfile2(handle.h, data.data, data.len, NULL, 0)) {
|
737
728
|
http_send_error(handle.h, 404);
|
738
729
|
}
|
@@ -754,7 +745,8 @@ iodine_perform_handle_action(iodine_http_request_handle_s handle) {
|
|
754
745
|
}
|
755
746
|
static void on_rack_request(http_s *h) {
|
756
747
|
iodine_http_request_handle_s handle = (iodine_http_request_handle_s){
|
757
|
-
.h = h,
|
748
|
+
.h = h,
|
749
|
+
.upgrade = IODINE_UPGRADE_NONE,
|
758
750
|
};
|
759
751
|
IodineCaller.enterGVL((void *(*)(void *))iodine_handle_request_in_GVL,
|
760
752
|
&handle);
|
@@ -794,13 +786,14 @@ void *iodine_print_http_msg_in_gvl(void *d_) {
|
|
794
786
|
fprintf(stderr,
|
795
787
|
"Iodine HTTP Server on port %s:\n"
|
796
788
|
" * Serving static files from %s\n\n",
|
797
|
-
|
789
|
+
(arg->port ? StringValueCStr(arg->port) : "----"),
|
790
|
+
StringValueCStr(arg->www));
|
798
791
|
}
|
799
792
|
return NULL;
|
800
793
|
}
|
801
794
|
|
802
795
|
static void iodine_print_http_msg(void *www, void *port) {
|
803
|
-
if (
|
796
|
+
if (!fio_is_master())
|
804
797
|
goto finish;
|
805
798
|
struct {
|
806
799
|
void *www;
|
@@ -989,7 +982,7 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
989
982
|
.ws_timeout = ping, .ws_max_msg_size = max_msg,
|
990
983
|
.max_header_size = max_headers, .on_finish = free_iodine_http,
|
991
984
|
.log = log_http, .max_body_size = max_body,
|
992
|
-
.public_folder = (www ? StringValueCStr(www) : NULL))) {
|
985
|
+
.public_folder = (www ? StringValueCStr(www) : NULL)) == -1) {
|
993
986
|
fprintf(stderr,
|
994
987
|
"ERROR: Failed to initialize a listening HTTP socket for port %s\n",
|
995
988
|
port ? StringValueCStr(port) : "3000");
|
@@ -1002,7 +995,7 @@ VALUE iodine_http_listen(VALUE self, VALUE opt) {
|
|
1002
995
|
"static files.\n",
|
1003
996
|
(port ? StringValueCStr(port) : "3000"));
|
1004
997
|
}
|
1005
|
-
|
998
|
+
fio_defer(iodine_print_http_msg, (www ? (void *)www : NULL), (void *)port);
|
1006
999
|
|
1007
1000
|
return Qtrue;
|
1008
1001
|
(void)self;
|
data/ext/iodine/iodine_json.c
CHANGED
@@ -0,0 +1,423 @@
|
|
1
|
+
|
2
|
+
#define INCLUDE_MUSTACHE_IMPLEMENTATION 1
|
3
|
+
#include "mustache_parser.h"
|
4
|
+
|
5
|
+
#include "iodine.h"
|
6
|
+
|
7
|
+
#define FIO_INCLUDE_STR
|
8
|
+
#include <fio.h>
|
9
|
+
|
10
|
+
ID call_func_id;
|
11
|
+
ID to_s_func_id;
|
12
|
+
/* *****************************************************************************
|
13
|
+
C <=> Ruby Data allocation
|
14
|
+
***************************************************************************** */
|
15
|
+
|
16
|
+
static size_t iodine_mustache_data_size(const void *c_) {
|
17
|
+
return sizeof(mustache_s *);
|
18
|
+
(void)c_;
|
19
|
+
}
|
20
|
+
|
21
|
+
static size_t iodine_mustache_data_free(const void *c_) {
|
22
|
+
mustache_free(((mustache_s **)c_)[0]);
|
23
|
+
free((void *)c_);
|
24
|
+
return sizeof(mustache_s *);
|
25
|
+
(void)c_;
|
26
|
+
}
|
27
|
+
|
28
|
+
const rb_data_type_t iodine_mustache_data_type = {
|
29
|
+
.wrap_struct_name = "IodineMustacheData",
|
30
|
+
.function =
|
31
|
+
{
|
32
|
+
.dmark = NULL,
|
33
|
+
.dfree = free,
|
34
|
+
.dsize = iodine_mustache_data_size,
|
35
|
+
},
|
36
|
+
.data = NULL,
|
37
|
+
// .flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
38
|
+
};
|
39
|
+
|
40
|
+
/* Iodine::PubSub::Engine.allocate */
|
41
|
+
static VALUE iodine_mustache_data_alloc_c(VALUE self) {
|
42
|
+
void *m = malloc(sizeof(mustache_s *));
|
43
|
+
((mustache_s **)m)[0] = NULL;
|
44
|
+
return TypedData_Wrap_Struct(self, &iodine_mustache_data_type, m);
|
45
|
+
}
|
46
|
+
|
47
|
+
static inline mustache_s *iodine_mustache_ruby2C(VALUE self) {
|
48
|
+
mustache_s **m = NULL;
|
49
|
+
TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
|
50
|
+
if (!m)
|
51
|
+
return NULL;
|
52
|
+
return m[0];
|
53
|
+
}
|
54
|
+
|
55
|
+
/* *****************************************************************************
|
56
|
+
Parser Callbacks
|
57
|
+
***************************************************************************** */
|
58
|
+
|
59
|
+
/** HTML ecape table, created using the following Ruby Script:
|
60
|
+
a = []
|
61
|
+
256.times {|i| a[i] = "&\#x#{ i < 16 ? "0#{i.to_s(16)}" : i.to_s(16)};"}
|
62
|
+
('a'.ord..'z'.ord).each {|i| a[i] = i.chr }
|
63
|
+
('A'.ord..'Z'.ord).each {|i| a[i] = i.chr }
|
64
|
+
('0'.ord..'9'.ord).each {|i| a[i] = i.chr }
|
65
|
+
a['<'.ord] = "<"
|
66
|
+
a['>'.ord] = ">"
|
67
|
+
a['&'.ord] = "&"
|
68
|
+
a['"'.ord] = """
|
69
|
+
|
70
|
+
b = a.map {|s| s.length }
|
71
|
+
puts "static char *html_escape_strs[] = {", a.to_s.slice(1..-2) ,"};",
|
72
|
+
"static uint8_t html_escape_len[] = {", b.to_s.slice(1..-2),"};"
|
73
|
+
*/
|
74
|
+
static char *html_escape_strs[] = {
|
75
|
+
"�", "", "", "", "", "", "",
|
76
|
+
"", "", "	", "
", "", "", "
",
|
77
|
+
"", "", "", "", "", "", "",
|
78
|
+
"", "", "", "", "", "", "",
|
79
|
+
"", "", "", "", " ", "!", """,
|
80
|
+
"#", "$", "%", "&", "'", "(", ")",
|
81
|
+
"*", "+", ",", "-", ".", "/", "0",
|
82
|
+
"1", "2", "3", "4", "5", "6", "7",
|
83
|
+
"8", "9", ":", ";", "<", "=", ">",
|
84
|
+
"?", "@", "A", "B", "C", "D", "E",
|
85
|
+
"F", "G", "H", "I", "J", "K", "L",
|
86
|
+
"M", "N", "O", "P", "Q", "R", "S",
|
87
|
+
"T", "U", "V", "W", "X", "Y", "Z",
|
88
|
+
"[", "\", "]", "^", "_", "`", "a",
|
89
|
+
"b", "c", "d", "e", "f", "g", "h",
|
90
|
+
"i", "j", "k", "l", "m", "n", "o",
|
91
|
+
"p", "q", "r", "s", "t", "u", "v",
|
92
|
+
"w", "x", "y", "z", "{", "|", "}",
|
93
|
+
"~", "", "€", "", "‚", "ƒ", "„",
|
94
|
+
"…", "†", "‡", "ˆ", "‰", "Š", "‹",
|
95
|
+
"Œ", "", "Ž", "", "", "‘", "’",
|
96
|
+
"“", "”", "•", "–", "—", "˜", "™",
|
97
|
+
"š", "›", "œ", "", "ž", "Ÿ", " ",
|
98
|
+
"¡", "¢", "£", "¤", "¥", "¦", "§",
|
99
|
+
"¨", "©", "ª", "«", "¬", "­", "®",
|
100
|
+
"¯", "°", "±", "²", "³", "´", "µ",
|
101
|
+
"¶", "·", "¸", "¹", "º", "»", "¼",
|
102
|
+
"½", "¾", "¿", "À", "Á", "Â", "Ã",
|
103
|
+
"Ä", "Å", "Æ", "Ç", "È", "É", "Ê",
|
104
|
+
"Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ",
|
105
|
+
"Ò", "Ó", "Ô", "Õ", "Ö", "×", "Ø",
|
106
|
+
"Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß",
|
107
|
+
"à", "á", "â", "ã", "ä", "å", "æ",
|
108
|
+
"ç", "è", "é", "ê", "ë", "ì", "í",
|
109
|
+
"î", "ï", "ð", "ñ", "ò", "ó", "ô",
|
110
|
+
"õ", "ö", "÷", "ø", "ù", "ú", "û",
|
111
|
+
"ü", "ý", "þ", "ÿ"};
|
112
|
+
static uint8_t html_escape_len[] = {
|
113
|
+
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
114
|
+
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
115
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 4, 6, 4, 6, 6, 1, 1, 1, 1, 1, 1, 1,
|
116
|
+
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6,
|
117
|
+
6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
118
|
+
1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
119
|
+
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
120
|
+
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
121
|
+
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
122
|
+
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
123
|
+
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6};
|
124
|
+
|
125
|
+
static inline VALUE fiobj_mustache_find_obj(mustache_section_s *section,
|
126
|
+
const char *name,
|
127
|
+
uint32_t name_len) {
|
128
|
+
do {
|
129
|
+
VALUE tmp;
|
130
|
+
#if 0
|
131
|
+
/* test for Array indexing */
|
132
|
+
if (name[0] >= '0' && name[0] <= '9' &&
|
133
|
+
RB_TYPE_P((VALUE)section->udata2, T_ARRAY)) {
|
134
|
+
char **pos = (char **)&name;
|
135
|
+
tmp = rb_ary_entry((VALUE)section->udata2, fio_atol(pos));
|
136
|
+
if (tmp)
|
137
|
+
return tmp;
|
138
|
+
}
|
139
|
+
#endif
|
140
|
+
if (!RB_TYPE_P((VALUE)section->udata2, T_HASH)) {
|
141
|
+
continue;
|
142
|
+
}
|
143
|
+
/* search by String */
|
144
|
+
VALUE key = rb_str_new(name, name_len);
|
145
|
+
tmp = rb_hash_aref((VALUE)section->udata2, key);
|
146
|
+
if (tmp != Qnil)
|
147
|
+
return tmp;
|
148
|
+
/* search by Symbol */
|
149
|
+
key = rb_id2sym(rb_intern2(name, name_len));
|
150
|
+
tmp = rb_hash_aref((VALUE)section->udata2, key);
|
151
|
+
if (tmp != Qnil)
|
152
|
+
return tmp;
|
153
|
+
section = section->parent;
|
154
|
+
} while (section);
|
155
|
+
return Qnil;
|
156
|
+
}
|
157
|
+
/**
|
158
|
+
* Called when an argument name was detected in the current section.
|
159
|
+
*
|
160
|
+
* A conforming implementation will search for the named argument both in the
|
161
|
+
* existing section and all of it's parents (walking backwards towards the root)
|
162
|
+
* until a value is detected.
|
163
|
+
*
|
164
|
+
* A missing value should be treated the same as an empty string.
|
165
|
+
*
|
166
|
+
* A conforming implementation will output the named argument's value (either
|
167
|
+
* HTML escaped or not, depending on the `escape` flag) as a string.
|
168
|
+
*/
|
169
|
+
static int mustache_on_arg(mustache_section_s *section, const char *name,
|
170
|
+
uint32_t name_len, unsigned char escape) {
|
171
|
+
VALUE o = fiobj_mustache_find_obj(section, name, name_len);
|
172
|
+
if (!o)
|
173
|
+
return 0;
|
174
|
+
if (rb_respond_to(o, call_func_id))
|
175
|
+
goto callable;
|
176
|
+
if (!RB_TYPE_P(o, T_STRING))
|
177
|
+
o = IodineCaller.call(o, to_s_func_id);
|
178
|
+
if (!RB_TYPE_P(o, T_STRING) || !RSTRUCT_LEN(o))
|
179
|
+
return 0;
|
180
|
+
if (!escape) {
|
181
|
+
fio_str_write(section->udata1, RSTRING_PTR(o), RSTRING_LEN(o));
|
182
|
+
return 0;
|
183
|
+
}
|
184
|
+
/* HTML escape */
|
185
|
+
fio_str_info_s str = {.data = RSTRING_PTR(o), .len = RSTRING_LEN(o)};
|
186
|
+
fio_str_info_s i = fio_str_capa_assert(
|
187
|
+
section->udata1, fio_str_len(section->udata1) + str.len + 64);
|
188
|
+
do {
|
189
|
+
if (i.len + 6 >= i.capa)
|
190
|
+
i = fio_str_capa_assert(section->udata1, i.capa + 64);
|
191
|
+
i = fio_str_write(section->udata1, html_escape_strs[(uint8_t)str.data[0]],
|
192
|
+
html_escape_len[(uint8_t)str.data[0]]);
|
193
|
+
--str.len;
|
194
|
+
++str.data;
|
195
|
+
} while (str.len);
|
196
|
+
(void)section;
|
197
|
+
(void)name;
|
198
|
+
(void)name_len;
|
199
|
+
(void)escape;
|
200
|
+
return 0;
|
201
|
+
callable:
|
202
|
+
o = rb_funcall2(o, call_func_id, 0, NULL);
|
203
|
+
o = rb_any_to_s(o);
|
204
|
+
fio_str_write(section->udata1, RSTRING_PTR(o), RSTRUCT_LEN(o));
|
205
|
+
return 0;
|
206
|
+
}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* Called when simple template text (string) is detected.
|
210
|
+
*
|
211
|
+
* A conforming implementation will output data as a string (no escaping).
|
212
|
+
*/
|
213
|
+
static int mustache_on_text(mustache_section_s *section, const char *data,
|
214
|
+
uint32_t data_len) {
|
215
|
+
fio_str_write(section->udata1, data, data_len);
|
216
|
+
return 0;
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Called for nested sections, must return the number of objects in the new
|
221
|
+
* subsection (depending on the argument's name).
|
222
|
+
*
|
223
|
+
* Arrays should return the number of objects in the array.
|
224
|
+
*
|
225
|
+
* `true` values should return 1.
|
226
|
+
*
|
227
|
+
* `false` values should return 0.
|
228
|
+
*
|
229
|
+
* A return value of -1 will stop processing with an error.
|
230
|
+
*
|
231
|
+
* Please note, this will handle both normal and inverted sections.
|
232
|
+
*/
|
233
|
+
static int32_t mustache_on_section_test(mustache_section_s *section,
|
234
|
+
const char *name, uint32_t name_len) {
|
235
|
+
VALUE o = fiobj_mustache_find_obj(section, name, name_len);
|
236
|
+
if (o == Qnil) {
|
237
|
+
return 0;
|
238
|
+
}
|
239
|
+
if (RB_TYPE_P(o, T_ARRAY)) {
|
240
|
+
return rb_array_len(o);
|
241
|
+
}
|
242
|
+
return 1;
|
243
|
+
}
|
244
|
+
|
245
|
+
/**
|
246
|
+
* Called when entering a nested section.
|
247
|
+
*
|
248
|
+
* `index` is a zero based index indicating the number of repetitions that
|
249
|
+
* occurred so far (same as the array index for arrays).
|
250
|
+
*
|
251
|
+
* A return value of -1 will stop processing with an error.
|
252
|
+
*
|
253
|
+
* Note: this is a good time to update the subsection's `udata` with the value
|
254
|
+
* of the array index. The `udata` will always contain the value or the parent's
|
255
|
+
* `udata`.
|
256
|
+
*/
|
257
|
+
static int mustache_on_section_start(mustache_section_s *section,
|
258
|
+
char const *name, uint32_t name_len,
|
259
|
+
uint32_t index) {
|
260
|
+
VALUE o = fiobj_mustache_find_obj(section, name, name_len);
|
261
|
+
if (o == Qnil)
|
262
|
+
return 0;
|
263
|
+
if (RB_TYPE_P(o, T_ARRAY))
|
264
|
+
section->udata2 = (void *)rb_ary_entry(o, index);
|
265
|
+
else
|
266
|
+
section->udata2 = (void *)o;
|
267
|
+
return 0;
|
268
|
+
}
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Called for cleanup in case of error.
|
272
|
+
*/
|
273
|
+
static void mustache_on_formatting_error(void *udata1, void *udata2) {
|
274
|
+
(void)udata1;
|
275
|
+
(void)udata2;
|
276
|
+
}
|
277
|
+
|
278
|
+
/* *****************************************************************************
|
279
|
+
Loading the template
|
280
|
+
***************************************************************************** */
|
281
|
+
|
282
|
+
/**
|
283
|
+
Loads a mustache template (and any partials).
|
284
|
+
|
285
|
+
Once a template was loaded, it could be rendered using {render}.
|
286
|
+
*/
|
287
|
+
static VALUE iodine_mustache_new(VALUE self, VALUE filename) {
|
288
|
+
mustache_s **m = NULL;
|
289
|
+
TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
|
290
|
+
if (!m) {
|
291
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache allocation error.");
|
292
|
+
}
|
293
|
+
Check_Type(filename, T_STRING);
|
294
|
+
mustache_error_en err;
|
295
|
+
*m = mustache_load(.filename = RSTRING_PTR(filename),
|
296
|
+
.filename_len = RSTRING_LEN(filename), .err = &err);
|
297
|
+
if (!*m)
|
298
|
+
goto error;
|
299
|
+
return self;
|
300
|
+
error:
|
301
|
+
switch (err) {
|
302
|
+
case MUSTACHE_OK:
|
303
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache template ok, unknown error.");
|
304
|
+
break;
|
305
|
+
case MUSTACHE_ERR_TOO_DEEP:
|
306
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache element nesting too deep.");
|
307
|
+
break;
|
308
|
+
case MUSTACHE_ERR_CLOSURE_MISMATCH:
|
309
|
+
rb_raise(rb_eRuntimeError,
|
310
|
+
"Iodine::Mustache template error, closure mismatch.");
|
311
|
+
break;
|
312
|
+
case MUSTACHE_ERR_FILE_NOT_FOUND:
|
313
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache template not found.");
|
314
|
+
break;
|
315
|
+
case MUSTACHE_ERR_FILE_TOO_BIG:
|
316
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache template too big.");
|
317
|
+
break;
|
318
|
+
case MUSTACHE_ERR_FILE_NAME_TOO_LONG:
|
319
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache template name too long.");
|
320
|
+
break;
|
321
|
+
case MUSTACHE_ERR_EMPTY_TEMPLATE:
|
322
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache template is empty.");
|
323
|
+
break;
|
324
|
+
case MUSTACHE_ERR_UNKNOWN:
|
325
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache unknown error.");
|
326
|
+
break;
|
327
|
+
case MUSTACHE_ERR_USER_ERROR:
|
328
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache internal error.");
|
329
|
+
break;
|
330
|
+
}
|
331
|
+
return self;
|
332
|
+
}
|
333
|
+
|
334
|
+
/* *****************************************************************************
|
335
|
+
Rendering
|
336
|
+
***************************************************************************** */
|
337
|
+
|
338
|
+
/**
|
339
|
+
Renders the mustache template using the data provided in the `data` argument.
|
340
|
+
|
341
|
+
Returns a String.
|
342
|
+
|
343
|
+
Raises an exception on error.
|
344
|
+
|
345
|
+
NOTE:
|
346
|
+
|
347
|
+
As one might notice, no binding is provided. Instead, a `data` Hash is assumed.
|
348
|
+
Iodine will search the Hash for any data while protecting against code
|
349
|
+
execution.
|
350
|
+
*/
|
351
|
+
static VALUE iodine_mustache_render(VALUE self, VALUE data) {
|
352
|
+
fio_str_s str = FIO_STR_INIT;
|
353
|
+
mustache_s **m = NULL;
|
354
|
+
TypedData_Get_Struct(self, mustache_s *, &iodine_mustache_data_type, m);
|
355
|
+
if (!m) {
|
356
|
+
rb_raise(rb_eRuntimeError, "Iodine::Mustache allocation error.");
|
357
|
+
}
|
358
|
+
if (mustache_build(*m, .udata1 = &str, .udata2 = (void *)data))
|
359
|
+
goto error;
|
360
|
+
fio_str_info_s i = fio_str_info(&str);
|
361
|
+
VALUE ret = rb_str_new(i.data, i.len);
|
362
|
+
fio_str_free(&str);
|
363
|
+
return ret;
|
364
|
+
|
365
|
+
error:
|
366
|
+
fio_str_free(&str);
|
367
|
+
rb_raise(rb_eRuntimeError, "Couldn't build template frome data.");
|
368
|
+
}
|
369
|
+
|
370
|
+
/* *****************************************************************************
|
371
|
+
Initialize Iodine::Mustache
|
372
|
+
***************************************************************************** */
|
373
|
+
|
374
|
+
void iodine_init_mustache(void) {
|
375
|
+
call_func_id = rb_intern2("call", 4);
|
376
|
+
to_s_func_id = rb_intern2("to_s", 4);
|
377
|
+
/**
|
378
|
+
Iodine::Mustache offers a logicless mustache template engine with strict HTML
|
379
|
+
escaping (more than the basic `"<>'$`).
|
380
|
+
|
381
|
+
This offers more security against XSS and protects against the chance of
|
382
|
+
executing Ruby code within the template.
|
383
|
+
|
384
|
+
You can test the parser using:
|
385
|
+
|
386
|
+
TEMPLATE="my_template.mustache"
|
387
|
+
|
388
|
+
require 'json'
|
389
|
+
require 'iodine'
|
390
|
+
TIMES = 100
|
391
|
+
STR = IO.binread(JSON_FILENAME); nil
|
392
|
+
|
393
|
+
JSON.parse(STR) == Iodine::JSON.parse(STR) # => true
|
394
|
+
JSON.parse(STR,
|
395
|
+
symbolize_names: true) == Iodine::JSON.parse(STR,
|
396
|
+
symbolize_names: true) # => true
|
397
|
+
JSON.parse!(STR) == Iodine::JSON.parse!(STR) # => true/false (unknown)
|
398
|
+
|
399
|
+
# warm-up
|
400
|
+
TIMES.times { JSON.parse STR }
|
401
|
+
TIMES.times { Iodine::JSON.parse STR }
|
402
|
+
|
403
|
+
Benchmark.bm do |b|
|
404
|
+
sys = b.report("system") { TIMES.times { JSON.parse STR } }
|
405
|
+
sys_sym = b.report("system sym") { TIMES.times { JSON.parse STR,
|
406
|
+
symbolize_names: true } }
|
407
|
+
iodine = b.report("iodine") { TIMES.times { Iodine::JSON.parse STR } }
|
408
|
+
iodine_sym = b.report("iodine sym") do
|
409
|
+
TIMES.times { Iodine::JSON.parse STR,
|
410
|
+
symbolize_names: true }
|
411
|
+
end
|
412
|
+
puts "System / Iodine: #{sys/iodine}"
|
413
|
+
puts "System-sym/Iodine-sym: #{sys_sym/iodine_sym}"
|
414
|
+
end; nil
|
415
|
+
|
416
|
+
|
417
|
+
*/
|
418
|
+
VALUE tmp = rb_define_class_under(IodineModule, "Mustache", rb_cData);
|
419
|
+
rb_define_alloc_func(tmp, iodine_mustache_data_alloc_c);
|
420
|
+
rb_define_method(tmp, "initialize", iodine_mustache_new, 1);
|
421
|
+
rb_define_method(tmp, "render", iodine_mustache_render, 1);
|
422
|
+
// rb_define_module_function(tmp, "parse", iodine_json_parse, -1);
|
423
|
+
}
|