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 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: []