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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd09c3697caf76eb487770a2818af0a06ee4711761a15f2d80d7d0d291747234
4
- data.tar.gz: cc53773debbd42d66a2aa99e1b3d9ec3e3fcd05db99f5e58bfab94e5ecf1811a
3
+ metadata.gz: 46fd760653d3303f32429a4dfd767c4d93ac56817393785d7da35632ab2d4ad2
4
+ data.tar.gz: a809d96c97f31441dd51c8e5c87aeaee47059835718f62de3217b38348d7d111
5
5
  SHA512:
6
- metadata.gz: 38c374376cfe497c4abd8039272ae616d44e5152d3ba15fc47578d789bd16d88cf8af21d2471fd71eafa0e3852f60704a0aabf03dd560bac2de5c87591724d1a
7
- data.tar.gz: 0467d90a76aedd75515f38c678ce7cdcac7e36760a687c9ae6283dfdd9723ea2d0e005d67e357fbcf60491c594511c45be105002a008f650c2eb488f5f315c53
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 up_server_request_handler(uws_res_t *res, uws_req_t *req,
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 renv = rb_hash_dup(s->env_template);
327
- up_server_prepare_env(renv, req);
328
-
329
- // call app
330
- VALUE rres = rb_funcall(s->rapp, id_call, 1, renv);
331
- if (TYPE(rres) != T_ARRAY)
332
- goto response_error;
333
-
334
- // response status
335
- VALUE rstatus = rb_ary_entry(rres, 0);
336
- if (!up_internal_set_response_status(res, rstatus))
337
- goto response_error;
338
-
339
- // collect headers
340
- VALUE rheaders = rb_ary_entry(rres, 1);
341
- if (TYPE(rheaders) != T_HASH)
342
- goto response_error;
343
- rb_hash_foreach(rheaders, up_internal_res_header_handler, (VALUE)res);
344
-
345
- // collect response body
346
- VALUE rparts = rb_ary_entry(rres, 2);
347
- up_internal_collect_response_body(res, rparts);
348
-
349
- // end response
350
- uws_res_end_without_body(USE_SSL, res, false);
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
- // close resources if necessary
353
- if (rb_respond_to(rparts, id_close))
354
- rb_funcall(rparts, id_close, 0);
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
- return;
357
- RB_GC_GUARD(rstatus);
358
- RB_GC_GUARD(rheaders);
359
- RB_GC_GUARD(rres);
360
- RB_GC_GUARD(renv);
361
- response_error:
362
- fprintf(stderr, "response error\n");
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(server_s *s, int st, VALUE channel,
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, s->secret, 36);
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 VALUE up_client_publish(int argc, VALUE *argv, VALUE self) {
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
- int st = socket(AF_INET, SOCK_STREAM, 0);
475
- if (st) {
476
- member_addr.sin_port = htons(FIX2INT(s->port) + i);
477
- if (connect(st, (struct sockaddr *)&member_addr,
478
- sizeof(struct sockaddr_in)) == 0) {
479
- up_client_cluster_publish(s, st, channel, message);
480
- close(st);
481
- }
482
- }
483
- }
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 per
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
- uws_app_any(USE_SSL, s->app, "/*", up_server_request_handler, (void *)s);
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, rb_intern("new"), 1, rb_stderr);
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, rb_intern("new"), 1, empty_string);
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, -1);
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 = IO.new
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
- env.set('rack.input', #@default_input);
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, 'log file') do |log_file|
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, engine = nil)
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 (engine !== false && self.worker) {
46
+ if (#@worker) {
48
47
  process.send({c: channel, m: message});
49
48
  }
50
49
  }
@@ -15,6 +15,7 @@ module Up
15
15
 
16
16
  def listen
17
17
  raise "already running" unless @members.empty?
18
+ ::Up.instance_variable_set(:@instance, self)
18
19
  @workers.times do
19
20
  @members << fork do
20
21
  @member_id = @members.size + 1
@@ -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
- self.worker = true;
37
+ #@worker = true;
37
38
  function process_message_handler(message, handle) {
38
- self.server.publish(message.c, message.m);
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 = IO.new
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', #@default_input);
58
- env.set('rack.logger', #@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', #@t_factory);
61
- env.set('rack.url_scheme', #@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', #@host);
66
- env.set('SERVER_PORT', #@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 = self.server;
166
+ client.server = #@server;
142
167
  client.timeout = 120;
143
- if (self.worker) {
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
@@ -1,3 +1,3 @@
1
1
  module Up
2
- VERSION = '0.0.5'.freeze
2
+ VERSION = '0.0.7'.freeze
3
3
  end
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.5
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-14 00:00:00.000000000 Z
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: []