opal-up 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +209 -0
- data/README.md +97 -29
- data/bin/up_ruby +4 -0
- data/bin/up_ruby_cluster +4 -0
- data/ext/up_ext/App.h +606 -0
- data/ext/up_ext/AsyncSocket.h +355 -0
- data/ext/up_ext/AsyncSocketData.h +87 -0
- data/ext/up_ext/BloomFilter.h +83 -0
- data/ext/up_ext/ChunkedEncoding.h +236 -0
- data/ext/up_ext/ClientApp.h +36 -0
- data/ext/up_ext/HttpContext.h +502 -0
- data/ext/up_ext/HttpContextData.h +56 -0
- data/ext/up_ext/HttpErrors.h +53 -0
- data/ext/up_ext/HttpParser.h +680 -0
- data/ext/up_ext/HttpResponse.h +578 -0
- data/ext/up_ext/HttpResponseData.h +95 -0
- data/ext/up_ext/HttpRouter.h +380 -0
- data/ext/up_ext/Loop.h +204 -0
- data/ext/up_ext/LoopData.h +112 -0
- data/ext/up_ext/MoveOnlyFunction.h +377 -0
- data/ext/up_ext/PerMessageDeflate.h +315 -0
- data/ext/up_ext/ProxyParser.h +163 -0
- data/ext/up_ext/QueryParser.h +120 -0
- data/ext/up_ext/TopicTree.h +363 -0
- data/ext/up_ext/Utilities.h +66 -0
- data/ext/up_ext/WebSocket.h +381 -0
- data/ext/up_ext/WebSocketContext.h +434 -0
- data/ext/up_ext/WebSocketContextData.h +109 -0
- data/ext/up_ext/WebSocketData.h +86 -0
- data/ext/up_ext/WebSocketExtensions.h +256 -0
- data/ext/up_ext/WebSocketHandshake.h +145 -0
- data/ext/up_ext/WebSocketProtocol.h +506 -0
- data/ext/up_ext/bsd.c +767 -0
- data/ext/up_ext/bsd.h +109 -0
- data/ext/up_ext/context.c +524 -0
- data/ext/up_ext/epoll_kqueue.c +458 -0
- data/ext/up_ext/epoll_kqueue.h +67 -0
- data/ext/up_ext/extconf.rb +5 -0
- data/ext/up_ext/internal.h +224 -0
- data/ext/up_ext/libusockets.h +350 -0
- data/ext/up_ext/libuwebsockets.cpp +1344 -0
- data/ext/up_ext/libuwebsockets.h +396 -0
- data/ext/up_ext/loop.c +386 -0
- data/ext/up_ext/loop_data.h +38 -0
- data/ext/up_ext/socket.c +231 -0
- data/ext/up_ext/up_ext.c +930 -0
- data/lib/up/bun/rack_env.rb +1 -13
- data/lib/up/bun/server.rb +93 -19
- data/lib/up/cli.rb +3 -0
- data/lib/up/client.rb +68 -0
- data/lib/up/ruby/cluster.rb +39 -0
- data/lib/up/ruby/cluster_cli.rb +10 -0
- data/lib/up/{node → ruby}/rack_cluster.rb +5 -4
- data/lib/up/{node → ruby}/rack_server.rb +4 -4
- data/lib/up/ruby/server_cli.rb +10 -0
- data/lib/up/u_web_socket/cluster.rb +18 -3
- data/lib/up/u_web_socket/server.rb +108 -15
- data/lib/up/version.rb +1 -1
- metadata +72 -30
- data/.gitignore +0 -5
- data/Gemfile +0 -2
- data/bin/up_node +0 -12
- data/bin/up_node_cluster +0 -12
- data/example_rack_app/Gemfile +0 -3
- data/example_rack_app/config.ru +0 -6
- data/example_rack_app/rack_app.rb +0 -5
- data/example_roda_app/Gemfile +0 -6
- data/example_roda_app/config.ru +0 -6
- data/example_roda_app/roda_app.rb +0 -37
- data/example_sinatra_app/Gemfile +0 -6
- data/example_sinatra_app/config.ru +0 -6
- data/example_sinatra_app/sinatra_app.rb +0 -7
- data/lib/up/node/cluster.rb +0 -39
- data/lib/up/node/cluster_cli.rb +0 -15
- data/lib/up/node/rack_env.rb +0 -106
- data/lib/up/node/server.rb +0 -84
- data/lib/up/node/server_cli.rb +0 -15
- data/lib/up/u_web_socket/rack_env.rb +0 -101
- data/opal-up.gemspec +0 -27
- data/up_logo.svg +0 -256
data/ext/up_ext/up_ext.c
ADDED
@@ -0,0 +1,930 @@
|
|
1
|
+
#include "libusockets.h"
|
2
|
+
#include "libuwebsockets.h"
|
3
|
+
#include <arpa/inet.h>
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <ruby/encoding.h>
|
6
|
+
#include <sys/socket.h>
|
7
|
+
#include <sys/wait.h>
|
8
|
+
#include <unistd.h>
|
9
|
+
|
10
|
+
#define USE_SSL 0
|
11
|
+
#define MAX_HEADER_KEY_BUF 256
|
12
|
+
#define MAX_HEADER_KEY_LEN 255
|
13
|
+
#define INTERNAL_PUBLISH_PATH "/__up__cluster__publish__"
|
14
|
+
|
15
|
+
static VALUE mUp;
|
16
|
+
static VALUE mRuby;
|
17
|
+
static VALUE cServer;
|
18
|
+
static VALUE cClient;
|
19
|
+
static VALUE cStringIO;
|
20
|
+
static VALUE cLogger;
|
21
|
+
|
22
|
+
static ID at_env;
|
23
|
+
static ID at_handler;
|
24
|
+
static ID at_member_id;
|
25
|
+
static ID at_open;
|
26
|
+
static ID at_protocol;
|
27
|
+
static ID at_secret;
|
28
|
+
static ID at_server;
|
29
|
+
static ID at_timeout;
|
30
|
+
static ID at_workers;
|
31
|
+
static ID id_app;
|
32
|
+
static ID id_call;
|
33
|
+
static ID id_close;
|
34
|
+
static ID id_each;
|
35
|
+
static ID id_host;
|
36
|
+
static ID id_logger;
|
37
|
+
static ID id_on_close;
|
38
|
+
static ID id_on_drained;
|
39
|
+
static ID id_on_message;
|
40
|
+
static ID id_on_open;
|
41
|
+
static ID id_port;
|
42
|
+
|
43
|
+
static rb_encoding *utf8_encoding;
|
44
|
+
static rb_encoding *binary_encoding;
|
45
|
+
|
46
|
+
static VALUE default_input;
|
47
|
+
static VALUE default_logger;
|
48
|
+
|
49
|
+
static VALUE rack_env_template;
|
50
|
+
|
51
|
+
static VALUE empty_string;
|
52
|
+
static VALUE http11;
|
53
|
+
static VALUE rack_input;
|
54
|
+
static VALUE rack_logger;
|
55
|
+
static VALUE rack_upgrade_q;
|
56
|
+
static VALUE rack_upgrade;
|
57
|
+
static VALUE sym_websocket;
|
58
|
+
|
59
|
+
static VALUE HTTP_VERSION;
|
60
|
+
static VALUE PATH_INFO;
|
61
|
+
static VALUE QUERY_STRING;
|
62
|
+
static VALUE REQUEST_METHOD;
|
63
|
+
static VALUE SCRIPT_NAME;
|
64
|
+
static VALUE SERVER_NAME;
|
65
|
+
static VALUE SERVER_PORT;
|
66
|
+
static VALUE SERVER_PROTOCOL;
|
67
|
+
|
68
|
+
#define set_str_val(gl_name, str) \
|
69
|
+
rb_gc_register_address(&gl_name); \
|
70
|
+
(gl_name) = rb_enc_str_new((str), strlen((str)), binary_encoding); \
|
71
|
+
rb_obj_freeze(gl_name);
|
72
|
+
|
73
|
+
#define set_sym_val(gl_name, str) \
|
74
|
+
rb_gc_register_address(&gl_name); \
|
75
|
+
(gl_name) = ID2SYM(rb_intern(str));
|
76
|
+
|
77
|
+
#define set_global(global_name) set_str_val((global_name), #global_name)
|
78
|
+
|
79
|
+
#define to_upper(c) (((c) >= 'a' && (c) <= 'z') ? ((c) & ~32) : (c))
|
80
|
+
|
81
|
+
static inline long ltoa(char *dest, long value) {
|
82
|
+
char *ptr = dest, *ptr1 = dest, tmp_char;
|
83
|
+
long tmp;
|
84
|
+
|
85
|
+
do {
|
86
|
+
tmp = value;
|
87
|
+
value /= 10;
|
88
|
+
*ptr++ = "0123456789"[(tmp - value * 10)];
|
89
|
+
} while (value);
|
90
|
+
|
91
|
+
tmp = ptr - ptr1;
|
92
|
+
*ptr-- = '\0';
|
93
|
+
|
94
|
+
while (ptr1 < ptr) {
|
95
|
+
tmp_char = *ptr;
|
96
|
+
*ptr-- = *ptr1;
|
97
|
+
*ptr1++ = tmp_char;
|
98
|
+
}
|
99
|
+
return tmp;
|
100
|
+
}
|
101
|
+
|
102
|
+
VALUE up_internal_handle_part(RB_BLOCK_CALL_FUNC_ARGLIST(rpart, res)) {
|
103
|
+
if (TYPE(rpart) == T_STRING)
|
104
|
+
uws_res_write(USE_SSL, (uws_res_t *)res, RSTRING_PTR(rpart),
|
105
|
+
RSTRING_LEN(rpart));
|
106
|
+
return Qnil;
|
107
|
+
}
|
108
|
+
|
109
|
+
typedef struct server_s {
|
110
|
+
VALUE self;
|
111
|
+
uws_app_t *app;
|
112
|
+
VALUE rapp;
|
113
|
+
VALUE host;
|
114
|
+
VALUE port;
|
115
|
+
VALUE logger;
|
116
|
+
VALUE env_template;
|
117
|
+
int workers;
|
118
|
+
int member_id;
|
119
|
+
char secret[37];
|
120
|
+
} server_s;
|
121
|
+
|
122
|
+
static void up_internal_req_header_handler(const char *h, size_t h_len,
|
123
|
+
const char *v, size_t v_len,
|
124
|
+
void *renv) {
|
125
|
+
char header_key[MAX_HEADER_KEY_BUF] = {'H', 'T', 'T', 'P', '_', '\0'};
|
126
|
+
if ((h_len + 5) > MAX_HEADER_KEY_LEN)
|
127
|
+
h_len = MAX_HEADER_KEY_LEN - 5;
|
128
|
+
|
129
|
+
for (size_t i = 0; i < h_len; ++i) {
|
130
|
+
header_key[i + 5] = (h[i] == '-') ? '_' : to_upper(h[i]);
|
131
|
+
}
|
132
|
+
|
133
|
+
header_key[h_len + 5] = '\0';
|
134
|
+
rb_hash_aset((VALUE)renv,
|
135
|
+
rb_enc_str_new(header_key, h_len + 5, binary_encoding),
|
136
|
+
rb_enc_str_new(v, v_len, binary_encoding));
|
137
|
+
}
|
138
|
+
|
139
|
+
static void up_server_prepare_env(VALUE renv, uws_req_t *req) {
|
140
|
+
// The HTTP request method, such as “GET” or “POST”. This cannot ever be an
|
141
|
+
// empty string, and so is always required.
|
142
|
+
const char *str;
|
143
|
+
size_t len = uws_req_get_method(req, &str);
|
144
|
+
char m[20];
|
145
|
+
if (len > 19)
|
146
|
+
len = 19;
|
147
|
+
for (size_t i = 0; i < len; ++i) {
|
148
|
+
m[i] = (str[i] == '-') ? '_' : to_upper(str[i]);
|
149
|
+
}
|
150
|
+
rb_hash_aset(renv, REQUEST_METHOD, rb_enc_str_new(m, len, binary_encoding));
|
151
|
+
|
152
|
+
// The remainder of the request URL’s “path”, designating the virtual
|
153
|
+
// “location” of the request’s target within the application.
|
154
|
+
len = uws_req_get_url(req, &str);
|
155
|
+
rb_hash_aset(renv, PATH_INFO, rb_enc_str_new(str, len, binary_encoding));
|
156
|
+
|
157
|
+
// The portion of the request URL that follows the ?, if any. May be empty,
|
158
|
+
// but is always required!
|
159
|
+
len = uws_req_get_query(req, NULL, 0, &str);
|
160
|
+
if (len > 0)
|
161
|
+
rb_hash_aset(renv, QUERY_STRING, rb_enc_str_new(str, len, binary_encoding));
|
162
|
+
|
163
|
+
uws_req_for_each_header(req, up_internal_req_header_handler, (void *)renv);
|
164
|
+
}
|
165
|
+
|
166
|
+
static int up_internal_res_header_handler(VALUE key, VALUE data, VALUE arg) {
|
167
|
+
char header_key[MAX_HEADER_KEY_BUF];
|
168
|
+
|
169
|
+
uws_res_t *res = (uws_res_t *)arg;
|
170
|
+
int kt = TYPE(key), dt = TYPE(data);
|
171
|
+
if (dt == T_NIL || kt == T_NIL)
|
172
|
+
return ST_CONTINUE;
|
173
|
+
if (dt == T_ARRAY) {
|
174
|
+
for (long i = 0, end = RARRAY_LEN(data); i < end; ++i) {
|
175
|
+
if (up_internal_res_header_handler(key, rb_ary_entry(data, i), arg) ==
|
176
|
+
ST_CONTINUE)
|
177
|
+
continue;
|
178
|
+
return ST_STOP;
|
179
|
+
}
|
180
|
+
return ST_CONTINUE;
|
181
|
+
}
|
182
|
+
if (kt != T_STRING) {
|
183
|
+
key = rb_obj_as_string(key);
|
184
|
+
if (TYPE(key) != T_STRING)
|
185
|
+
return ST_CONTINUE;
|
186
|
+
}
|
187
|
+
if (dt != T_STRING) {
|
188
|
+
data = rb_obj_as_string(data);
|
189
|
+
if (TYPE(data) != T_STRING)
|
190
|
+
return ST_CONTINUE;
|
191
|
+
}
|
192
|
+
char *key_s = RSTRING_PTR(key);
|
193
|
+
int key_len = RSTRING_LEN(key);
|
194
|
+
char *data_s = RSTRING_PTR(data);
|
195
|
+
int data_len = RSTRING_LEN(data);
|
196
|
+
|
197
|
+
if (key_len > MAX_HEADER_KEY_LEN)
|
198
|
+
key_len = MAX_HEADER_KEY_LEN;
|
199
|
+
|
200
|
+
for (int i = 0; i < key_len; ++i) {
|
201
|
+
header_key[i] = tolower(key_s[i]);
|
202
|
+
}
|
203
|
+
|
204
|
+
// scan the value for newline (\n) delimiters
|
205
|
+
char *pos_s = data_s;
|
206
|
+
char *pos_e = data_s + data_len;
|
207
|
+
while (pos_s < pos_e) {
|
208
|
+
// scanning for newline (\n) delimiters
|
209
|
+
char *const start = pos_s;
|
210
|
+
pos_s = memchr(pos_s, '\n', pos_e - pos_s);
|
211
|
+
if (!pos_s)
|
212
|
+
pos_s = pos_e;
|
213
|
+
uws_res_write_header(USE_SSL, res, header_key, key_len, start,
|
214
|
+
pos_s - start);
|
215
|
+
// move forward (skip the '\n' if exists)
|
216
|
+
++pos_s;
|
217
|
+
}
|
218
|
+
|
219
|
+
// no errors, return 0
|
220
|
+
return ST_CONTINUE;
|
221
|
+
RB_GC_GUARD(key);
|
222
|
+
RB_GC_GUARD(data);
|
223
|
+
}
|
224
|
+
|
225
|
+
static bool up_internal_set_response_status(uws_res_t *res, VALUE rstatus) {
|
226
|
+
char status[10];
|
227
|
+
int type = TYPE(rstatus);
|
228
|
+
long a_long;
|
229
|
+
if (type == T_FIXNUM) {
|
230
|
+
a_long = FIX2INT(rstatus);
|
231
|
+
if (a_long < 0 || a_long > 999)
|
232
|
+
return false;
|
233
|
+
a_long = ltoa(status, a_long);
|
234
|
+
} else if (type == T_STRING) {
|
235
|
+
a_long = RSTRING_LEN(rstatus);
|
236
|
+
if (a_long > 6)
|
237
|
+
a_long = 6;
|
238
|
+
memcpy(status, RSTRING_PTR(rstatus), a_long);
|
239
|
+
} else {
|
240
|
+
return false;
|
241
|
+
}
|
242
|
+
memcpy(status + a_long, " OK", 4); // copy the '\0' too
|
243
|
+
uws_res_write_status(USE_SSL, res, status, a_long + 3);
|
244
|
+
return true;
|
245
|
+
}
|
246
|
+
|
247
|
+
static bool up_internal_collect_response_body(uws_res_t *res, VALUE rparts) {
|
248
|
+
VALUE rpart;
|
249
|
+
if (TYPE(rparts) == T_ARRAY) {
|
250
|
+
long i, l = RARRAY_LEN(rparts);
|
251
|
+
for (i = 0; i < l; i++) {
|
252
|
+
rpart = rb_ary_entry(rparts, i);
|
253
|
+
if (TYPE(rpart) != T_STRING)
|
254
|
+
return false;
|
255
|
+
uws_res_write(USE_SSL, res, RSTRING_PTR(rpart), RSTRING_LEN(rpart));
|
256
|
+
}
|
257
|
+
} else if (rb_respond_to(rparts, id_each)) {
|
258
|
+
rb_block_call(rparts, id_each, 0, NULL, up_internal_handle_part,
|
259
|
+
(VALUE)res);
|
260
|
+
} else if (rb_respond_to(rparts, id_call)) {
|
261
|
+
rpart = rb_funcall(rparts, id_call, 0);
|
262
|
+
if (TYPE(rpart) != T_STRING)
|
263
|
+
return false;
|
264
|
+
uws_res_write(USE_SSL, res, RSTRING_PTR(rpart), RSTRING_LEN(rpart));
|
265
|
+
} else {
|
266
|
+
return false;
|
267
|
+
}
|
268
|
+
return true;
|
269
|
+
}
|
270
|
+
|
271
|
+
typedef struct publish_data_s {
|
272
|
+
int pos;
|
273
|
+
const char *data[2];
|
274
|
+
size_t lengths[2];
|
275
|
+
server_s *s;
|
276
|
+
} publish_data_s;
|
277
|
+
|
278
|
+
static void up_internal_process_publish_post_data(uws_res_t *res,
|
279
|
+
const char *chunk,
|
280
|
+
size_t chunk_length,
|
281
|
+
bool is_end, void *arg) {
|
282
|
+
server_s *s = (server_s *)arg;
|
283
|
+
const char *channel_start = chunk, *chunk_ptr = chunk,
|
284
|
+
*chunk_end = chunk + chunk_length, *message_start = NULL;
|
285
|
+
size_t channel_length = 0, message_length = 0;
|
286
|
+
for (; chunk_ptr < chunk_end; chunk_ptr++) {
|
287
|
+
if (*chunk_ptr == '\r') {
|
288
|
+
channel_length = chunk_ptr - chunk;
|
289
|
+
message_start = chunk + channel_length + 2;
|
290
|
+
message_length = chunk_length - 2 - channel_length;
|
291
|
+
break;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
if (channel_length > 0 && message_length > 0) {
|
295
|
+
uws_publish(USE_SSL, s->app, channel_start, channel_length, message_start,
|
296
|
+
message_length, TEXT, false);
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
static void up_internal_publish_handler(uws_res_t *res, uws_req_t *req,
|
301
|
+
void *arg) {
|
302
|
+
server_s *s = (server_s *)arg;
|
303
|
+
// check for header
|
304
|
+
const char *secret;
|
305
|
+
uws_req_get_header(req, "secret", 6, &secret);
|
306
|
+
if (secret && (strncmp(s->secret, secret, 36) == 0)) {
|
307
|
+
// ok, requests origin knows the secret, continue processing
|
308
|
+
uws_res_on_data(false, res, up_internal_process_publish_post_data, arg);
|
309
|
+
uws_res_write_status(false, res, "200 OK", 6);
|
310
|
+
uws_res_end_without_body(false, res, true);
|
311
|
+
} else {
|
312
|
+
// don't know the secret? bugger off!
|
313
|
+
uws_res_end_without_body(false, res, true);
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
static void up_server_request_handler(uws_res_t *res, uws_req_t *req,
|
318
|
+
void *arg) {
|
319
|
+
// prepare rack env
|
320
|
+
server_s *s = (server_s *)arg;
|
321
|
+
VALUE renv = rb_hash_dup(s->env_template);
|
322
|
+
up_server_prepare_env(renv, req);
|
323
|
+
|
324
|
+
// call app
|
325
|
+
VALUE rres = rb_funcall(s->rapp, id_call, 1, renv);
|
326
|
+
if (TYPE(rres) != T_ARRAY)
|
327
|
+
goto response_error;
|
328
|
+
|
329
|
+
// response status
|
330
|
+
VALUE rstatus = rb_ary_entry(rres, 0);
|
331
|
+
if (!up_internal_set_response_status(res, rstatus))
|
332
|
+
goto response_error;
|
333
|
+
|
334
|
+
// collect headers
|
335
|
+
VALUE rheaders = rb_ary_entry(rres, 1);
|
336
|
+
if (TYPE(rheaders) != T_HASH)
|
337
|
+
goto response_error;
|
338
|
+
rb_hash_foreach(rheaders, up_internal_res_header_handler, (VALUE)res);
|
339
|
+
|
340
|
+
// collect response body
|
341
|
+
VALUE rparts = rb_ary_entry(rres, 2);
|
342
|
+
up_internal_collect_response_body(res, rparts);
|
343
|
+
|
344
|
+
// end response
|
345
|
+
uws_res_end_without_body(USE_SSL, res, false);
|
346
|
+
|
347
|
+
// close resources if necessary
|
348
|
+
if (rb_respond_to(rparts, id_close))
|
349
|
+
rb_funcall(rparts, id_close, 0);
|
350
|
+
|
351
|
+
return;
|
352
|
+
RB_GC_GUARD(rstatus);
|
353
|
+
RB_GC_GUARD(rheaders);
|
354
|
+
RB_GC_GUARD(rres);
|
355
|
+
RB_GC_GUARD(renv);
|
356
|
+
response_error:
|
357
|
+
fprintf(stderr, "response error\n");
|
358
|
+
uws_res_end_without_body(USE_SSL, res, false);
|
359
|
+
}
|
360
|
+
|
361
|
+
static void up_server_listen_handler(struct us_listen_socket_t *listen_socket,
|
362
|
+
uws_app_listen_config_t config,
|
363
|
+
void *user_data) {
|
364
|
+
if (listen_socket)
|
365
|
+
fprintf(stderr, "Server is running on http://%s:%d\n", config.host,
|
366
|
+
config.port);
|
367
|
+
}
|
368
|
+
|
369
|
+
const rb_data_type_t up_client_t = {.wrap_struct_name = "Up::Client",
|
370
|
+
.function = {.dmark = NULL,
|
371
|
+
.dfree = NULL,
|
372
|
+
.dsize = NULL,
|
373
|
+
.dcompact = NULL,
|
374
|
+
.reserved = {0}},
|
375
|
+
.parent = NULL,
|
376
|
+
.data = NULL,
|
377
|
+
.flags = 0};
|
378
|
+
|
379
|
+
static VALUE up_client_alloc(VALUE rclass) {
|
380
|
+
return TypedData_Wrap_Struct(rclass, &up_client_t, NULL);
|
381
|
+
}
|
382
|
+
|
383
|
+
static VALUE up_client_close(VALUE self) {
|
384
|
+
uws_websocket_t *ws = DATA_PTR(self);
|
385
|
+
rb_ivar_set(self, at_open, Qfalse);
|
386
|
+
if (ws)
|
387
|
+
uws_ws_close(USE_SSL, ws);
|
388
|
+
return Qnil;
|
389
|
+
}
|
390
|
+
|
391
|
+
static VALUE up_client_pending(VALUE self) {
|
392
|
+
uws_websocket_t *ws = DATA_PTR(self);
|
393
|
+
if (ws)
|
394
|
+
return INT2FIX(uws_ws_get_buffered_amount(USE_SSL, ws));
|
395
|
+
return INT2FIX(0);
|
396
|
+
}
|
397
|
+
|
398
|
+
static void up_client_cluster_publish(server_s *s, int st, VALUE channel,
|
399
|
+
VALUE message) {
|
400
|
+
const char *opening_line = "POST " INTERNAL_PUBLISH_PATH " HTTP/1.1\r\n";
|
401
|
+
const char *host_header = "Host: localhost\r\n";
|
402
|
+
const char *secret = "Secret: ";
|
403
|
+
char secret_header[50];
|
404
|
+
memcpy(secret_header, secret, 8);
|
405
|
+
memcpy(secret_header + 8, s->secret, 36);
|
406
|
+
memcpy(secret_header + 8 + 36, "\r\n", 2);
|
407
|
+
const char *content_type = "Content-Type: text/plain\r\n";
|
408
|
+
long c_length = RSTRING_LEN(channel) + RSTRING_LEN(message) + 2;
|
409
|
+
char content_length[50];
|
410
|
+
memcpy(content_length, "Content-Length: ", 16);
|
411
|
+
long cl = ltoa(content_length + 16, c_length);
|
412
|
+
memcpy(content_length + 16 + cl, "\r\n\r\n", 4);
|
413
|
+
const char *boundary_disposition = "\r\n";
|
414
|
+
|
415
|
+
send(st, opening_line, strlen(opening_line), MSG_DONTROUTE | MSG_MORE);
|
416
|
+
send(st, host_header, strlen(host_header), MSG_DONTROUTE | MSG_MORE);
|
417
|
+
send(st, secret_header, 46, MSG_DONTROUTE | MSG_MORE);
|
418
|
+
send(st, content_type, strlen(content_type), MSG_DONTROUTE | MSG_MORE);
|
419
|
+
send(st, content_length, strlen(content_length), MSG_DONTROUTE | MSG_MORE);
|
420
|
+
send(st, RSTRING_PTR(channel), RSTRING_LEN(channel),
|
421
|
+
MSG_DONTROUTE | MSG_MORE);
|
422
|
+
send(st, boundary_disposition, strlen(boundary_disposition),
|
423
|
+
MSG_DONTROUTE | MSG_MORE);
|
424
|
+
send(st, RSTRING_PTR(message), RSTRING_LEN(message),
|
425
|
+
MSG_DONTROUTE | MSG_MORE);
|
426
|
+
|
427
|
+
// char read_buf[256];
|
428
|
+
// if (read(st, read_buf, 256)) {
|
429
|
+
// // do nothing
|
430
|
+
// };
|
431
|
+
// fprintf(stderr, "read: %s\n", read_buf);
|
432
|
+
}
|
433
|
+
|
434
|
+
static VALUE up_client_publish(int argc, VALUE *argv, VALUE self) {
|
435
|
+
uws_websocket_t *ws = DATA_PTR(self);
|
436
|
+
if (!ws)
|
437
|
+
return Qnil;
|
438
|
+
VALUE channel, message, engine;
|
439
|
+
rb_scan_args(argc, argv, "21", &channel, &message, &engine);
|
440
|
+
if (TYPE(channel) != T_STRING)
|
441
|
+
channel = rb_obj_as_string(channel);
|
442
|
+
if (TYPE(message) != T_STRING)
|
443
|
+
message = rb_obj_as_string(message);
|
444
|
+
VALUE server = rb_ivar_get(self, at_server);
|
445
|
+
if (server != Qnil) {
|
446
|
+
server_s *s = DATA_PTR(server);
|
447
|
+
int res =
|
448
|
+
uws_publish(USE_SSL, s->app, RSTRING_PTR(channel), RSTRING_LEN(channel),
|
449
|
+
RSTRING_PTR(message), RSTRING_LEN(message), TEXT, false);
|
450
|
+
if (s->member_id > 0) {
|
451
|
+
|
452
|
+
// publish to cluster members
|
453
|
+
int i;
|
454
|
+
struct sockaddr_in member_addr = {
|
455
|
+
.sin_addr.s_addr = inet_addr("127.0.0.1"), .sin_family = AF_INET};
|
456
|
+
for (i = 1; i <= s->workers; i++) {
|
457
|
+
if (i != s->member_id) {
|
458
|
+
int st = socket(AF_INET, SOCK_STREAM, 0);
|
459
|
+
if (st) {
|
460
|
+
member_addr.sin_port = htons(FIX2INT(s->port) + i);
|
461
|
+
if (connect(st, (struct sockaddr *)&member_addr,
|
462
|
+
sizeof(struct sockaddr_in)) == 0) {
|
463
|
+
up_client_cluster_publish(s, st, channel, message);
|
464
|
+
close(st);
|
465
|
+
}
|
466
|
+
}
|
467
|
+
}
|
468
|
+
}
|
469
|
+
}
|
470
|
+
return res ? Qtrue : Qfalse;
|
471
|
+
}
|
472
|
+
return Qfalse;
|
473
|
+
}
|
474
|
+
|
475
|
+
static VALUE up_client_subscribe(int argc, VALUE *argv, VALUE self) {
|
476
|
+
uws_websocket_t *ws = DATA_PTR(self);
|
477
|
+
if (!ws)
|
478
|
+
return Qnil;
|
479
|
+
VALUE channel, is_pattern;
|
480
|
+
rb_scan_args(argc, argv, "11", &channel, &is_pattern);
|
481
|
+
if (TYPE(channel) != T_STRING)
|
482
|
+
channel = rb_obj_as_string(channel);
|
483
|
+
return uws_ws_subscribe(USE_SSL, ws, RSTRING_PTR(channel),
|
484
|
+
RSTRING_LEN(channel))
|
485
|
+
? Qtrue
|
486
|
+
: Qnil;
|
487
|
+
}
|
488
|
+
|
489
|
+
static VALUE up_client_write(VALUE self, VALUE rdata) {
|
490
|
+
uws_websocket_t *ws = DATA_PTR(self);
|
491
|
+
if (!ws)
|
492
|
+
rb_raise(rb_eStandardError, "socket closed, can't write");
|
493
|
+
if (TYPE(rdata) != T_STRING)
|
494
|
+
rdata = rb_obj_as_string(rdata);
|
495
|
+
if (TYPE(rdata) != T_STRING)
|
496
|
+
rb_raise(rb_eTypeError,
|
497
|
+
"rdata not a string or cannot be converted to a string");
|
498
|
+
int opcode = rb_enc_get(rdata) == binary_encoding ? BINARY : TEXT;
|
499
|
+
return INT2FIX(
|
500
|
+
uws_ws_send(USE_SSL, ws, RSTRING_PTR(rdata), RSTRING_LEN(rdata), opcode));
|
501
|
+
}
|
502
|
+
|
503
|
+
static VALUE up_client_unsubscribe(int argc, VALUE *argv, VALUE self) {
|
504
|
+
uws_websocket_t *ws = DATA_PTR(self);
|
505
|
+
if (!ws)
|
506
|
+
return Qnil;
|
507
|
+
VALUE channel, is_pattern;
|
508
|
+
rb_scan_args(argc, argv, "11", &channel, &is_pattern);
|
509
|
+
if (TYPE(channel) != T_STRING)
|
510
|
+
channel = rb_obj_as_string(channel);
|
511
|
+
return uws_ws_unsubscribe(USE_SSL, ws, RSTRING_PTR(channel),
|
512
|
+
RSTRING_LEN(channel))
|
513
|
+
? Qtrue
|
514
|
+
: Qnil;
|
515
|
+
}
|
516
|
+
|
517
|
+
static void up_server_t_free(void *p) {
|
518
|
+
server_s *s = (server_s *)p;
|
519
|
+
rb_gc_unregister_address(&s->host);
|
520
|
+
rb_gc_unregister_address(&s->port);
|
521
|
+
rb_gc_unregister_address(&s->env_template);
|
522
|
+
free(s);
|
523
|
+
}
|
524
|
+
|
525
|
+
const rb_data_type_t up_server_t = {.wrap_struct_name = "Up::Ruby::Server",
|
526
|
+
.function = {.dmark = NULL,
|
527
|
+
.dfree = up_server_t_free,
|
528
|
+
.dsize = NULL,
|
529
|
+
.dcompact = NULL,
|
530
|
+
.reserved = {0}},
|
531
|
+
.parent = NULL,
|
532
|
+
.data = NULL,
|
533
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY};
|
534
|
+
|
535
|
+
static VALUE up_server_alloc(VALUE rclass) {
|
536
|
+
server_s *s = calloc(1, sizeof(server_s));
|
537
|
+
if (!s)
|
538
|
+
rb_raise(rb_eNoMemError, "unable to allocate server");
|
539
|
+
rb_gc_register_address(&s->host);
|
540
|
+
rb_gc_register_address(&s->port);
|
541
|
+
rb_gc_register_address(&s->env_template);
|
542
|
+
return s->self = TypedData_Wrap_Struct(rclass, &up_server_t, s);
|
543
|
+
}
|
544
|
+
|
545
|
+
static void up_internal_check_arg_types(VALUE rapp, VALUE *rhost,
|
546
|
+
VALUE *rport) {
|
547
|
+
if (!rb_respond_to(rapp, id_call))
|
548
|
+
rb_raise(rb_eArgError, "app does not respond to #call");
|
549
|
+
if (*rhost == Qundef || *rhost == Qnil) {
|
550
|
+
*rhost = rb_str_new("localhost", 9);
|
551
|
+
}
|
552
|
+
Check_Type(*rhost, T_STRING);
|
553
|
+
if (*rport == Qundef || *rport == Qnil) {
|
554
|
+
*rport = INT2FIX(3000);
|
555
|
+
}
|
556
|
+
Check_Type(*rport, T_FIXNUM);
|
557
|
+
}
|
558
|
+
|
559
|
+
static VALUE up_server_init(int argc, VALUE *argv, VALUE self) {
|
560
|
+
if (!rb_keyword_given_p())
|
561
|
+
rb_raise(rb_eArgError, "no args given, must at least provide app:");
|
562
|
+
ID kwargs[] = {id_app, id_host, id_port, id_logger};
|
563
|
+
VALUE rargs[4] = {Qnil, Qnil, Qnil, Qnil};
|
564
|
+
VALUE options = Qnil;
|
565
|
+
|
566
|
+
rb_scan_args_kw(1, argc, argv, ":", &options);
|
567
|
+
rb_get_kwargs(options, kwargs, 1, 2, rargs);
|
568
|
+
|
569
|
+
VALUE rapp = rargs[0];
|
570
|
+
VALUE rhost = rargs[1];
|
571
|
+
VALUE rport = rargs[2];
|
572
|
+
|
573
|
+
up_internal_check_arg_types(rapp, &rhost, &rport);
|
574
|
+
|
575
|
+
server_s *s = DATA_PTR(self);
|
576
|
+
s->rapp = rapp;
|
577
|
+
s->host = rb_obj_freeze(rhost);
|
578
|
+
s->port = rport;
|
579
|
+
s->logger = rargs[3];
|
580
|
+
|
581
|
+
return self;
|
582
|
+
}
|
583
|
+
|
584
|
+
void up_ws_drain_handler(uws_websocket_t *ws, void *user_data) {
|
585
|
+
VALUE *client = (VALUE *)uws_ws_get_user_data(USE_SSL, ws);
|
586
|
+
DATA_PTR(*client) = ws;
|
587
|
+
VALUE rhandler = rb_ivar_get(*client, at_handler);
|
588
|
+
if (rb_respond_to(rhandler, id_on_drained))
|
589
|
+
rb_funcall(rhandler, id_on_drained, 1, *client);
|
590
|
+
DATA_PTR(*client) = NULL;
|
591
|
+
}
|
592
|
+
|
593
|
+
void up_ws_ping_handler(uws_websocket_t *ws, const char *message, size_t length,
|
594
|
+
void *user_data) {
|
595
|
+
/* You don't need to handle this one, we automatically respond to pings as per
|
596
|
+
* standard */
|
597
|
+
}
|
598
|
+
|
599
|
+
void up_ws_pong_handler(uws_websocket_t *ws, const char *message, size_t length,
|
600
|
+
void *user_data) {
|
601
|
+
/* You don't need to handle this one either */
|
602
|
+
}
|
603
|
+
|
604
|
+
static void up_ws_close_handler(uws_websocket_t *ws, int code,
|
605
|
+
const char *message, size_t length,
|
606
|
+
void *user_data) {
|
607
|
+
VALUE *client = (VALUE *)uws_ws_get_user_data(USE_SSL, ws);
|
608
|
+
rb_ivar_set(*client, at_open, Qfalse);
|
609
|
+
DATA_PTR(*client) = ws;
|
610
|
+
VALUE rhandler = rb_ivar_get(*client, at_handler);
|
611
|
+
if (rb_respond_to(rhandler, id_on_close))
|
612
|
+
rb_funcall(rhandler, id_on_close, 1, *client);
|
613
|
+
// rb_gc_unregister_address(client);
|
614
|
+
DATA_PTR(*client) = NULL;
|
615
|
+
free(client);
|
616
|
+
}
|
617
|
+
|
618
|
+
static void up_ws_message_handler(uws_websocket_t *ws, const char *message,
|
619
|
+
size_t length, uws_opcode_t opcode,
|
620
|
+
void *user_data) {
|
621
|
+
VALUE rmessage;
|
622
|
+
if (opcode == TEXT) {
|
623
|
+
rmessage = rb_enc_str_new(message, length, utf8_encoding);
|
624
|
+
} else if (opcode == BINARY) {
|
625
|
+
rmessage = rb_enc_str_new(message, length, binary_encoding);
|
626
|
+
} else {
|
627
|
+
return;
|
628
|
+
}
|
629
|
+
VALUE *client = (VALUE *)uws_ws_get_user_data(USE_SSL, ws);
|
630
|
+
DATA_PTR(*client) = ws;
|
631
|
+
VALUE rhandler = rb_ivar_get(*client, at_handler);
|
632
|
+
if (rb_respond_to(rhandler, id_on_message))
|
633
|
+
rb_funcall(rhandler, id_on_message, 2, *client, rmessage);
|
634
|
+
DATA_PTR(*client) = NULL;
|
635
|
+
}
|
636
|
+
|
637
|
+
static void up_ws_open_handler(uws_websocket_t *ws, void *user_data) {
|
638
|
+
VALUE *client = (VALUE *)uws_ws_get_user_data(USE_SSL, ws);
|
639
|
+
rb_ivar_set(*client, at_open, Qtrue);
|
640
|
+
DATA_PTR(*client) = ws;
|
641
|
+
VALUE rhandler = rb_ivar_get(*client, at_handler);
|
642
|
+
if (rb_respond_to(rhandler, id_on_open))
|
643
|
+
rb_funcall(rhandler, id_on_open, 1, *client);
|
644
|
+
DATA_PTR(*client) = NULL;
|
645
|
+
}
|
646
|
+
|
647
|
+
static void up_ws_upgrade_handler(uws_res_t *res, uws_req_t *req,
|
648
|
+
uws_socket_context_t *context, void *arg) {
|
649
|
+
server_s *s = (server_s *)arg;
|
650
|
+
// prepare rack env
|
651
|
+
VALUE renv = rb_hash_dup(s->env_template);
|
652
|
+
up_server_prepare_env(renv, req);
|
653
|
+
rb_hash_aset(renv, rack_upgrade_q, sym_websocket);
|
654
|
+
|
655
|
+
// call app
|
656
|
+
VALUE rres = rb_funcall(s->rapp, id_call, 1, renv);
|
657
|
+
|
658
|
+
if (TYPE(rres) != T_ARRAY)
|
659
|
+
goto upgrade_error;
|
660
|
+
|
661
|
+
// response status
|
662
|
+
VALUE rstatus = rb_ary_entry(rres, 0);
|
663
|
+
int st = FIX2INT(rstatus);
|
664
|
+
|
665
|
+
VALUE rhandler = rb_hash_lookup2(renv, rack_upgrade, Qundef);
|
666
|
+
if (st >= 0 && st < 300 && rhandler != Qundef && rhandler != Qnil) {
|
667
|
+
// upgrade
|
668
|
+
|
669
|
+
VALUE *client = malloc(sizeof(VALUE));
|
670
|
+
// rb_gc_register_address(client);
|
671
|
+
*client = rb_class_new_instance(0, NULL, cClient);
|
672
|
+
rb_ivar_set(*client, at_env, renv);
|
673
|
+
rb_ivar_set(*client, at_open, false);
|
674
|
+
rb_ivar_set(*client, at_handler, rhandler);
|
675
|
+
rb_ivar_set(*client, at_protocol, sym_websocket);
|
676
|
+
rb_ivar_set(*client, at_timeout, INT2FIX(120));
|
677
|
+
rb_ivar_set(*client, at_server, s->self);
|
678
|
+
|
679
|
+
const char *ws_key = NULL;
|
680
|
+
const char *ws_protocol = NULL;
|
681
|
+
const char *ws_extensions = NULL;
|
682
|
+
size_t ws_key_length =
|
683
|
+
uws_req_get_header(req, "sec-websocket-key", 17, &ws_key);
|
684
|
+
size_t ws_protocol_length =
|
685
|
+
uws_req_get_header(req, "sec-websocket-protocol", 22, &ws_protocol);
|
686
|
+
size_t ws_extensions_length =
|
687
|
+
uws_req_get_header(req, "sec-websocket-extensions", 24, &ws_extensions);
|
688
|
+
uws_res_upgrade(USE_SSL, res, (void *)client, ws_key, ws_key_length,
|
689
|
+
ws_protocol, ws_protocol_length, ws_extensions,
|
690
|
+
ws_extensions_length, context);
|
691
|
+
} else {
|
692
|
+
// treat as normal request
|
693
|
+
// response status
|
694
|
+
if (!up_internal_set_response_status(res, rstatus))
|
695
|
+
goto upgrade_error;
|
696
|
+
|
697
|
+
// collect headers
|
698
|
+
VALUE rheaders = rb_ary_entry(rres, 1);
|
699
|
+
if (TYPE(rheaders) != T_HASH)
|
700
|
+
goto upgrade_error;
|
701
|
+
rb_hash_foreach(rheaders, up_internal_res_header_handler, (VALUE)res);
|
702
|
+
|
703
|
+
// collect response body
|
704
|
+
VALUE rparts = rb_ary_entry(rres, 2);
|
705
|
+
up_internal_collect_response_body(res, rparts);
|
706
|
+
|
707
|
+
// end response
|
708
|
+
uws_res_end_without_body(USE_SSL, res, false);
|
709
|
+
|
710
|
+
// close resources if necessary
|
711
|
+
if (rb_respond_to(rparts, id_close))
|
712
|
+
rb_funcall(rparts, id_close, 0);
|
713
|
+
|
714
|
+
RB_GC_GUARD(rheaders);
|
715
|
+
}
|
716
|
+
return;
|
717
|
+
RB_GC_GUARD(rstatus);
|
718
|
+
RB_GC_GUARD(rres);
|
719
|
+
RB_GC_GUARD(renv);
|
720
|
+
upgrade_error:
|
721
|
+
fprintf(stderr, "upgrade error");
|
722
|
+
}
|
723
|
+
|
724
|
+
static VALUE up_server_listen(VALUE self) {
|
725
|
+
server_s *s = DATA_PTR(self);
|
726
|
+
up_internal_check_arg_types(s->rapp, &s->host, &s->port);
|
727
|
+
|
728
|
+
s->env_template = rb_hash_dup(rack_env_template);
|
729
|
+
// When combined with SCRIPT_NAME and PATH_INFO, these variables can be used
|
730
|
+
// to complete the URL.
|
731
|
+
rb_hash_aset(s->env_template, SERVER_NAME, s->host);
|
732
|
+
// An optional Integer which is the port the server is running on.
|
733
|
+
rb_hash_aset(s->env_template, SERVER_PORT, s->port);
|
734
|
+
if (s->logger && s->logger != Qundef && s->logger != Qnil) {
|
735
|
+
rb_hash_aset(s->env_template, rack_logger, s->logger);
|
736
|
+
}
|
737
|
+
struct us_socket_context_options_t options = {.key_file_name = NULL,
|
738
|
+
.cert_file_name = NULL,
|
739
|
+
.ca_file_name = NULL,
|
740
|
+
.passphrase = NULL,
|
741
|
+
.dh_params_file_name = NULL,
|
742
|
+
.ssl_ciphers = NULL};
|
743
|
+
s->app = uws_create_app(USE_SSL, options);
|
744
|
+
if (!s->app)
|
745
|
+
rb_raise(rb_eRuntimeError, "could not init uws app");
|
746
|
+
uws_app_listen_config_t config = {
|
747
|
+
.port = FIX2INT(s->port), .host = RSTRING_PTR(s->host), .options = 0};
|
748
|
+
VALUE rmember_id = rb_ivar_get(self, at_member_id);
|
749
|
+
if (rmember_id != Qnil) {
|
750
|
+
s->member_id = FIX2INT(rmember_id);
|
751
|
+
// got a cluster, open publish ports
|
752
|
+
VALUE rworkers = rb_ivar_get(self, at_workers);
|
753
|
+
s->workers = FIX2INT(rworkers);
|
754
|
+
VALUE rsecret = rb_ivar_get(self, at_secret);
|
755
|
+
if (TYPE(rsecret) != T_STRING || RSTRING_LEN(rsecret) != 36)
|
756
|
+
rb_raise(rb_eTypeError, "cluster secret of unknown type");
|
757
|
+
memcpy(s->secret, RSTRING_PTR(rsecret), 36);
|
758
|
+
s->secret[36] = '\0';
|
759
|
+
uws_app_any(USE_SSL, s->app, INTERNAL_PUBLISH_PATH,
|
760
|
+
up_internal_publish_handler, (void *)s);
|
761
|
+
uws_app_listen_config_t config_internal = {
|
762
|
+
.port = config.port + s->member_id, .host = "localhost", .options = 0};
|
763
|
+
uws_app_listen_with_config(false, s->app, config_internal,
|
764
|
+
up_server_listen_handler, NULL);
|
765
|
+
}
|
766
|
+
uws_app_any(USE_SSL, s->app, "/*", up_server_request_handler, (void *)s);
|
767
|
+
uws_ws(USE_SSL, s->app, "/*",
|
768
|
+
(uws_socket_behavior_t){.compression = DISABLED,
|
769
|
+
.maxPayloadLength = 5 * 1024 * 1024,
|
770
|
+
.idleTimeout = 120,
|
771
|
+
.upgrade = up_ws_upgrade_handler,
|
772
|
+
.open = up_ws_open_handler,
|
773
|
+
.message = up_ws_message_handler,
|
774
|
+
.close = up_ws_close_handler,
|
775
|
+
.drain = up_ws_drain_handler,
|
776
|
+
.ping = up_ws_ping_handler,
|
777
|
+
.pong = up_ws_pong_handler},
|
778
|
+
s);
|
779
|
+
uws_app_listen_with_config(USE_SSL, s->app, config, up_server_listen_handler,
|
780
|
+
NULL);
|
781
|
+
uws_app_run(USE_SSL, s->app);
|
782
|
+
return self;
|
783
|
+
}
|
784
|
+
|
785
|
+
static VALUE up_server_stop(VALUE self) {
|
786
|
+
server_s *s = DATA_PTR(self);
|
787
|
+
if (!s->app)
|
788
|
+
rb_raise(rb_eRuntimeError, "no uws, did initialize call super?");
|
789
|
+
uws_app_close(USE_SSL, s->app);
|
790
|
+
uws_app_destroy(USE_SSL, s->app);
|
791
|
+
s->app = NULL;
|
792
|
+
return Qnil;
|
793
|
+
}
|
794
|
+
|
795
|
+
void up_hash_set(VALUE rhash, const char *key, VALUE val) {
|
796
|
+
rb_hash_aset(rhash, rb_enc_str_new(key, strlen(key), binary_encoding), val);
|
797
|
+
}
|
798
|
+
|
799
|
+
void up_setup_rack_env_template(void) {
|
800
|
+
rb_gc_register_address(&rack_env_template);
|
801
|
+
rack_env_template = rb_hash_new();
|
802
|
+
|
803
|
+
// error stream
|
804
|
+
up_hash_set(rack_env_template, "rack.errors", rb_stderr);
|
805
|
+
|
806
|
+
// if present, an object responding to call that is used to perform a full
|
807
|
+
// hijack. up_hash_set(rack_env_template, "rack.hijack", Qnil);
|
808
|
+
|
809
|
+
// if present and true, indicates that the server supports partial hijacking
|
810
|
+
// up_hash_set(rack_env_template, "rack.hijack?", Qfalse);
|
811
|
+
|
812
|
+
// The input stream is an IO-like object which contains the raw HTTP POST
|
813
|
+
// data
|
814
|
+
rb_hash_aset(rack_env_template, rack_input, default_input);
|
815
|
+
|
816
|
+
// A common object interface for logging messages
|
817
|
+
up_hash_set(rack_env_template, "rack.logger", default_logger);
|
818
|
+
|
819
|
+
// An Integer hint to the multipart parser as to what chunk size to use for
|
820
|
+
// reads and writes.
|
821
|
+
up_hash_set(rack_env_template, "rack.multipart.buffer_size", INT2FIX(4096));
|
822
|
+
|
823
|
+
// An object responding to #call with two arguments, the filename and
|
824
|
+
// content_type given for the multipart form field, and returning an IO-like
|
825
|
+
// object that responds to #<< and optionally #rewind.
|
826
|
+
// up_hash_set(rack_env_template, "rack.multipart.tempfile_factory", Qnil);
|
827
|
+
|
828
|
+
// An array of callables run by the server after the response has been
|
829
|
+
// processed.
|
830
|
+
// up_hash_set(rack_env_template, "rack.response_finished", Qnil);
|
831
|
+
|
832
|
+
// A hash-like interface for storing request session data.
|
833
|
+
// up_hash_set(rack_env_template, "rack.session", Qnil);
|
834
|
+
|
835
|
+
// http or https, depending on the request URL.
|
836
|
+
up_hash_set(rack_env_template, "rack.url_scheme",
|
837
|
+
rb_enc_str_new_cstr("http", binary_encoding));
|
838
|
+
|
839
|
+
// The portion of the request URL that follows the ?, if any. May be empty,
|
840
|
+
// but is always required!
|
841
|
+
rb_hash_aset(rack_env_template, QUERY_STRING, empty_string);
|
842
|
+
|
843
|
+
// The initial portion of the request URL’s “path” that corresponds to the
|
844
|
+
// application object, so that the application knows its virtual “location”.
|
845
|
+
// This may be an empty string, if the application corresponds to the “root”
|
846
|
+
// of the server.
|
847
|
+
rb_hash_aset(rack_env_template, SCRIPT_NAME, empty_string);
|
848
|
+
|
849
|
+
// A string representing the HTTP version used for the request.
|
850
|
+
// Note: uws has no way to get that information from the request
|
851
|
+
// so set it to a static value
|
852
|
+
rb_hash_aset(rack_env_template, SERVER_PROTOCOL, http11);
|
853
|
+
rb_hash_aset(rack_env_template, HTTP_VERSION, http11);
|
854
|
+
}
|
855
|
+
|
856
|
+
void Init_up_ext(void) {
|
857
|
+
at_env = rb_intern("@env");
|
858
|
+
at_handler = rb_intern("@handler");
|
859
|
+
at_member_id = rb_intern("@member_id");
|
860
|
+
at_open = rb_intern("@open");
|
861
|
+
at_protocol = rb_intern("@protocol");
|
862
|
+
at_secret = rb_intern("@secret");
|
863
|
+
at_server = rb_intern("@server");
|
864
|
+
at_timeout = rb_intern("@timeout");
|
865
|
+
at_workers = rb_intern("@workers");
|
866
|
+
id_app = rb_intern("app");
|
867
|
+
id_call = rb_intern("call");
|
868
|
+
id_close = rb_intern("close");
|
869
|
+
id_each = rb_intern("each");
|
870
|
+
id_host = rb_intern("host");
|
871
|
+
id_logger = rb_intern("logger");
|
872
|
+
id_on_close = rb_intern("on_close");
|
873
|
+
id_on_drained = rb_intern("on_drained");
|
874
|
+
id_on_message = rb_intern("on_message");
|
875
|
+
id_on_open = rb_intern("on_open");
|
876
|
+
id_port = rb_intern("port");
|
877
|
+
|
878
|
+
utf8_encoding = rb_enc_find("UTF-8");
|
879
|
+
binary_encoding = rb_enc_find("binary");
|
880
|
+
|
881
|
+
set_str_val(empty_string, "");
|
882
|
+
set_str_val(http11, "HTTP/1.1");
|
883
|
+
set_str_val(rack_input, "rack.input");
|
884
|
+
set_str_val(rack_logger, "rack.logger");
|
885
|
+
set_str_val(rack_upgrade, "rack.upgrade");
|
886
|
+
set_str_val(rack_upgrade_q, "rack.upgrade?");
|
887
|
+
set_sym_val(sym_websocket, "websocket");
|
888
|
+
set_global(HTTP_VERSION);
|
889
|
+
set_global(PATH_INFO);
|
890
|
+
set_global(QUERY_STRING);
|
891
|
+
set_global(REQUEST_METHOD);
|
892
|
+
set_global(SCRIPT_NAME);
|
893
|
+
set_global(SERVER_NAME);
|
894
|
+
set_global(SERVER_PORT);
|
895
|
+
set_global(SERVER_PROTOCOL);
|
896
|
+
|
897
|
+
rb_require("logger");
|
898
|
+
|
899
|
+
rb_gc_register_address(&cLogger);
|
900
|
+
cLogger = rb_const_get(rb_cObject, rb_intern("Logger"));
|
901
|
+
rb_gc_register_address(&default_logger);
|
902
|
+
default_logger = rb_funcall(cLogger, rb_intern("new"), 1, rb_stderr);
|
903
|
+
|
904
|
+
rb_require("stringio");
|
905
|
+
|
906
|
+
rb_gc_register_address(&cStringIO);
|
907
|
+
cStringIO = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
908
|
+
rb_gc_register_address(&default_input);
|
909
|
+
default_input = rb_funcall(cStringIO, rb_intern("new"), 1, empty_string);
|
910
|
+
|
911
|
+
up_setup_rack_env_template();
|
912
|
+
|
913
|
+
mUp = rb_define_module("Up");
|
914
|
+
cClient = rb_define_class_under(mUp, "Client", rb_cObject);
|
915
|
+
rb_define_alloc_func(cClient, up_client_alloc);
|
916
|
+
rb_define_method(cClient, "close", up_client_close, 0);
|
917
|
+
rb_define_method(cClient, "pending", up_client_pending, 0);
|
918
|
+
rb_define_method(cClient, "publish", up_client_publish, -1);
|
919
|
+
rb_define_method(cClient, "subscribe", up_client_subscribe, -1);
|
920
|
+
rb_define_method(cClient, "unsubscribe", up_client_unsubscribe, -1);
|
921
|
+
rb_define_method(cClient, "write", up_client_write, 1);
|
922
|
+
|
923
|
+
mRuby = rb_define_module_under(mUp, "Ruby");
|
924
|
+
|
925
|
+
cServer = rb_define_class_under(mRuby, "Server", rb_cObject);
|
926
|
+
rb_define_alloc_func(cServer, up_server_alloc);
|
927
|
+
rb_define_method(cServer, "initialize", up_server_init, -1);
|
928
|
+
rb_define_method(cServer, "listen", up_server_listen, 0);
|
929
|
+
rb_define_method(cServer, "stop", up_server_stop, 0);
|
930
|
+
}
|