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.
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
  }