opal-up 0.0.3 → 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/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
|
}
|