isomorfeus-iodine 0.7.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.gitignore +20 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +32 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1038 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +44 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/config.ru +56 -0
  25. data/examples/echo.ru +59 -0
  26. data/examples/hello.ru +29 -0
  27. data/examples/pubsub_engine.ru +81 -0
  28. data/examples/redis.ru +70 -0
  29. data/examples/shootout.ru +73 -0
  30. data/examples/sub-protocols.ru +90 -0
  31. data/examples/tcp_client.rb +66 -0
  32. data/examples/x-sendfile.ru +14 -0
  33. data/exe/iodine +277 -0
  34. data/ext/iodine/extconf.rb +109 -0
  35. data/ext/iodine/fio.c +11985 -0
  36. data/ext/iodine/fio.h +6373 -0
  37. data/ext/iodine/fio_cli.c +431 -0
  38. data/ext/iodine/fio_cli.h +189 -0
  39. data/ext/iodine/fio_json_parser.h +687 -0
  40. data/ext/iodine/fio_siphash.c +157 -0
  41. data/ext/iodine/fio_siphash.h +37 -0
  42. data/ext/iodine/fio_tls.h +129 -0
  43. data/ext/iodine/fio_tls_missing.c +649 -0
  44. data/ext/iodine/fio_tls_openssl.c +1056 -0
  45. data/ext/iodine/fio_tmpfile.h +50 -0
  46. data/ext/iodine/fiobj.h +44 -0
  47. data/ext/iodine/fiobj4fio.h +21 -0
  48. data/ext/iodine/fiobj_ary.c +333 -0
  49. data/ext/iodine/fiobj_ary.h +139 -0
  50. data/ext/iodine/fiobj_data.c +1185 -0
  51. data/ext/iodine/fiobj_data.h +167 -0
  52. data/ext/iodine/fiobj_hash.c +409 -0
  53. data/ext/iodine/fiobj_hash.h +176 -0
  54. data/ext/iodine/fiobj_json.c +622 -0
  55. data/ext/iodine/fiobj_json.h +68 -0
  56. data/ext/iodine/fiobj_mem.h +71 -0
  57. data/ext/iodine/fiobj_mustache.c +317 -0
  58. data/ext/iodine/fiobj_mustache.h +62 -0
  59. data/ext/iodine/fiobj_numbers.c +344 -0
  60. data/ext/iodine/fiobj_numbers.h +127 -0
  61. data/ext/iodine/fiobj_str.c +433 -0
  62. data/ext/iodine/fiobj_str.h +172 -0
  63. data/ext/iodine/fiobject.c +620 -0
  64. data/ext/iodine/fiobject.h +654 -0
  65. data/ext/iodine/hpack.h +1923 -0
  66. data/ext/iodine/http.c +2754 -0
  67. data/ext/iodine/http.h +1002 -0
  68. data/ext/iodine/http1.c +912 -0
  69. data/ext/iodine/http1.h +29 -0
  70. data/ext/iodine/http1_parser.h +873 -0
  71. data/ext/iodine/http_internal.c +1278 -0
  72. data/ext/iodine/http_internal.h +237 -0
  73. data/ext/iodine/http_mime_parser.h +350 -0
  74. data/ext/iodine/iodine.c +1430 -0
  75. data/ext/iodine/iodine.h +63 -0
  76. data/ext/iodine/iodine_caller.c +218 -0
  77. data/ext/iodine/iodine_caller.h +27 -0
  78. data/ext/iodine/iodine_connection.c +933 -0
  79. data/ext/iodine/iodine_connection.h +55 -0
  80. data/ext/iodine/iodine_defer.c +420 -0
  81. data/ext/iodine/iodine_defer.h +6 -0
  82. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  83. data/ext/iodine/iodine_helpers.c +282 -0
  84. data/ext/iodine/iodine_helpers.h +12 -0
  85. data/ext/iodine/iodine_http.c +1171 -0
  86. data/ext/iodine/iodine_http.h +23 -0
  87. data/ext/iodine/iodine_json.c +302 -0
  88. data/ext/iodine/iodine_json.h +6 -0
  89. data/ext/iodine/iodine_mustache.c +567 -0
  90. data/ext/iodine/iodine_mustache.h +6 -0
  91. data/ext/iodine/iodine_pubsub.c +580 -0
  92. data/ext/iodine/iodine_pubsub.h +26 -0
  93. data/ext/iodine/iodine_rack_io.c +281 -0
  94. data/ext/iodine/iodine_rack_io.h +20 -0
  95. data/ext/iodine/iodine_store.c +142 -0
  96. data/ext/iodine/iodine_store.h +20 -0
  97. data/ext/iodine/iodine_tcp.c +346 -0
  98. data/ext/iodine/iodine_tcp.h +13 -0
  99. data/ext/iodine/iodine_tls.c +261 -0
  100. data/ext/iodine/iodine_tls.h +13 -0
  101. data/ext/iodine/mustache_parser.h +1546 -0
  102. data/ext/iodine/redis_engine.c +957 -0
  103. data/ext/iodine/redis_engine.h +79 -0
  104. data/ext/iodine/resp_parser.h +317 -0
  105. data/ext/iodine/websocket_parser.h +505 -0
  106. data/ext/iodine/websockets.c +735 -0
  107. data/ext/iodine/websockets.h +185 -0
  108. data/isomorfeus-iodine.gemspec +42 -0
  109. data/lib/iodine/connection.rb +61 -0
  110. data/lib/iodine/json.rb +42 -0
  111. data/lib/iodine/mustache.rb +113 -0
  112. data/lib/iodine/pubsub.rb +55 -0
  113. data/lib/iodine/rack_utils.rb +43 -0
  114. data/lib/iodine/tls.rb +16 -0
  115. data/lib/iodine/version.rb +3 -0
  116. data/lib/iodine.rb +274 -0
  117. data/lib/rack/handler/iodine.rb +33 -0
  118. data/logo.png +0 -0
  119. metadata +271 -0
