opal-up 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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
  }