rage-iodine 1.7.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/workflows/ruby.yml +42 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +1098 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/LIMITS.md +41 -0
- data/README.md +782 -0
- data/Rakefile +23 -0
- data/SPEC-PubSub-Draft.md +159 -0
- data/SPEC-WebSocket-Draft.md +239 -0
- data/bin/console +22 -0
- data/bin/info.md +353 -0
- data/bin/mustache_bench.rb +100 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/examples/async_task.ru +92 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/examples/config.ru +59 -0
- data/examples/echo.ru +59 -0
- data/examples/etag.ru +16 -0
- data/examples/hello.ru +29 -0
- data/examples/pubsub_engine.ru +81 -0
- data/examples/rack3.ru +12 -0
- data/examples/redis.ru +70 -0
- data/examples/shootout.ru +73 -0
- data/examples/sub-protocols.ru +90 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +280 -0
- data/ext/iodine/extconf.rb +110 -0
- data/ext/iodine/fio.c +12096 -0
- data/ext/iodine/fio.h +6390 -0
- data/ext/iodine/fio_cli.c +431 -0
- data/ext/iodine/fio_cli.h +189 -0
- data/ext/iodine/fio_json_parser.h +687 -0
- data/ext/iodine/fio_siphash.c +157 -0
- data/ext/iodine/fio_siphash.h +37 -0
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +649 -0
- data/ext/iodine/fio_tls_openssl.c +1056 -0
- data/ext/iodine/fio_tmpfile.h +50 -0
- data/ext/iodine/fiobj.h +44 -0
- data/ext/iodine/fiobj4fio.h +21 -0
- data/ext/iodine/fiobj_ary.c +333 -0
- data/ext/iodine/fiobj_ary.h +139 -0
- data/ext/iodine/fiobj_data.c +1185 -0
- data/ext/iodine/fiobj_data.h +167 -0
- data/ext/iodine/fiobj_hash.c +409 -0
- data/ext/iodine/fiobj_hash.h +176 -0
- data/ext/iodine/fiobj_json.c +622 -0
- data/ext/iodine/fiobj_json.h +68 -0
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +317 -0
- data/ext/iodine/fiobj_mustache.h +62 -0
- data/ext/iodine/fiobj_numbers.c +344 -0
- data/ext/iodine/fiobj_numbers.h +127 -0
- data/ext/iodine/fiobj_str.c +433 -0
- data/ext/iodine/fiobj_str.h +172 -0
- data/ext/iodine/fiobject.c +620 -0
- data/ext/iodine/fiobject.h +654 -0
- data/ext/iodine/hpack.h +1923 -0
- data/ext/iodine/http.c +2736 -0
- data/ext/iodine/http.h +1019 -0
- data/ext/iodine/http1.c +825 -0
- data/ext/iodine/http1.h +29 -0
- data/ext/iodine/http1_parser.h +1835 -0
- data/ext/iodine/http_internal.c +1279 -0
- data/ext/iodine/http_internal.h +248 -0
- data/ext/iodine/http_mime_parser.h +350 -0
- data/ext/iodine/iodine.c +1433 -0
- data/ext/iodine/iodine.h +64 -0
- data/ext/iodine/iodine_caller.c +218 -0
- data/ext/iodine/iodine_caller.h +27 -0
- data/ext/iodine/iodine_connection.c +941 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +420 -0
- data/ext/iodine/iodine_defer.h +6 -0
- data/ext/iodine/iodine_fiobj2rb.h +120 -0
- data/ext/iodine/iodine_helpers.c +282 -0
- data/ext/iodine/iodine_helpers.h +12 -0
- data/ext/iodine/iodine_http.c +1280 -0
- data/ext/iodine/iodine_http.h +23 -0
- data/ext/iodine/iodine_json.c +302 -0
- data/ext/iodine/iodine_json.h +6 -0
- data/ext/iodine/iodine_mustache.c +567 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +580 -0
- data/ext/iodine/iodine_pubsub.h +26 -0
- data/ext/iodine/iodine_rack_io.c +273 -0
- data/ext/iodine/iodine_rack_io.h +20 -0
- data/ext/iodine/iodine_store.c +142 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +346 -0
- data/ext/iodine/iodine_tcp.h +13 -0
- data/ext/iodine/iodine_tls.c +261 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1546 -0
- data/ext/iodine/redis_engine.c +957 -0
- data/ext/iodine/redis_engine.h +79 -0
- data/ext/iodine/resp_parser.h +317 -0
- data/ext/iodine/scheduler.c +173 -0
- data/ext/iodine/scheduler.h +6 -0
- data/ext/iodine/websocket_parser.h +506 -0
- data/ext/iodine/websockets.c +752 -0
- data/ext/iodine/websockets.h +185 -0
- data/iodine.gemspec +50 -0
- data/lib/iodine/connection.rb +61 -0
- data/lib/iodine/json.rb +42 -0
- data/lib/iodine/mustache.rb +113 -0
- data/lib/iodine/pubsub.rb +55 -0
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +3 -0
- data/lib/iodine.rb +274 -0
- data/lib/rack/handler/iodine.rb +33 -0
- data/logo.png +0 -0
- metadata +284 -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 */
|
@@ -0,0 +1,173 @@
|
|
1
|
+
#include "iodine.h"
|
2
|
+
#include "iodine_store.h"
|
3
|
+
#include "ruby.h"
|
4
|
+
|
5
|
+
#include <stddef.h>
|
6
|
+
#include <stdint.h>
|
7
|
+
#include <stdio.h>
|
8
|
+
// clang-format on
|
9
|
+
|
10
|
+
#include "fio.h"
|
11
|
+
|
12
|
+
#define IO_MAX_READ 8192
|
13
|
+
|
14
|
+
static ID call_id;
|
15
|
+
static uint8_t ATTACH_ON_READ_READY_CALLBACK;
|
16
|
+
static uint8_t ATTACH_ON_WRITE_READY_CALLBACK;
|
17
|
+
|
18
|
+
/* *****************************************************************************
|
19
|
+
Fiber Scheduler API
|
20
|
+
***************************************************************************** */
|
21
|
+
|
22
|
+
static void noop(intptr_t uuid, fio_protocol_s *protocol) {
|
23
|
+
(void)uuid;
|
24
|
+
(void)protocol;
|
25
|
+
}
|
26
|
+
|
27
|
+
typedef struct {
|
28
|
+
fio_protocol_s p;
|
29
|
+
VALUE block;
|
30
|
+
} scheduler_protocol_s;
|
31
|
+
|
32
|
+
static void iodine_scheduler_perform(intptr_t uuid, fio_protocol_s *fio_protocol) {
|
33
|
+
scheduler_protocol_s *protocol = (scheduler_protocol_s *)fio_protocol;
|
34
|
+
VALUE block = protocol->block;
|
35
|
+
|
36
|
+
IodineCaller.call(block, call_id);
|
37
|
+
|
38
|
+
IodineStore.remove(block);
|
39
|
+
fio_free(protocol);
|
40
|
+
|
41
|
+
(void)uuid;
|
42
|
+
}
|
43
|
+
|
44
|
+
static VALUE iodine_scheduler_attach(VALUE self, VALUE r_fd, VALUE r_waittype, VALUE r_timeout) {
|
45
|
+
Check_Type(r_fd, T_FIXNUM);
|
46
|
+
int fd = FIX2INT(r_fd);
|
47
|
+
|
48
|
+
Check_Type(r_waittype, T_FIXNUM);
|
49
|
+
size_t waittype = FIX2UINT(r_waittype);
|
50
|
+
|
51
|
+
Check_Type(r_timeout, T_FIXNUM);
|
52
|
+
size_t timeout = FIX2UINT(r_timeout);
|
53
|
+
|
54
|
+
fio_set_non_block(fd);
|
55
|
+
|
56
|
+
rb_need_block();
|
57
|
+
VALUE block = IodineStore.add(rb_block_proc());
|
58
|
+
|
59
|
+
scheduler_protocol_s *protocol = fio_malloc(sizeof(*protocol));
|
60
|
+
FIO_ASSERT_ALLOC(protocol);
|
61
|
+
|
62
|
+
if ((waittype & ATTACH_ON_READ_READY_CALLBACK) && (waittype & ATTACH_ON_WRITE_READY_CALLBACK)) {
|
63
|
+
*protocol = (scheduler_protocol_s){
|
64
|
+
.p.on_data = iodine_scheduler_perform,
|
65
|
+
.p.on_ready = iodine_scheduler_perform,
|
66
|
+
.p.on_close = noop,
|
67
|
+
.p.ping = noop,
|
68
|
+
.block = block,
|
69
|
+
};
|
70
|
+
} else if (waittype & ATTACH_ON_READ_READY_CALLBACK) {
|
71
|
+
*protocol = (scheduler_protocol_s){
|
72
|
+
.p.on_data = iodine_scheduler_perform,
|
73
|
+
.p.on_ready = noop,
|
74
|
+
.p.on_close = noop,
|
75
|
+
.p.ping = noop,
|
76
|
+
.block = block,
|
77
|
+
};
|
78
|
+
} else if (waittype & ATTACH_ON_WRITE_READY_CALLBACK) {
|
79
|
+
*protocol = (scheduler_protocol_s){
|
80
|
+
.p.on_data = noop,
|
81
|
+
.p.on_ready = iodine_scheduler_perform,
|
82
|
+
.p.on_close = noop,
|
83
|
+
.p.ping = noop,
|
84
|
+
.block = block,
|
85
|
+
};
|
86
|
+
}
|
87
|
+
|
88
|
+
intptr_t uuid = fio_fd2uuid(fd);
|
89
|
+
if (timeout) {
|
90
|
+
fio_timeout_set(uuid, timeout);
|
91
|
+
}
|
92
|
+
|
93
|
+
fio_watch(uuid, (fio_protocol_s *)protocol);
|
94
|
+
|
95
|
+
return LONG2NUM(uuid);
|
96
|
+
(void)self;
|
97
|
+
}
|
98
|
+
|
99
|
+
static VALUE iodine_scheduler_write(VALUE self, VALUE r_fd, VALUE r_buffer, VALUE r_length, VALUE r_offset) {
|
100
|
+
Check_Type(r_fd, T_FIXNUM);
|
101
|
+
int fd = FIX2INT(r_fd);
|
102
|
+
|
103
|
+
Check_Type(r_buffer, T_STRING);
|
104
|
+
char *buffer = RSTRING_PTR(r_buffer);
|
105
|
+
|
106
|
+
Check_Type(r_length, T_FIXNUM);
|
107
|
+
int length = FIX2INT(r_length);
|
108
|
+
|
109
|
+
Check_Type(r_offset, T_FIXNUM);
|
110
|
+
int offset = FIX2INT(r_offset);
|
111
|
+
|
112
|
+
fio_write2(fio_fd2uuid(fd), .data.buffer = buffer, .length = length, .offset = offset,
|
113
|
+
.after.dealloc = FIO_DEALLOC_NOOP);
|
114
|
+
|
115
|
+
return r_length;
|
116
|
+
|
117
|
+
(void)self;
|
118
|
+
}
|
119
|
+
|
120
|
+
static VALUE iodine_scheduler_read(VALUE self, VALUE r_fd, VALUE r_length, VALUE r_offset) {
|
121
|
+
Check_Type(r_fd, T_FIXNUM);
|
122
|
+
int fd = FIX2INT(r_fd);
|
123
|
+
|
124
|
+
Check_Type(r_length, T_FIXNUM);
|
125
|
+
int length = FIX2INT(r_length);
|
126
|
+
|
127
|
+
if (length == 0) {
|
128
|
+
length = IO_MAX_READ;
|
129
|
+
}
|
130
|
+
|
131
|
+
intptr_t uuid = fio_fd2uuid(fd);
|
132
|
+
char buffer[length];
|
133
|
+
|
134
|
+
ssize_t len = fio_read_unsafe(uuid, &buffer, length);
|
135
|
+
if (len == -1) {
|
136
|
+
return Qnil;
|
137
|
+
}
|
138
|
+
|
139
|
+
return rb_str_new(buffer, len);
|
140
|
+
|
141
|
+
(void)self;
|
142
|
+
(void)r_offset;
|
143
|
+
}
|
144
|
+
|
145
|
+
static VALUE iodine_scheduler_close(VALUE self) {
|
146
|
+
fio_defer_perform();
|
147
|
+
while (fio_flush_all()) {}
|
148
|
+
|
149
|
+
return Qtrue;
|
150
|
+
(void)self;
|
151
|
+
}
|
152
|
+
|
153
|
+
/* *****************************************************************************
|
154
|
+
Scheduler initialization
|
155
|
+
***************************************************************************** */
|
156
|
+
|
157
|
+
void iodine_scheduler_initialize(void) {
|
158
|
+
call_id = rb_intern2("call", 4);
|
159
|
+
|
160
|
+
VALUE SchedulerModule = rb_define_module_under(IodineModule, "Scheduler");
|
161
|
+
|
162
|
+
rb_define_module_function(SchedulerModule, "attach", iodine_scheduler_attach, 3);
|
163
|
+
rb_define_module_function(SchedulerModule, "write", iodine_scheduler_write, 4);
|
164
|
+
rb_define_module_function(SchedulerModule, "read", iodine_scheduler_read, 3);
|
165
|
+
rb_define_module_function(SchedulerModule, "close", iodine_scheduler_close, 0);
|
166
|
+
|
167
|
+
VALUE cIO = rb_const_get(rb_cObject, rb_intern2("IO", 2));
|
168
|
+
VALUE io_readable = rb_const_get(cIO, rb_intern2("READABLE", 8));
|
169
|
+
VALUE io_writable = rb_const_get(cIO, rb_intern2("WRITABLE", 8));
|
170
|
+
|
171
|
+
ATTACH_ON_READ_READY_CALLBACK = NUM2SHORT(io_readable);
|
172
|
+
ATTACH_ON_WRITE_READY_CALLBACK = NUM2SHORT(io_writable);
|
173
|
+
}
|