@@ -0,0 +1,79 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_REDIS_ENGINE_H
8
+ #define H_REDIS_ENGINE_H
9
+
10
+ #include <fio.h>
11
+ #include <fiobj.h>
12
+
13
+ /* support C++ */
14
+ #ifdef __cplusplus
15
+ extern "C" {
16
+ #endif
17
+
18
+ /** possible arguments for the `redis_engine_create` function call */
19
+ struct redis_engine_create_args {
20
+ /** Redis server's address, defaults to localhost. */
21
+ fio_str_info_s address;
22
+ /** Redis server's port, defaults to 6379. */
23
+ fio_str_info_s port;
24
+ /** Redis server's password, if any. */
25
+ fio_str_info_s auth;
26
+ /** A `ping` will be sent every `ping_interval` interval or inactivity. */
27
+ uint8_t ping_interval;
28
+ };
29
+
30
+ /**
31
+ * See the {fio.h} file for documentation about engines.
32
+ *
33
+ * The engine is active only after facil.io starts running.
34
+ *
35
+ * A `ping` will be sent every `ping_interval` interval or inactivity. The
36
+ * default value (0) will fallback to facil.io's maximum time of inactivity (5
37
+ * minutes) before polling on the connection's protocol.
38
+ *
39
+ * function names speak for themselves ;-)
40
+ *
41
+ * Note: The Redis engine assumes it will stay alive until all the messages and
42
+ * callbacks have been called (or facil.io exits)... If the engine is destroyed
43
+ * midway, memory leaks might occur (and little puppies might cry).
44
+ */
45
+ fio_pubsub_engine_s *redis_engine_create(struct redis_engine_create_args);
46
+ #define redis_engine_create(...) \
47
+ redis_engine_create((struct redis_engine_create_args){__VA_ARGS__})
48
+
49
+ /**
50
+ * Sends a Redis command through the engine's connection.
51
+ *
52
+ * The response will be sent back using the optional callback. `udata` is passed
53
+ * along untouched.
54
+ *
55
+ * The message will be resent on network failures, until a response validates
56
+ * the fact that the command was sent (or the engine is destroyed).
57
+ *
58
+ * Note: NEVER call Pub/Sub commands using this function, as it will violate the
59
+ * Redis connection's protocol (best case scenario, a disconnection will occur
60
+ * before and messages are lost).
61
+ */
62
+ intptr_t redis_engine_send(fio_pubsub_engine_s *engine, FIOBJ command,
63
+ void (*callback)(fio_pubsub_engine_s *e, FIOBJ reply,
64
+ void *udata),
65
+ void *udata);
66
+
67
+ /**
68
+ * See the {pubsub.h} file for documentation about engines.
69
+ *
70
+ * function names speak for themselves ;-)
71
+ */
72
+ void redis_engine_destroy(fio_pubsub_engine_s *engine);
73
+
74
+ /* support C++ */
75
+ #ifdef __cplusplus
76
+ }
77
+ #endif
78
+
79
+ #endif /* H_REDIS_ENGINE_H */
@@ -0,0 +1,317 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2019
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef H_RESP_PARSER_H
8
+ /**
9
+ * This single file library is a RESP parser for Redis connections.
10
+ *
11
+ * To use this file, the `.c` file in which this file is included MUST define a
12
+ * number of callbacks, as later inticated.
13
+ *
14
+ * When feeding the parser, the parser will inform of any trailing bytes (bytes
15
+ * at the end of the buffer that could not be parsed). These bytes should be
16
+ * resent to the parser along with more data. Zero is a valid return value.
17
+ *
18
+ * Note: mostly, callback return vaslues are ignored.
19
+ */
20
+ #define H_RESP_PARSER_H
21
+
22
+ #ifndef _GNU_SOURCE
23
+ #define _GNU_SOURCE
24
+ #endif
25
+
26
+ #include <stdint.h>
27
+ #include <stdlib.h>
28
+ #include <string.h>
29
+
30
+ /* *****************************************************************************
31
+ The Parser
32
+ ***************************************************************************** */
33
+
34
+ typedef struct resp_parser_s {
35
+ /* for internal use - (array / object countdown) */
36
+ intptr_t obj_countdown;
37
+ /* for internal use - (string byte consumption) */
38
+ intptr_t expecting;
39
+ } resp_parser_s;
40
+
41
+ /**
42
+ * Returns the number of bytes to be resent. i.e., for a return value 5, the
43
+ * last 5 bytes in the buffer need to be resent to the parser.
44
+ */
45
+ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
46
+ size_t length);
47
+
48
+ /* *****************************************************************************
49
+ Required Parser Callbacks (to be defined by the including file)
50
+ ***************************************************************************** */
51
+
52
+ /** a local static callback, called when the RESP message is complete. */
53
+ static int resp_on_message(resp_parser_s *parser);
54
+
55
+ /** a local static callback, called when a Number object is parsed. */
56
+ static int resp_on_number(resp_parser_s *parser, int64_t num);
57
+ /** a local static callback, called when a OK message is received. */
58
+ static int resp_on_okay(resp_parser_s *parser);
59
+ /** a local static callback, called when NULL is received. */
60
+ static int resp_on_null(resp_parser_s *parser);
61
+
62
+ /**
63
+ * a local static callback, called when a String should be allocated.
64
+ *
65
+ * `str_len` is the expected number of bytes that will fill the final string
66
+ * object, without any NUL byte marker (the string might be binary).
67
+ *
68
+ * If this function returns any value besides 0, parsing is stopped.
69
+ */
70
+ static int resp_on_start_string(resp_parser_s *parser, size_t str_len);
71
+ /** a local static callback, called as String objects are streamed. */
72
+ static int resp_on_string_chunk(resp_parser_s *parser, void *data, size_t len);
73
+ /** a local static callback, called when a String object had finished streaming.
74
+ */
75
+ static int resp_on_end_string(resp_parser_s *parser);
76
+
77
+ /** a local static callback, called an error message is received. */
78
+ static int resp_on_err_msg(resp_parser_s *parser, void *data, size_t len);
79
+
80
+ /**
81
+ * a local static callback, called when an Array should be allocated.
82
+ *
83
+ * `array_len` is the expected number of objects that will fill the Array
84
+ * object.
85
+ *
86
+ * There's no `resp_on_end_array` callback since the RESP protocol assumes the
87
+ * message is finished along with the Array (`resp_on_message` is called).
88
+ * However, just in case a non-conforming client/server sends nested Arrays, the
89
+ * callback should test against possible overflow or nested Array endings.
90
+ *
91
+ * If this function returns any value besides 0, parsing is stopped.
92
+ */
93
+ static int resp_on_start_array(resp_parser_s *parser, size_t array_len);
94
+
95
+ /** a local static callback, called when a parser / protocol error occurs. */
96
+ static int resp_on_parser_error(resp_parser_s *parser);
97
+
98
+ /* *****************************************************************************
99
+ Seeking the new line...
100
+ ***************************************************************************** */
101
+
102
+ #if FIO_MEMCHAR
103
+
104
+ /**
105
+ * This seems to be faster on some systems, especially for smaller distances.
106
+ *
107
+ * On newer systems, `memchr` should be faster.
108
+ */
109
+ static inline int seek2ch(uint8_t **buffer, register const uint8_t *limit,
110
+ const uint8_t c) {
111
+ if (**buffer == c)
112
+ return 1;
113
+
114
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
115
+ /* too short for this mess */
116
+ if ((uintptr_t)limit <= 16 + ((uintptr_t)*buffer & (~(uintptr_t)7)))
117
+ goto finish;
118
+
119
+ /* align memory */
120
+ {
121
+ const uint8_t *alignment =
122
+ (uint8_t *)(((uintptr_t)(*buffer) & (~(uintptr_t)7)) + 8);
123
+ if (limit >= alignment) {
124
+ while (*buffer < alignment) {
125
+ if (**buffer == c)
126
+ return 1;
127
+ *buffer += 1;
128
+ }
129
+ }
130
+ }
131
+ const uint8_t *limit64 = (uint8_t *)((uintptr_t)limit & (~(uintptr_t)7));
132
+ #else
133
+ const uint8_t *limit64 = (uint8_t *)limit - 7;
134
+ #endif
135
+ uint64_t wanted1 = 0x0101010101010101ULL * c;
136
+ for (; *buffer < limit64; *buffer += 8) {
137
+ const uint64_t eq1 = ~((*((uint64_t *)*buffer)) ^ wanted1);
138
+ const uint64_t t0 = (eq1 & 0x7f7f7f7f7f7f7f7fllu) + 0x0101010101010101llu;
139
+ const uint64_t t1 = (eq1 & 0x8080808080808080llu);
140
+ if ((t0 & t1)) {
141
+ break;
142
+ }
143
+ }
144
+ #if !ALLOW_UNALIGNED_MEMORY_ACCESS || !defined(__x86_64__)
145
+ finish:
146
+ #endif
147
+ while (*buffer < limit) {
148
+ if (**buffer == c)
149
+ return 1;
150
+ (*buffer)++;
151
+ }
152
+ return 0;
153
+ }
154
+
155
+ #else
156
+
157
+ /* a helper that seeks any char, converts it to NUL and returns 1 if found. */
158
+ inline static uint8_t seek2ch(uint8_t **pos, const uint8_t *limit, uint8_t ch) {
159
+ /* This is library based alternative that is sometimes slower */
160
+ if (*pos >= limit || **pos == ch) {
161
+ return 0;
162
+ }
163
+ uint8_t *tmp = (uint8_t *)memchr(*pos, ch, limit - (*pos));
164
+ if (tmp) {
165
+ *pos = tmp;
166
+ return 1;
167
+ }
168
+ *pos = (uint8_t *)limit;
169
+ return 0;
170
+ }
171
+
172
+ #endif
173
+
174
+ /* *****************************************************************************
175
+ Parsing RESP requests
176
+ ***************************************************************************** */
177
+
178
+ /**
179
+ * Returns the number of bytes to be resent. i.e., for a return value 5, the
180
+ * last 5 bytes in the buffer need to be resent to the parser.
181
+ */
182
+ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
183
+ size_t length) {
184
+ if (!parser->obj_countdown)
185
+ parser->obj_countdown = 1; /* always expect something... */
186
+ uint8_t *pos = (uint8_t *)buffer;
187
+ const uint8_t *stop = pos + length;
188
+ while (pos < stop) {
189
+ uint8_t *eol;
190
+ if (parser->expecting) {
191
+ if (pos + parser->expecting + 2 > stop) {
192
+ /* read, but make sure the buffer includes the new line markers */
193
+ size_t tmp = (size_t)((uintptr_t)stop - (uintptr_t)pos);
194
+ if ((intptr_t)tmp >= parser->expecting)
195
+ tmp = parser->expecting - 1;
196
+ resp_on_string_chunk(parser, (void *)pos, tmp);
197
+ parser->expecting -= tmp;
198
+ return (size_t)((uintptr_t)stop - ((uintptr_t)pos + tmp)); /* 0 or 1 */
199
+ } else {
200
+ resp_on_string_chunk(parser, (void *)pos, parser->expecting);
201
+ resp_on_end_string(parser);
202
+ pos += parser->expecting;
203
+ if (pos[0] == '\r')
204
+ ++pos;
205
+ if (pos[0] == '\n')
206
+ ++pos;
207
+ parser->expecting = 0;
208
+ --parser->obj_countdown;
209
+ if (parser->obj_countdown <= 0) {
210
+ parser->obj_countdown = 1;
211
+ if (resp_on_message(parser))
212
+ goto finish;
213
+ }
214
+ continue;
215
+ }
216
+ }
217
+ eol = pos;
218
+ if (seek2ch(&eol, stop, '\n') == 0)
219
+ break;
220
+ switch (*pos) {
221
+ case '+':
222
+ if (pos[1] == 'O' && pos[2] == 'K' && pos[3] == '\r' && pos[4] == '\n') {
223
+ resp_on_okay(parser);
224
+ --parser->obj_countdown;
225
+ break;
226
+ }
227
+ if (resp_on_start_string(parser,
228
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 2))) {
229
+ pos = eol + 1;
230
+ goto finish;
231
+ }
232
+ resp_on_string_chunk(parser, (void *)(pos + 1),
233
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 1));
234
+ resp_on_end_string(parser);
235
+ --parser->obj_countdown;
236
+ break;
237
+ case '-':
238
+ resp_on_err_msg(parser, pos,
239
+ (size_t)((uintptr_t)eol - (uintptr_t)pos - 1));
240
+ --parser->obj_countdown;
241
+ break;
242
+ case '*': /* fallthrough */
243
+ case '$': /* fallthrough */
244
+ case ':': {
245
+ uint8_t id = *pos;
246
+ uint8_t inv = 0;
247
+ int64_t i = 0;
248
+ ++pos;
249
+ if (pos[0] == '-') {
250
+ inv = 1;
251
+ ++pos;
252
+ }
253
+ while ((size_t)(pos[0] - (uint8_t)'0') <= 9) {
254
+ i = (i * 10) + (pos[0] - ((uint8_t)'0'));
255
+ ++pos;
256
+ }
257
+ if (inv)
258
+ i = i * -1;
259
+
260
+ switch (id) {
261
+ case ':':
262
+ resp_on_number(parser, i);
263
+ --parser->obj_countdown;
264
+ break;
265
+ case '$':
266
+ if (i < 0) {
267
+ resp_on_null(parser);
268
+ --parser->obj_countdown;
269
+ } else if (i == 0) {
270
+ resp_on_start_string(parser, 0);
271
+ resp_on_end_string(parser);
272
+ --parser->obj_countdown;
273
+ eol += 2; /* consume the extra "\r\n" */
274
+ } else {
275
+ if (resp_on_start_string(parser, i)) {
276
+ pos = eol + 1;
277
+ goto finish;
278
+ }
279
+ parser->expecting = i;
280
+ }
281
+ break;
282
+ case '*':
283
+ if (i < 0) {
284
+ resp_on_null(parser);
285
+ } else {
286
+ if (resp_on_start_array(parser, i)) {
287
+ pos = eol + 1;
288
+ goto finish;
289
+ }
290
+ parser->obj_countdown += i;
291
+ }
292
+ --parser->obj_countdown;
293
+ break;
294
+ }
295
+ } break;
296
+ default:
297
+ if (!parser->obj_countdown && !parser->expecting) {
298
+ /* possible (probable) inline command... for server authoring. */
299
+ /* Not Supported, PRs are welcome. */
300
+ resp_on_parser_error(parser);
301
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
302
+ } else {
303
+ resp_on_parser_error(parser);
304
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
305
+ }
306
+ }
307
+ pos = eol + 1;
308
+ if (parser->obj_countdown <= 0 && !parser->expecting) {
309
+ parser->obj_countdown = 1;
310
+ resp_on_message(parser);
311
+ }
312
+ }
313
+ finish:
314
+ return (size_t)((uintptr_t)stop - (uintptr_t)pos);
315
+ }
316
+
317
+ #endif /* H_RESP_PARSER_H */