opal-up 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +66 -51
- data/ext/up_ext/extconf.rb +1 -1
- data/ext/up_ext/libuwebsockets.cpp +1262 -1292
- data/ext/up_ext/libuwebsockets.h +337 -201
- data/ext/up_ext/up_ext.c +816 -164
- 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/rack_cluster.rb +1 -1
- data/lib/up/ruby/rack_server.rb +0 -1
- 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 +4 -15
- data/bin/up_node +0 -12
- data/bin/up_node_cluster +0 -12
- data/lib/up/node/cluster.rb +0 -39
- data/lib/up/node/cluster_cli.rb +0 -15
- data/lib/up/node/rack_cluster.rb +0 -25
- data/lib/up/node/rack_env.rb +0 -106
- data/lib/up/node/rack_server.rb +0 -25
- data/lib/up/node/server.rb +0 -84
- data/lib/up/node/server_cli.rb +0 -15
- data/lib/up/ruby/rack_env.rb +0 -97
- data/lib/up/u_web_socket/rack_env.rb +0 -101
data/ext/up_ext/up_ext.c
CHANGED
@@ -1,93 +1,549 @@
|
|
1
|
-
#include <ruby.h>
|
2
|
-
#include <unistd.h>
|
3
1
|
#include "libusockets.h"
|
4
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>
|
5
9
|
|
6
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__"
|
7
14
|
|
8
15
|
static VALUE mUp;
|
9
16
|
static VALUE mRuby;
|
10
17
|
static VALUE cServer;
|
11
|
-
static VALUE
|
12
|
-
static VALUE
|
13
|
-
static VALUE
|
14
|
-
|
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;
|
15
31
|
static ID id_app;
|
16
32
|
static ID id_call;
|
17
33
|
static ID id_close;
|
18
34
|
static ID id_each;
|
19
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;
|
20
41
|
static ID id_port;
|
21
42
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
+
}
|
31
101
|
|
32
102
|
VALUE up_internal_handle_part(RB_BLOCK_CALL_FUNC_ARGLIST(rpart, res)) {
|
33
|
-
|
34
|
-
|
103
|
+
if (TYPE(rpart) == T_STRING)
|
104
|
+
uws_res_write(USE_SSL, (uws_res_t *)res, RSTRING_PTR(rpart),
|
105
|
+
RSTRING_LEN(rpart));
|
35
106
|
return Qnil;
|
36
107
|
}
|
37
108
|
|
38
|
-
|
39
|
-
VALUE
|
40
|
-
|
41
|
-
VALUE
|
42
|
-
|
43
|
-
VALUE
|
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) {
|
44
248
|
VALUE rpart;
|
45
249
|
if (TYPE(rparts) == T_ARRAY) {
|
46
250
|
long i, l = RARRAY_LEN(rparts);
|
47
251
|
for (i = 0; i < l; i++) {
|
48
|
-
rpart =
|
49
|
-
|
252
|
+
rpart = rb_ary_entry(rparts, i);
|
253
|
+
if (TYPE(rpart) != T_STRING)
|
254
|
+
return false;
|
50
255
|
uws_res_write(USE_SSL, res, RSTRING_PTR(rpart), RSTRING_LEN(rpart));
|
51
256
|
}
|
52
257
|
} else if (rb_respond_to(rparts, id_each)) {
|
53
|
-
rb_block_call(rparts, id_each, 0, NULL, up_internal_handle_part,
|
258
|
+
rb_block_call(rparts, id_each, 0, NULL, up_internal_handle_part,
|
259
|
+
(VALUE)res);
|
54
260
|
} else if (rb_respond_to(rparts, id_call)) {
|
55
261
|
rpart = rb_funcall(rparts, id_call, 0);
|
56
|
-
|
262
|
+
if (TYPE(rpart) != T_STRING)
|
263
|
+
return false;
|
57
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);
|
58
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
|
59
348
|
if (rb_respond_to(rparts, id_close))
|
60
349
|
rb_funcall(rparts, id_close, 0);
|
61
|
-
|
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");
|
62
358
|
uws_res_end_without_body(USE_SSL, res, false);
|
63
359
|
}
|
64
360
|
|
65
|
-
static void up_server_listen_handler(struct us_listen_socket_t *listen_socket,
|
66
|
-
|
67
|
-
|
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;
|
68
515
|
}
|
69
516
|
|
70
517
|
static void up_server_t_free(void *p) {
|
71
|
-
|
72
|
-
|
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);
|
73
523
|
}
|
74
524
|
|
75
525
|
const rb_data_type_t up_server_t = {.wrap_struct_name = "Up::Ruby::Server",
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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};
|
84
534
|
|
85
535
|
static VALUE up_server_alloc(VALUE rclass) {
|
86
|
-
|
87
|
-
|
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);
|
88
543
|
}
|
89
544
|
|
90
|
-
static void up_internal_check_arg_types(VALUE rapp, VALUE *rhost,
|
545
|
+
static void up_internal_check_arg_types(VALUE rapp, VALUE *rhost,
|
546
|
+
VALUE *rport) {
|
91
547
|
if (!rb_respond_to(rapp, id_call))
|
92
548
|
rb_raise(rb_eArgError, "app does not respond to #call");
|
93
549
|
if (*rhost == Qundef || *rhost == Qnil) {
|
@@ -103,158 +559,367 @@ static void up_internal_check_arg_types(VALUE rapp, VALUE *rhost, VALUE *rport)
|
|
103
559
|
static VALUE up_server_init(int argc, VALUE *argv, VALUE self) {
|
104
560
|
if (!rb_keyword_given_p())
|
105
561
|
rb_raise(rb_eArgError, "no args given, must at least provide app:");
|
106
|
-
ID kwargs[] = {id_app, id_host, id_port};
|
107
|
-
VALUE rargs[
|
562
|
+
ID kwargs[] = {id_app, id_host, id_port, id_logger};
|
563
|
+
VALUE rargs[4] = {Qnil, Qnil, Qnil, Qnil};
|
108
564
|
VALUE options = Qnil;
|
109
565
|
|
110
566
|
rb_scan_args_kw(1, argc, argv, ":", &options);
|
111
567
|
rb_get_kwargs(options, kwargs, 1, 2, rargs);
|
112
|
-
|
568
|
+
|
113
569
|
VALUE rapp = rargs[0];
|
114
570
|
VALUE rhost = rargs[1];
|
115
571
|
VALUE rport = rargs[2];
|
116
572
|
|
117
573
|
up_internal_check_arg_types(rapp, &rhost, &rport);
|
118
574
|
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
+
|
122
581
|
return self;
|
123
582
|
}
|
124
583
|
|
125
|
-
|
126
|
-
VALUE
|
127
|
-
|
128
|
-
VALUE
|
129
|
-
|
130
|
-
|
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
|
+
}
|
131
592
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
if (!app)
|
137
|
-
rb_raise(rb_eRuntimeError, "could not init uws app");
|
138
|
-
uws_app_any(USE_SSL, app, "/*", up_server_request_handler, (void *)rapp);
|
139
|
-
uws_app_listen_config_t config = {.port=3000, .host="localhost", .options=0};
|
140
|
-
uws_app_listen_with_config(USE_SSL, app, config, up_server_listen_handler, NULL);
|
141
|
-
uws_app_run(USE_SSL, app);
|
142
|
-
return Qnil;
|
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 */
|
143
597
|
}
|
144
598
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
rb_raise(rb_eRuntimeError, "no uws, did initialize call super?");
|
149
|
-
uws_app_close(USE_SSL, app);
|
150
|
-
return Qnil;
|
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 */
|
151
602
|
}
|
152
603
|
|
153
|
-
static
|
154
|
-
|
155
|
-
|
156
|
-
VALUE
|
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
|
+
}
|
157
617
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
DATA_PTR(self) = uws_create_app(USE_SSL, options);
|
169
|
-
uws_app_t *app = DATA_PTR(self);
|
170
|
-
if (!app)
|
171
|
-
rb_raise(rb_eRuntimeError, "could not init uws app");
|
172
|
-
uws_app_any(USE_SSL, app, "/*", up_server_request_handler, (void *)rapp);
|
173
|
-
uws_app_listen_config_t config = {.port=3000, .host="localhost", .options=0};
|
174
|
-
uws_app_listen_with_config(USE_SSL, app, config, up_server_listen_handler, NULL);
|
175
|
-
uws_app_run(USE_SSL, app);
|
176
|
-
}
|
177
|
-
}
|
178
|
-
if (pid > 0) {
|
179
|
-
struct us_socket_context_options_t options = {.key_file_name = NULL, .cert_file_name = NULL, .ca_file_name = NULL,
|
180
|
-
.passphrase = NULL, .dh_params_file_name = NULL, .ssl_ciphers = NULL};
|
181
|
-
DATA_PTR(self) = uws_create_app(USE_SSL, options);
|
182
|
-
uws_app_t *app = DATA_PTR(self);
|
183
|
-
if (!app)
|
184
|
-
rb_raise(rb_eRuntimeError, "could not init uws app");
|
185
|
-
uws_app_any(USE_SSL, app, "/*", up_server_request_handler, (void *)rapp);
|
186
|
-
uws_app_listen_config_t config = {.port=3000, .host="localhost", .options=0};
|
187
|
-
uws_app_listen_with_config(USE_SSL, app, config, up_server_listen_handler, NULL);
|
188
|
-
uws_app_run(USE_SSL, app);
|
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;
|
189
628
|
}
|
190
|
-
|
191
|
-
|
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;
|
192
635
|
}
|
193
636
|
|
194
|
-
static
|
195
|
-
|
196
|
-
|
197
|
-
|
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;
|
198
645
|
}
|
199
646
|
|
200
|
-
static
|
201
|
-
|
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");
|
202
722
|
}
|
203
723
|
|
204
|
-
static
|
205
|
-
|
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;
|
206
783
|
}
|
207
784
|
|
208
|
-
static VALUE
|
209
|
-
|
210
|
-
if (!
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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;
|
215
793
|
}
|
216
794
|
|
217
|
-
|
218
|
-
|
219
|
-
if (!req)
|
220
|
-
return Qnil;
|
221
|
-
Check_Type(rheader, T_STRING);
|
222
|
-
const char *header;
|
223
|
-
size_t len = uws_req_get_header(req, RSTRING_PTR(rheader), RSTRING_LEN(rheader), &header);
|
224
|
-
return rb_str_new(header, len);
|
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);
|
225
797
|
}
|
226
798
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
return Qnil;
|
231
|
-
const char *method;
|
232
|
-
size_t len = uws_req_get_method(req, &method);
|
233
|
-
return rb_str_new(method, len);
|
234
|
-
}
|
799
|
+
void up_setup_rack_env_template(void) {
|
800
|
+
rb_gc_register_address(&rack_env_template);
|
801
|
+
rack_env_template = rb_hash_new();
|
235
802
|
|
236
|
-
|
237
|
-
|
238
|
-
}
|
803
|
+
// error stream
|
804
|
+
up_hash_set(rack_env_template, "rack.errors", rb_stderr);
|
239
805
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
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);
|
247
854
|
}
|
248
855
|
|
249
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");
|
250
866
|
id_app = rb_intern("app");
|
251
867
|
id_call = rb_intern("call");
|
252
868
|
id_close = rb_intern("close");
|
253
869
|
id_each = rb_intern("each");
|
254
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");
|
255
876
|
id_port = rb_intern("port");
|
256
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
|
+
|
257
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
|
+
|
258
923
|
mRuby = rb_define_module_under(mUp, "Ruby");
|
259
924
|
|
260
925
|
cServer = rb_define_class_under(mRuby, "Server", rb_cObject);
|
@@ -262,17 +927,4 @@ void Init_up_ext(void) {
|
|
262
927
|
rb_define_method(cServer, "initialize", up_server_init, -1);
|
263
928
|
rb_define_method(cServer, "listen", up_server_listen, 0);
|
264
929
|
rb_define_method(cServer, "stop", up_server_stop, 0);
|
265
|
-
|
266
|
-
cCluster = rb_define_class_under(mRuby, "Cluster", cServer);
|
267
|
-
rb_define_method(cCluster, "listen", up_cluster_listen, 0);
|
268
|
-
rb_define_method(cCluster, "stop", up_cluster_stop, 0);
|
269
|
-
|
270
|
-
cRackEnv = rb_define_class_under(mRuby, "RackEnv", rb_cHash);
|
271
|
-
cRequest = rb_define_class_under(mRuby, "Request", rb_cObject);
|
272
|
-
rb_define_alloc_func(cRequest, up_request_alloc);
|
273
|
-
rb_define_method(cRequest, "each_header", up_request_headers, 0);
|
274
|
-
rb_define_method(cRequest, "get_header", up_request_get_header, 1);
|
275
|
-
rb_define_method(cRequest, "get_method", up_request_get_method, 0);
|
276
|
-
rb_define_method(cRequest, "get_query", up_request_get_query, 0);
|
277
|
-
rb_define_method(cRequest, "get_url", up_request_get_url, 0);
|
278
930
|
}
|