opal-up 0.0.3 → 0.0.4

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