opal-up 0.0.5 → 0.0.7
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.
- 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: []
|