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