opal-up 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +66 -51
  3. data/ext/up_ext/App.h +665 -544
  4. data/ext/up_ext/AsyncSocket.h +307 -284
  5. data/ext/up_ext/AsyncSocketData.h +35 -51
  6. data/ext/up_ext/BloomFilter.h +37 -42
  7. data/ext/up_ext/ChunkedEncoding.h +174 -175
  8. data/ext/up_ext/ClientApp.h +20 -23
  9. data/ext/up_ext/HttpContext.h +476 -381
  10. data/ext/up_ext/HttpContextData.h +20 -20
  11. data/ext/up_ext/HttpErrors.h +14 -10
  12. data/ext/up_ext/HttpParser.h +631 -563
  13. data/ext/up_ext/HttpResponse.h +526 -460
  14. data/ext/up_ext/HttpResponseData.h +59 -55
  15. data/ext/up_ext/HttpRouter.h +328 -310
  16. data/ext/up_ext/Loop.h +174 -168
  17. data/ext/up_ext/LoopData.h +60 -67
  18. data/ext/up_ext/MoveOnlyFunction.h +71 -80
  19. data/ext/up_ext/PerMessageDeflate.h +218 -198
  20. data/ext/up_ext/ProxyParser.h +100 -99
  21. data/ext/up_ext/QueryParser.h +91 -84
  22. data/ext/up_ext/TopicTree.h +273 -268
  23. data/ext/up_ext/Utilities.h +25 -25
  24. data/ext/up_ext/WebSocket.h +376 -310
  25. data/ext/up_ext/WebSocketContext.h +487 -372
  26. data/ext/up_ext/WebSocketContextData.h +74 -62
  27. data/ext/up_ext/WebSocketData.h +53 -46
  28. data/ext/up_ext/WebSocketExtensions.h +194 -178
  29. data/ext/up_ext/WebSocketHandshake.h +115 -110
  30. data/ext/up_ext/WebSocketProtocol.h +441 -398
  31. data/ext/up_ext/extconf.rb +1 -1
  32. data/ext/up_ext/libuwebsockets.cpp +1262 -1292
  33. data/ext/up_ext/libuwebsockets.h +337 -201
  34. data/ext/up_ext/up_ext.c +853 -163
  35. data/lib/up/bun/rack_env.rb +1 -13
  36. data/lib/up/bun/server.rb +93 -19
  37. data/lib/up/cli.rb +3 -0
  38. data/lib/up/client.rb +68 -0
  39. data/lib/up/ruby/cluster.rb +62 -0
  40. data/lib/up/ruby/rack_cluster.rb +1 -1
  41. data/lib/up/ruby/rack_server.rb +0 -1
  42. data/lib/up/u_web_socket/cluster.rb +18 -3
  43. data/lib/up/u_web_socket/server.rb +108 -15
  44. data/lib/up/version.rb +1 -1
  45. metadata +4 -15
  46. data/bin/up_node +0 -12
  47. data/bin/up_node_cluster +0 -12
  48. data/lib/up/node/cluster.rb +0 -39
  49. data/lib/up/node/cluster_cli.rb +0 -15
  50. data/lib/up/node/rack_cluster.rb +0 -25
  51. data/lib/up/node/rack_env.rb +0 -106
  52. data/lib/up/node/rack_server.rb +0 -25
  53. data/lib/up/node/server.rb +0 -84
  54. data/lib/up/node/server_cli.rb +0 -15
  55. data/lib/up/ruby/rack_env.rb +0 -97
  56. 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 cCluster;
