opal-up 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/ext/up_ext/up_ext.c +141 -63
- data/lib/up/bun/server.rb +27 -3
- data/lib/up/cli.rb +6 -3
- data/lib/up/client.rb +2 -3
- data/lib/up/ruby/cluster.rb +1 -0
- data/lib/up/u_web_socket/cluster.rb +22 -2
- data/lib/up/u_web_socket/server.rb +47 -13
- data/lib/up/version.rb +1 -1
- metadata +4 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46fd760653d3303f32429a4dfd767c4d93ac56817393785d7da35632ab2d4ad2
|
4
|
+
data.tar.gz: a809d96c97f31441dd51c8e5c87aeaee47059835718f62de3217b38348d7d111
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6cd09583959a4340bafda2c118a570df793884fc2813e2b93a251f26220e243910263093c3ff24ac8f9dcc337af5cf1c152f5c9a491e58a5dcc32d035ef5c545
|
7
|
+
data.tar.gz: 7e45708e9f770ea45dd463dd5bf39075d7d241fe73d0ea60d638e6d80369dec4ae16661b9187525b522440133fa0272df62ca1c982bb132737807c9e05dae6f3
|
data/README.md
CHANGED
@@ -84,21 +84,21 @@ Available with `bundle exec` within the example apps or if this gem is included
|
|
84
84
|
Usage: up [options]
|
85
85
|
|
86
86
|
-h, --help Show this message
|
87
|
-
-p, --port PORT Port number the server will listen to
|
88
|
-
-b, --bind ADDRESS Address the server will listen to
|
87
|
+
-p, --port PORT Port number the server will listen to. Default: 3000
|
88
|
+
-b, --bind ADDRESS Address the server will listen to. Default: localhost
|
89
89
|
-s, --secure Use secure sockets.
|
90
90
|
When using secure sockets, the -a, -c and -k options must be provided
|
91
91
|
-a, --ca-file FILE File with CA certs
|
92
92
|
-c, --cert-file FILE File with the servers certificate
|
93
93
|
-k, --key-file FILE File with the servers certificate
|
94
|
+
-l, --log-file FILE Log file
|
94
95
|
-v, --version Show version
|
95
|
-
|
96
|
+
-w, --workers NUMBER For clusters, the number of workers to run. Default: number of processors
|
96
97
|
```
|
97
98
|
## Supported Features
|
98
99
|
|
99
100
|
Up! implements the [Rack Spec as of Rack 3.0](https://github.com/rack/rack/blob/main/SPEC.rdoc) with the following differences:
|
100
101
|
- `rack.hijack` is not implemented, but `rack.upgrade` instead is, see "Websockets" below
|
101
|
-
- `rack.input` is currently still missing
|
102
102
|
- Tempfile support is currently incomplete, affecting a few keys in the Rack Env ('tempfile' missing in Opal).
|
103
103
|
- Some Rack modules/classes still have issues when run in Opal and may not work as expected
|
104
104
|
|
data/ext/up_ext/up_ext.c
CHANGED
@@ -22,7 +22,9 @@ static VALUE cLogger;
|
|
22
22
|
|
23
23
|
static ID at_env;
|
24
24
|
static ID at_handler;
|
25
|
+
static ID at_instance;
|
25
26
|
static ID at_member_id;
|
27
|
+
static ID at_members;
|
26
28
|
static ID at_open;
|
27
29
|
static ID at_protocol;
|
28
30
|
static ID at_secret;
|
@@ -35,11 +37,13 @@ static ID id_close;
|
|
35
37
|
static ID id_each;
|
36
38
|
static ID id_host;
|
37
39
|
static ID id_logger;
|
40
|
+
static ID id_new;
|
38
41
|
static ID id_on_close;
|
39
42
|
static ID id_on_drained;
|
40
43
|
static ID id_on_message;
|
41
44
|
static ID id_on_open;
|
42
45
|
static ID id_port;
|
46
|
+
static ID id_publish;
|
43
47
|
|
44
48
|
static rb_encoding *utf8_encoding;
|
45
49
|
static rb_encoding *binary_encoding;
|
@@ -119,6 +123,8 @@ typedef struct server_s {
|
|
119
123
|
VALUE port;
|
120
124
|
VALUE logger;
|
121
125
|
VALUE env_template;
|
126
|
+
VALUE body;
|
127
|
+
VALUE env;
|
122
128
|
int workers;
|
123
129
|
int member_id;
|
124
130
|
char secret[37];
|
@@ -280,6 +286,43 @@ typedef struct publish_data_s {
|
|
280
286
|
server_s *s;
|
281
287
|
} publish_data_s;
|
282
288
|
|
289
|
+
static void up_internal_call_app(server_s *s, uws_res_t *res, VALUE env) {
|
290
|
+
// call app
|
291
|
+
VALUE rres = rb_funcall(s->rapp, id_call, 1, env);
|
292
|
+
|
293
|
+
if (TYPE(rres) != T_ARRAY)
|
294
|
+
goto response_error;
|
295
|
+
|
296
|
+
// response status
|
297
|
+
VALUE rstatus = rb_ary_entry(rres, 0);
|
298
|
+
if (!up_internal_set_response_status(res, rstatus))
|
299
|
+
goto response_error;
|
300
|
+
|
301
|
+
// collect headers
|
302
|
+
VALUE rheaders = rb_ary_entry(rres, 1);
|
303
|
+
if (TYPE(rheaders) != T_HASH)
|
304
|
+
goto response_error;
|
305
|
+
rb_hash_foreach(rheaders, up_internal_res_header_handler, (VALUE)res);
|
306
|
+
|
307
|
+
// collect response body
|
308
|
+
VALUE rparts = rb_ary_entry(rres, 2);
|
309
|
+
up_internal_collect_response_body(res, rparts);
|
310
|
+
|
311
|
+
// end response
|
312
|
+
uws_res_end_without_body(USE_SSL, res, false);
|
313
|
+
|
314
|
+
// close resources if necessary
|
315
|
+
if (rb_respond_to(rparts, id_close))
|
316
|
+
rb_funcall(rparts, id_close, 0);
|
317
|
+
|
318
|
+
return;
|
319
|
+
response_error:
|
320
|
+
fprintf(stderr, "response error\n");
|
321
|
+
uws_res_end_without_body(USE_SSL, res, false);
|
322
|
+
}
|
323
|
+
|
324
|
+
static void up_internal_abort_data(uws_res_t *res, void *arg) {}
|
325
|
+
|
283
326
|
static void up_internal_process_publish_post_data(uws_res_t *res,
|
284
327
|
const char *chunk,
|
285
328
|
size_t chunk_length,
|
@@ -296,9 +339,22 @@ static void up_internal_process_publish_post_data(uws_res_t *res,
|
|
296
339
|
break;
|
297
340
|
}
|
298
341
|
}
|
299
|
-
if (channel_length > 0 && message_length > 0)
|
342
|
+
if (channel_length > 0 && message_length > 0)
|
300
343
|
uws_publish(USE_SSL, s->app, channel_start, channel_length, message_start,
|
301
344
|
message_length, TEXT, false);
|
345
|
+
}
|
346
|
+
|
347
|
+
static void up_internal_process_post_data(uws_res_t *res, const char *chunk,
|
348
|
+
size_t chunk_length, bool is_end,
|
349
|
+
void *arg) {
|
350
|
+
server_s *s = (server_s *)arg;
|
351
|
+
rb_str_cat(s->body, chunk, chunk_length);
|
352
|
+
if (is_end) {
|
353
|
+
// set rack.input
|
354
|
+
rb_hash_aset(s->env, rack_input, rb_funcall(cStringIO, id_new, 1, s->body));
|
355
|
+
up_internal_call_app(s, res, s->env);
|
356
|
+
s->body = Qnil;
|
357
|
+
s->env = Qnil;
|
302
358
|
}
|
303
359
|
}
|
304
360
|
|
@@ -319,48 +375,25 @@ static void up_internal_publish_handler(uws_res_t *res, uws_req_t *req,
|
|
319
375
|
}
|
320
376
|
}
|
321
377
|
|
322
|
-
static void
|
323
|
-
void *arg) {
|
378
|
+
static void up_server_any_handler(uws_res_t *res, uws_req_t *req, void *arg) {
|
324
379
|
// prepare rack env
|
325
380
|
server_s *s = (server_s *)arg;
|
326
|
-
VALUE
|
327
|
-
up_server_prepare_env(
|
328
|
-
|
329
|
-
|
330
|
-
|
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);
|
381
|
+
VALUE env = rb_hash_dup(s->env_template);
|
382
|
+
up_server_prepare_env(env, req);
|
383
|
+
up_internal_call_app(s, res, env);
|
384
|
+
RB_GC_GUARD(env);
|
385
|
+
}
|
351
386
|
|
352
|
-
|
353
|
-
|
354
|
-
|
387
|
+
static void up_server_post_handler(uws_res_t *res, uws_req_t *req, void *arg) {
|
388
|
+
// prepare rack env
|
389
|
+
server_s *s = (server_s *)arg;
|
390
|
+
s->env = rb_hash_dup(s->env_template);
|
391
|
+
up_server_prepare_env(s->env, req);
|
355
392
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
RB_GC_GUARD(renv);
|
361
|
-
response_error:
|
362
|
-
fprintf(stderr, "response error\n");
|
363
|
-
uws_res_end_without_body(USE_SSL, res, false);
|
393
|
+
// receive POST data
|
394
|
+
s->body = rb_enc_str_new("", 0, utf8_encoding);
|
395
|
+
uws_res_on_data(USE_SSL, res, up_internal_process_post_data, (void *)s);
|
396
|
+
uws_res_on_aborted(USE_SSL, res, up_internal_abort_data, NULL);
|
364
397
|
}
|
365
398
|
|
366
399
|
static void
|
@@ -411,14 +444,14 @@ static VALUE up_client_pending(VALUE self) {
|
|
411
444
|
return INT2FIX(0);
|
412
445
|
}
|
413
446
|
|
414
|
-
static void up_client_cluster_publish(
|
447
|
+
static void up_client_cluster_publish(char *scrt, int st, VALUE channel,
|
415
448
|
VALUE message) {
|
416
449
|
const char *opening_line = "POST " INTERNAL_PUBLISH_PATH " HTTP/1.1\r\n";
|
417
450
|
const char *host_header = "Host: localhost\r\n";
|
418
451
|
const char *secret = "Secret: ";
|
419
452
|
char secret_header[50];
|
420
453
|
memcpy(secret_header, secret, 8);
|
421
|
-
memcpy(secret_header + 8,
|
454
|
+
memcpy(secret_header + 8, scrt, 36);
|
422
455
|
memcpy(secret_header + 8 + 36, "\r\n", 2);
|
423
456
|
const char *content_type = "Content-Type: text/plain\r\n";
|
424
457
|
long c_length = RSTRING_LEN(channel) + RSTRING_LEN(message) + 2;
|
@@ -447,12 +480,25 @@ static void up_client_cluster_publish(server_s *s, int st, VALUE channel,
|
|
447
480
|
// fprintf(stderr, "read: %s\n", read_buf);
|
448
481
|
}
|
449
482
|
|
450
|
-
static
|
483
|
+
static void up_internal_publish_to_member(server_s *s, VALUE channel,
|
484
|
+
VALUE message, int member_idx) {
|
485
|
+
struct sockaddr_in member_addr = {.sin_addr.s_addr = inet_addr("127.0.0.1"),
|
486
|
+
.sin_family = AF_INET};
|
487
|
+
int st = socket(AF_INET, SOCK_STREAM, 0);
|
488
|
+
if (st) {
|
489
|
+
member_addr.sin_port = htons(FIX2INT(s->port) + member_idx);
|
490
|
+
if (connect(st, (struct sockaddr *)&member_addr,
|
491
|
+
sizeof(struct sockaddr_in)) == 0) {
|
492
|
+
up_client_cluster_publish(s->secret, st, channel, message);
|
493
|
+
close(st);
|
494
|
+
}
|
495
|
+
}
|
496
|
+
}
|
497
|
+
|
498
|
+
static VALUE up_client_publish(VALUE self, VALUE channel, VALUE message) {
|
451
499
|
uws_websocket_t *ws = DATA_PTR(self);
|
452
500
|
if (!ws)
|
453
501
|
return Qnil;
|
454
|
-
VALUE channel, message, engine;
|
455
|
-
rb_scan_args(argc, argv, "21", &channel, &message, &engine);
|
456
502
|
if (TYPE(channel) != T_STRING)
|
457
503
|
channel = rb_obj_as_string(channel);
|
458
504
|
if (TYPE(message) != T_STRING)
|
@@ -467,20 +513,9 @@ static VALUE up_client_publish(int argc, VALUE *argv, VALUE self) {
|
|
467
513
|
|
468
514
|
// publish to cluster members
|
469
515
|
int i;
|
470
|
-
struct sockaddr_in member_addr = {
|
471
|
-
.sin_addr.s_addr = inet_addr("127.0.0.1"), .sin_family = AF_INET};
|
472
516
|
for (i = 1; i <= s->workers; i++) {
|
473
|
-
if (i != s->member_id)
|
474
|
-
|
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
|
-
}
|
517
|
+
if (i != s->member_id)
|
518
|
+
up_internal_publish_to_member(s, channel, message, i);
|
484
519
|
}
|
485
520
|
}
|
486
521
|
return res ? Qtrue : Qfalse;
|
@@ -608,8 +643,8 @@ void up_ws_drain_handler(uws_websocket_t *ws, void *user_data) {
|
|
608
643
|
|
609
644
|
void up_ws_ping_handler(uws_websocket_t *ws, const char *message, size_t length,
|
610
645
|
void *user_data) {
|
611
|
-
/* You don't need to handle this one, we automatically respond to pings as
|
612
|
-
* standard */
|
646
|
+
/* You don't need to handle this one, we automatically respond to pings as
|
647
|
+
* per standard */
|
613
648
|
}
|
614
649
|
|
615
650
|
void up_ws_pong_handler(uws_websocket_t *ws, const char *message, size_t length,
|
@@ -747,6 +782,7 @@ static void up_internal_close_sockets(int signal) {
|
|
747
782
|
static VALUE up_server_listen(VALUE self) {
|
748
783
|
server_s *s = DATA_PTR(self);
|
749
784
|
up_internal_check_arg_types(s->rapp, &s->host, &s->port);
|
785
|
+
rb_ivar_set(mUp, at_instance, self);
|
750
786
|
|
751
787
|
s->env_template = rb_hash_dup(rack_env_template);
|
752
788
|
// When combined with SCRIPT_NAME and PATH_INFO, these variables can be used
|
@@ -801,7 +837,8 @@ static VALUE up_server_listen(VALUE self) {
|
|
801
837
|
sigemptyset(&upclcl.sa_mask);
|
802
838
|
sigaction(SIGINT, &upclcl, NULL);
|
803
839
|
}
|
804
|
-
|
840
|
+
uws_app_post(USE_SSL, s->app, "/*", up_server_post_handler, (void *)s);
|
841
|
+
uws_app_any(USE_SSL, s->app, "/*", up_server_any_handler, (void *)s);
|
805
842
|
uws_ws(USE_SSL, s->app, "/*",
|
806
843
|
(uws_socket_behavior_t){.compression = DISABLED,
|
807
844
|
.maxPayloadLength = 5 * 1024 * 1024,
|
@@ -820,6 +857,33 @@ static VALUE up_server_listen(VALUE self) {
|
|
820
857
|
return self;
|
821
858
|
}
|
822
859
|
|
860
|
+
static VALUE up_server_publish(VALUE self, VALUE channel, VALUE message) {
|
861
|
+
if (TYPE(channel) != T_STRING)
|
862
|
+
channel = rb_obj_as_string(channel);
|
863
|
+
if (TYPE(message) != T_STRING)
|
864
|
+
message = rb_obj_as_string(message);
|
865
|
+
server_s *s = DATA_PTR(self);
|
866
|
+
VALUE members = rb_ivar_get(self, at_members);
|
867
|
+
if (members != Qnil) {
|
868
|
+
long i, mb_cnt = RARRAY_LEN(members);
|
869
|
+
for (i = 0; i < mb_cnt; i++) {
|
870
|
+
up_internal_publish_to_member(s, channel, message, i);
|
871
|
+
}
|
872
|
+
} else {
|
873
|
+
uws_publish(USE_SSL, s->app, RSTRING_PTR(channel), RSTRING_LEN(channel),
|
874
|
+
RSTRING_PTR(message), RSTRING_LEN(message), TEXT, false);
|
875
|
+
if (s->member_id > 0) {
|
876
|
+
// publish to cluster members
|
877
|
+
int i;
|
878
|
+
for (i = 1; i <= s->workers; i++) {
|
879
|
+
if (i != s->member_id)
|
880
|
+
up_internal_publish_to_member(s, channel, message, i);
|
881
|
+
}
|
882
|
+
}
|
883
|
+
}
|
884
|
+
return Qtrue;
|
885
|
+
}
|
886
|
+
|
823
887
|
static VALUE up_server_stop(VALUE self) {
|
824
888
|
server_s *s = DATA_PTR(self);
|
825
889
|
if (!s->app)
|
@@ -891,10 +955,19 @@ void up_setup_rack_env_template(void) {
|
|
891
955
|
rb_hash_aset(rack_env_template, HTTP_VERSION, http11);
|
892
956
|
}
|
893
957
|
|
958
|
+
static VALUE up_publish(VALUE self, VALUE channel, VALUE message) {
|
959
|
+
VALUE instance = rb_ivar_get(mUp, at_instance);
|
960
|
+
if (instance != Qnil)
|
961
|
+
return rb_funcall(instance, id_publish, 2, channel, message);
|
962
|
+
return Qfalse;
|
963
|
+
}
|
964
|
+
|
894
965
|
void Init_up_ext(void) {
|
895
966
|
at_env = rb_intern("@env");
|
896
967
|
at_handler = rb_intern("@handler");
|
968
|
+
at_instance = rb_intern("@instance");
|
897
969
|
at_member_id = rb_intern("@member_id");
|
970
|
+
at_members = rb_intern("@members");
|
898
971
|
at_open = rb_intern("@open");
|
899
972
|
at_protocol = rb_intern("@protocol");
|
900
973
|
at_secret = rb_intern("@secret");
|
@@ -907,11 +980,13 @@ void Init_up_ext(void) {
|
|
907
980
|
id_each = rb_intern("each");
|
908
981
|
id_host = rb_intern("host");
|
909
982
|
id_logger = rb_intern("logger");
|
983
|
+
id_new = rb_intern("new");
|
910
984
|
id_on_close = rb_intern("on_close");
|
911
985
|
id_on_drained = rb_intern("on_drained");
|
912
986
|
id_on_message = rb_intern("on_message");
|
913
987
|
id_on_open = rb_intern("on_open");
|
914
988
|
id_port = rb_intern("port");
|
989
|
+
id_publish = rb_intern("publish");
|
915
990
|
|
916
991
|
utf8_encoding = rb_enc_find("UTF-8");
|
917
992
|
binary_encoding = rb_enc_find("binary");
|
@@ -937,23 +1012,25 @@ void Init_up_ext(void) {
|
|
937
1012
|
rb_gc_register_address(&cLogger);
|
938
1013
|
cLogger = rb_const_get(rb_cObject, rb_intern("Logger"));
|
939
1014
|
rb_gc_register_address(&default_logger);
|
940
|
-
default_logger = rb_funcall(cLogger,
|
1015
|
+
default_logger = rb_funcall(cLogger, id_new, 1, rb_stderr);
|
941
1016
|
|
942
1017
|
rb_require("stringio");
|
943
1018
|
|
944
1019
|
rb_gc_register_address(&cStringIO);
|
945
1020
|
cStringIO = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
946
1021
|
rb_gc_register_address(&default_input);
|
947
|
-
default_input = rb_funcall(cStringIO,
|
1022
|
+
default_input = rb_funcall(cStringIO, id_new, 1, empty_string);
|
948
1023
|
|
949
1024
|
up_setup_rack_env_template();
|
950
1025
|
|
951
1026
|
mUp = rb_define_module("Up");
|
1027
|
+
rb_define_singleton_method(mUp, "publish", up_publish, 2);
|
1028
|
+
|
952
1029
|
cClient = rb_define_class_under(mUp, "Client", rb_cObject);
|
953
1030
|
rb_define_alloc_func(cClient, up_client_alloc);
|
954
1031
|
rb_define_method(cClient, "close", up_client_close, 0);
|
955
1032
|
rb_define_method(cClient, "pending", up_client_pending, 0);
|
956
|
-
rb_define_method(cClient, "publish", up_client_publish,
|
1033
|
+
rb_define_method(cClient, "publish", up_client_publish, 2);
|
957
1034
|
rb_define_method(cClient, "subscribe", up_client_subscribe, -1);
|
958
1035
|
rb_define_method(cClient, "unsubscribe", up_client_unsubscribe, -1);
|
959
1036
|
rb_define_method(cClient, "write", up_client_write, 1);
|
@@ -964,5 +1041,6 @@ void Init_up_ext(void) {
|
|
964
1041
|
rb_define_alloc_func(cServer, up_server_alloc);
|
965
1042
|
rb_define_method(cServer, "initialize", up_server_init, -1);
|
966
1043
|
rb_define_method(cServer, "listen", up_server_listen, 0);
|
1044
|
+
rb_define_method(cServer, "publish", up_server_publish, 2);
|
967
1045
|
rb_define_method(cServer, "stop", up_server_stop, 0);
|
968
1046
|
}
|
data/lib/up/bun/server.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
# backtick_javascript: true
|
2
2
|
require 'logger'
|
3
|
+
require 'stringio'
|
3
4
|
require 'up/cli'
|
4
5
|
require 'up/client'
|
5
6
|
|
6
7
|
module Up
|
8
|
+
class << self
|
9
|
+
def publish(channel, message)
|
10
|
+
raise 'no instance running' unless @instance
|
11
|
+
@instance&.publish(channel, message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
7
15
|
module Bun
|
8
16
|
class Server
|
9
17
|
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, logger: Logger.new(STDERR))
|
@@ -16,7 +24,7 @@ module Up
|
|
16
24
|
@ca_file = ca_file
|
17
25
|
@cert_file = cert_file
|
18
26
|
@key_file = key_file
|
19
|
-
@default_input =
|
27
|
+
@default_input = StringIO.new('', 'r')
|
20
28
|
@server = nil
|
21
29
|
@logger = logger
|
22
30
|
@t_factory = proc { |filename, _content_type| File.new(filename, 'a+') }
|
@@ -49,6 +57,7 @@ module Up
|
|
49
57
|
}
|
50
58
|
def listen
|
51
59
|
raise "already running" if @server
|
60
|
+
::Up.instance_variable_set(:@instance, self)
|
52
61
|
%x{
|
53
62
|
const oubs = Opal.Up.Bun.Server;
|
54
63
|
const ouwc = Opal.Up.Client;
|
@@ -57,11 +66,17 @@ module Up
|
|
57
66
|
port: #@port,
|
58
67
|
hostname: #@host,
|
59
68
|
development: false,
|
60
|
-
fetch(req, server) {
|
69
|
+
async fetch(req, server) {
|
61
70
|
const upgrade = req.headers.get('Upgrade');
|
62
71
|
const env = new Map();
|
63
72
|
env.set('rack.errors',#{STDERR});
|
64
|
-
|
73
|
+
if (req.method === 'POST') {
|
74
|
+
let body = await req.text();
|
75
|
+
console.log('received: ', body);
|
76
|
+
env.set('rack.input', #{StringIO.new(`body`)});
|
77
|
+
} else {
|
78
|
+
env.set('rack.input', #@default_input);
|
79
|
+
}
|
65
80
|
env.set('rack.logger', #@logger);
|
66
81
|
env.set('rack.multipart.buffer_size', 4096);
|
67
82
|
env.set('rack.multipart.tempfile_factory', #@t_factory);
|
@@ -152,6 +167,15 @@ module Up
|
|
152
167
|
}
|
153
168
|
end
|
154
169
|
|
170
|
+
def publish(channel, message)
|
171
|
+
%x{
|
172
|
+
if (!message.$$is_string) {
|
173
|
+
message = JSON.stringify(message);
|
174
|
+
}
|
175
|
+
#@server.publish(channel, message);
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
155
179
|
def stop
|
156
180
|
if Up::CLI::stoppable?
|
157
181
|
`#@server.stop()`
|
data/lib/up/cli.rb
CHANGED
@@ -14,10 +14,10 @@ module Up
|
|
14
14
|
puts self
|
15
15
|
exit
|
16
16
|
end
|
17
|
-
on('-p', '--port PORT', String, 'Port number the server will listen to') do |port|
|
17
|
+
on('-p', '--port PORT', String, 'Port number the server will listen to. Default: 3000') do |port|
|
18
18
|
options[:port] = port.to_i
|
19
19
|
end
|
20
|
-
on('-b', '--bind ADDRESS', String, 'Address the server will listen to') do |host|
|
20
|
+
on('-b', '--bind ADDRESS', String, 'Address the server will listen to. Default: localhost') do |host|
|
21
21
|
options[:host] = host
|
22
22
|
end
|
23
23
|
on('-s', '--secure', "Use secure sockets.\nWhen using secure sockets, the -a, -c and -k options must be provided") do
|
@@ -32,13 +32,16 @@ module Up
|
|
32
32
|
on('-k', '--key-file FILE', String, 'File with the servers certificate') do |key_file|
|
33
33
|
options[:key_file] = key_file
|
34
34
|
end
|
35
|
-
on('-l', '--log-file FILE', String, '
|
35
|
+
on('-l', '--log-file FILE', String, 'Log file') do |log_file|
|
36
36
|
options[:logger] = Logger.new(File.new(log_file, 'a+'))
|
37
37
|
end
|
38
38
|
on('-v', '--version', 'Show version') do
|
39
39
|
puts "Up! v#{Up::VERSION}"
|
40
40
|
exit
|
41
41
|
end
|
42
|
+
on('-w', '--workers NUMBER', 'For clusters, the number of workers to run. Default: number of processors') do |workers|
|
43
|
+
options[:workers] = workers.to_i
|
44
|
+
end
|
42
45
|
end
|
43
46
|
|
44
47
|
def parse!
|
data/lib/up/client.rb
CHANGED
@@ -36,15 +36,14 @@ module Up
|
|
36
36
|
`#@ws?.getBufferedAmount()`
|
37
37
|
end
|
38
38
|
|
39
|
-
def publish(channel, message
|
39
|
+
def publish(channel, message)
|
40
40
|
res = false
|
41
|
-
raise 'publish engine not supported' if engine
|
42
41
|
%x{
|
43
42
|
if (!message.$$is_string) {
|
44
43
|
message = JSON.stringify(message);
|
45
44
|
}
|
46
45
|
res = #@server?.publish(channel, message);
|
47
|
-
if (
|
46
|
+
if (#@worker) {
|
48
47
|
process.send({c: channel, m: message});
|
49
48
|
}
|
50
49
|
}
|
data/lib/up/ruby/cluster.rb
CHANGED
@@ -18,6 +18,7 @@ module Up
|
|
18
18
|
|
19
19
|
def listen
|
20
20
|
raise "already running" unless @members.empty?
|
21
|
+
::Up.instance_variable_set(:@instance, self)
|
21
22
|
%x{
|
22
23
|
if (cluster.isPrimary) {
|
23
24
|
cluster.on('message', (worker, message, handle) => {
|
@@ -33,9 +34,9 @@ module Up
|
|
33
34
|
#@members[i] = cluster.fork();
|
34
35
|
}
|
35
36
|
} else {
|
36
|
-
|
37
|
+
#@worker = true;
|
37
38
|
function process_message_handler(message, handle) {
|
38
|
-
|
39
|
+
#@server.publish(message.c, message.m);
|
39
40
|
}
|
40
41
|
process.on('message', process_message_handler);
|
41
42
|
#{super}
|
@@ -43,6 +44,25 @@ module Up
|
|
43
44
|
}
|
44
45
|
end
|
45
46
|
|
47
|
+
def publish(channel, message)
|
48
|
+
%x{
|
49
|
+
if (!message.$$is_string) {
|
50
|
+
message = JSON.stringify(message);
|
51
|
+
}
|
52
|
+
if (#@worker ) {
|
53
|
+
#@server?.publish(channel, message);
|
54
|
+
process.send({c: channel, m: message});
|
55
|
+
} else if (#@members) {
|
56
|
+
for (let member of #@members) {
|
57
|
+
if (member !== worker) {
|
58
|
+
member.send(message);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
46
66
|
def stop
|
47
67
|
if Up::CLI::stoppable?
|
48
68
|
@members.each { |m| `m.kill()` }
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# backtick_javascript: true
|
2
2
|
require 'logger'
|
3
|
+
require 'stringio'
|
3
4
|
require 'up/cli'
|
4
5
|
require 'up/client'
|
5
6
|
|
@@ -10,6 +11,13 @@ require 'up/client'
|
|
10
11
|
}
|
11
12
|
|
12
13
|
module Up
|
14
|
+
class << self
|
15
|
+
def publish(channel, message)
|
16
|
+
raise 'no instance running' unless @instance
|
17
|
+
@instance&.publish(channel, message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
module UWebSocket
|
14
22
|
class Server
|
15
23
|
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, logger: Logger.new(STDERR))
|
@@ -22,7 +30,7 @@ module Up
|
|
22
30
|
@ca_file = ca_file
|
23
31
|
@cert_file = cert_file
|
24
32
|
@key_file = key_file
|
25
|
-
@default_input =
|
33
|
+
@default_input = StringIO.new('', 'r')
|
26
34
|
@server = nil
|
27
35
|
@logger = logger
|
28
36
|
@t_factory = proc { |filename, _content_type| File.new(filename, 'a+') }
|
@@ -51,20 +59,20 @@ module Up
|
|
51
59
|
#{`parts`.close if `parts`.respond_to?(:close)}
|
52
60
|
}
|
53
61
|
|
54
|
-
self.prepare_env = function(req) {
|
62
|
+
self.prepare_env = function(req, ins) {
|
55
63
|
const env = new Map();
|
56
64
|
env.set('rack.errors',#{STDERR});
|
57
|
-
env.set('rack.input',
|
58
|
-
env.set('rack.logger',
|
65
|
+
env.set('rack.input', ins.default_input);
|
66
|
+
env.set('rack.logger', ins.logger);
|
59
67
|
env.set('rack.multipart.buffer_size', 4096);
|
60
|
-
env.set('rack.multipart.tempfile_factory',
|
61
|
-
env.set('rack.url_scheme',
|
68
|
+
env.set('rack.multipart.tempfile_factory', ins.t_factory);
|
69
|
+
env.set('rack.url_scheme', ins.scheme);
|
62
70
|
env.set('SCRIPT_NAME', "");
|
63
71
|
env.set('SERVER_PROTOCOL', 'HTTP/1.1');
|
64
72
|
env.set('HTTP_VERSION', 'HTTP/1.1');
|
65
|
-
env.set('SERVER_NAME',
|
66
|
-
env.set('SERVER_PORT',
|
67
|
-
env.set('QUERY_STRING', req.getQuery());
|
73
|
+
env.set('SERVER_NAME', ins.host);
|
74
|
+
env.set('SERVER_PORT', ins.port);
|
75
|
+
env.set('QUERY_STRING', req.getQuery() || '');
|
68
76
|
env.set('REQUEST_METHOD', req.getMethod().toUpperCase());
|
69
77
|
env.set('PATH_INFO', req.getUrl());
|
70
78
|
req.forEach((k, v) => { env.set('HTTP_' + k.toUpperCase().replaceAll('-', '_'), v) });
|
@@ -74,6 +82,7 @@ module Up
|
|
74
82
|
|
75
83
|
def listen
|
76
84
|
raise "already running" if @server
|
85
|
+
::Up.instance_variable_set(:@instance, self)
|
77
86
|
%x{
|
78
87
|
const ouws = Opal.Up.UWebSocket.Server;
|
79
88
|
const ouwc = Opal.Up.Client;
|
@@ -83,8 +92,24 @@ module Up
|
|
83
92
|
} else {
|
84
93
|
#@server = uws.App();
|
85
94
|
}
|
95
|
+
#@server.post('/*', (res, req) => {
|
96
|
+
const env = ouws.prepare_env(req, self);
|
97
|
+
let buffer = Buffer.from('');
|
98
|
+
res.onData((chunk, is_last) => {
|
99
|
+
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
100
|
+
if (is_last === true) {
|
101
|
+
env.set('rack.input', #{StringIO.new(`buffer.toString()`)});
|
102
|
+
const rack_res = #@app.$call(env);
|
103
|
+
res.writeStatus(rack_res[0].toString() + ' OK');
|
104
|
+
ouws.handle_headers(rack_res[1], res);
|
105
|
+
ouws.handle_response(rack_res[2], res);
|
106
|
+
res.end();
|
107
|
+
}
|
108
|
+
});
|
109
|
+
res.onAborted(() => {});
|
110
|
+
});
|
86
111
|
#@server.any('/*', (res, req) => {
|
87
|
-
const rack_res = #@app.$call(ouws.prepare_env(req));
|
112
|
+
const rack_res = #@app.$call(ouws.prepare_env(req, self));
|
88
113
|
res.writeStatus(rack_res[0].toString() + ' OK');
|
89
114
|
ouws.handle_headers(rack_res[1], res);
|
90
115
|
ouws.handle_response(rack_res[2], res);
|
@@ -128,7 +153,7 @@ module Up
|
|
128
153
|
},
|
129
154
|
sendPingsAutomatically: true,
|
130
155
|
upgrade: (res, req, context) => {
|
131
|
-
const env = ouws.prepare_env(req);
|
156
|
+
const env = ouws.prepare_env(req, self);
|
132
157
|
env.set('rack.upgrade?', #{:websocket});
|
133
158
|
const rack_res = #@app.$call(env);
|
134
159
|
const handler = env.get('rack.upgrade');
|
@@ -138,9 +163,9 @@ module Up
|
|
138
163
|
client.open = false;
|
139
164
|
client.handler = handler
|
140
165
|
client.protocol = #{:websocket};
|
141
|
-
client.server =
|
166
|
+
client.server = #@server;
|
142
167
|
client.timeout = 120;
|
143
|
-
if (
|
168
|
+
if (#@worker) {
|
144
169
|
client.worker = true;
|
145
170
|
}
|
146
171
|
res.upgrade({ client: client },
|
@@ -164,6 +189,15 @@ module Up
|
|
164
189
|
}
|
165
190
|
end
|
166
191
|
|
192
|
+
def publish(channel, message)
|
193
|
+
%x{
|
194
|
+
if (!message.$$is_string) {
|
195
|
+
message = JSON.stringify(message);
|
196
|
+
}
|
197
|
+
#@server.publish(channel, message);
|
198
|
+
}
|
199
|
+
end
|
200
|
+
|
167
201
|
def stop
|
168
202
|
if Up::CLI::stoppable?
|
169
203
|
`#@server.close()`
|
data/lib/up/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opal-up
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Biedermann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-02-
|
11
|
+
date: 2024-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: logger
|
@@ -58,26 +58,6 @@ dependencies:
|
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: 3.0.9
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
|
-
name: rackup
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
64
|
-
requirements:
|
65
|
-
- - ">="
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
version: 0.2.2
|
68
|
-
- - "<"
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
version: 3.0.0
|
71
|
-
type: :runtime
|
72
|
-
prerelease: false
|
73
|
-
version_requirements: !ruby/object:Gem::Requirement
|
74
|
-
requirements:
|
75
|
-
- - ">="
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: 0.2.2
|
78
|
-
- - "<"
|
79
|
-
- !ruby/object:Gem::Version
|
80
|
-
version: 3.0.0
|
81
61
|
- !ruby/object:Gem::Dependency
|
82
62
|
name: rake
|
83
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,7 +181,7 @@ files:
|
|
201
181
|
- lib/up/u_web_socket/server.rb
|
202
182
|
- lib/up/u_web_socket/server_cli.rb
|
203
183
|
- lib/up/version.rb
|
204
|
-
homepage:
|
184
|
+
homepage: https://github.com/janbiedermann/up
|
205
185
|
licenses:
|
206
186
|
- MIT
|
207
187
|
metadata: {}
|
@@ -223,5 +203,5 @@ requirements: []
|
|
223
203
|
rubygems_version: 3.5.3
|
224
204
|
signing_key:
|
225
205
|
specification_version: 4
|
226
|
-
summary: Rack server for Opal
|
206
|
+
summary: Rack server for Opal and Ruby
|
227
207
|
test_files: []
|