12
- static VALUE cRackEnv;
13
- static VALUE cRequest;
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
- const rb_data_type_t up_request_t = {.wrap_struct_name = "Up::Ruby::Request",
23
- .function = {.dmark = NULL,
24
- .dfree = NULL,
25
- .dsize = NULL,
26
- .dcompact = NULL,
27
- .reserved = {0}},
28
- .parent = NULL,
29
- .data = NULL,
30
- .flags = 0};
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
- Check_Type(rpart, T_STRING);
34
- uws_res_write(USE_SSL, (uws_res_t *)res, RSTRING_PTR(rpart), RSTRING_LEN(rpart));
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
- static void up_server_request_handler(uws_res_t *res, uws_req_t *req, void *rapp) {
39
- VALUE rreq = TypedData_Wrap_Struct(cRequest, &up_request_t, req);
40
- VALUE renv = rb_class_new_instance(1, (const VALUE *)&rreq, cRackEnv);
41
- VALUE rres = rb_funcall((VALUE)rapp, id_call, 1, renv);
42
- Check_Type(rres, T_ARRAY);
43
- VALUE rparts = RARRAY_AREF(rres, 2);
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 = RARRAY_AREF(rparts, i);
49
- Check_Type(rpart, T_STRING);
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, (VALUE)res);
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
- Check_Type(rpart, T_STRING);
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
- FIX2INT(RARRAY_PTR(rres)[0]);
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 up_server_listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void *user_data) {
66
- if (listen_socket)
67
- fprintf(stderr, "Server is running on http://%s:%d\n", config.host, config.port);
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
- if (p)
72
- uws_app_destroy(USE_SSL, (uws_app_t *)p);
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
- .function = {.dmark = NULL,
77
- .dfree = up_server_t_free,
78
- .dsize = NULL,
79
- .dcompact = NULL,
80
- .reserved = {0}},
81
- .parent = NULL,
82
- .data = NULL,
83
- .flags = RUBY_TYPED_FREE_IMMEDIATELY};
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
- uws_app_t *app = NULL;
87
- return TypedData_Wrap_Struct(rclass, &up_server_t, app);
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, VALUE *rport) {
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[3] = {Qnil, Qnil, Qnil};
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
- rb_ivar_set(self, id_app, rapp);
120
- rb_ivar_set(self, id_host, rhost);
121
- rb_ivar_set(self, id_port, rport);
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
- static VALUE up_server_listen(VALUE self) {
126
- VALUE rapp = rb_ivar_get(self, id_app);
127
- VALUE rhost = rb_ivar_get(self, id_host);
128
- VALUE rport = rb_ivar_get(self, id_port);
129
-
130
- up_internal_check_arg_types(rapp, &rhost, &rport);
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
- struct us_socket_context_options_t options = {.key_file_name = NULL, .cert_file_name = NULL, .ca_file_name = NULL,
133
- .passphrase = NULL, .dh_params_file_name = NULL, .ssl_ciphers = NULL};
134
- DATA_PTR(self) = uws_create_app(USE_SSL, options);
135
- uws_app_t *app = DATA_PTR(self);
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
- static VALUE up_server_stop(VALUE self) {
146
- uws_app_t *app = DATA_PTR(self);
147
- if (!app)
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 VALUE up_cluster_listen(VALUE self) {
154
- VALUE rapp = rb_ivar_get(self, id_app);
155
- VALUE rhost = rb_ivar_get(self, id_host);
156
- VALUE rport = rb_ivar_get(self, id_port);
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
- up_internal_check_arg_types(rapp, &rhost, &rport);
159
- long i = sysconf(_SC_NPROCESSORS_ONLN);
160
- pid_t pid;
161
- for (; i > 1; i--) {
162
- pid = fork();
163
- if (pid > 0) {
164
- // do nothing
165
- } else if (pid == 0) {
166
- struct us_socket_context_options_t options = {.key_file_name = NULL, .cert_file_name = NULL, .ca_file_name = NULL,
167
- .passphrase = NULL, .dh_params_file_name = NULL, .ssl_ciphers = NULL};
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
- 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);
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 VALUE up_cluster_stop(VALUE self) {
195
- // uws_app_t *app = DATA_PTR(self);
196
- // uws_app_close(USE_SSL, app);
197
- return Qnil;
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 VALUE up_request_alloc(VALUE rclass) {
201
- return TypedData_Wrap_Struct(rclass, &up_request_t, (uws_req_t*)NULL);
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 up_internal_header_handler(const char *h, size_t h_len, const char *v, size_t v_len, void *rheaders) {
205
- rb_hash_aset((VALUE)rheaders, rb_str_new(h, h_len), rb_str_new(v, v_len));
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 up_request_headers(VALUE self) {
209
- uws_req_t *req = DATA_PTR(self);
210
- if (!req)
211
- return Qnil;
212
- VALUE rheaders = rb_hash_new();
213
- uws_req_for_each_header(req, up_internal_header_handler, (void *)rheaders);
214
- return rheaders;
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 up_request_get_header(VALUE self, VALUE rheader) {
218
- uws_req_t *req = DATA_PTR(self);
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);
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
- static VALUE up_request_get_method(VALUE self) {
228
- uws_req_t *req = DATA_PTR(self);
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
- static VALUE up_request_get_query(VALUE self) {
237
- return Qnil;
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
- static VALUE up_request_get_url(VALUE self) {
241
- uws_req_t *req = DATA_PTR(self);
242
- if (!req)
243
- return Qnil;
244
- const char *url;
245
- size_t len = uws_req_get_url(req, &url);
246
- return rb_str_new(url, len);
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
  }