opal-up 0.0.4 → 0.0.6

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.
data/ext/up_ext/up_ext.c CHANGED
@@ -3,6 +3,7 @@
3
3
  #include <arpa/inet.h>
4
4
  #include <ruby.h>
5
5
  #include <ruby/encoding.h>
6
+ #include <signal.h>
6
7
  #include <sys/socket.h>
7
8
  #include <sys/wait.h>
8
9
  #include <unistd.h>
@@ -21,7 +22,9 @@ static VALUE cLogger;
21
22
 
22
23
  static ID at_env;
23
24
  static ID at_handler;
25
+ static ID at_instance;
24
26
  static ID at_member_id;
27
+ static ID at_members;
25
28
  static ID at_open;
26
29
  static ID at_protocol;
27
30
  static ID at_secret;
@@ -39,6 +42,7 @@ static ID id_on_drained;
39
42
  static ID id_on_message;
40
43
  static ID id_on_open;
41
44
  static ID id_port;
45
+ static ID id_publish;
42
46
 
43
47
  static rb_encoding *utf8_encoding;
44
48
  static rb_encoding *binary_encoding;
@@ -65,6 +69,10 @@ static VALUE SERVER_NAME;
65
69
  static VALUE SERVER_PORT;
66
70
  static VALUE SERVER_PROTOCOL;
67
71
 
72
+ // both used when worker of a cluster
73
+ uws_app_t *cluster_app;
74
+ struct us_listen_socket_t *cluster_socket;
75
+
68
76
  #define set_str_val(gl_name, str) \
69
77
  rb_gc_register_address(&gl_name); \
70
78
  (gl_name) = rb_enc_str_new((str), strlen((str)), binary_encoding); \
@@ -358,12 +366,23 @@ response_error:
358
366
  uws_res_end_without_body(USE_SSL, res, false);
359
367
  }
360
368
 
369
+ static void
370
+ up_server_cluster_listen_handler(struct us_listen_socket_t *listen_socket,
371
+ uws_app_listen_config_t config, void *arg) {
372
+ if (listen_socket) {
373
+ cluster_socket = listen_socket;
374
+ fprintf(stderr, "Internal Cluster communication on http://localhost:%d\n",
375
+ config.port);
376
+ }
377
+ }
378
+
361
379
  static void up_server_listen_handler(struct us_listen_socket_t *listen_socket,
362
380
  uws_app_listen_config_t config,
363
- void *user_data) {
364
- if (listen_socket)
365
- fprintf(stderr, "Server is running on http://%s:%d\n", config.host,
381
+ void *arg) {
382
+ if (listen_socket) {
383
+ fprintf(stderr, "Server is listening on http://%s:%d\n", config.host,
366
384
  config.port);
385
+ }
367
386
  }
368
387
 
369
388
  const rb_data_type_t up_client_t = {.wrap_struct_name = "Up::Client",
@@ -395,14 +414,14 @@ static VALUE up_client_pending(VALUE self) {
395
414
  return INT2FIX(0);
396
415
  }
397
416
 
398
- static void up_client_cluster_publish(server_s *s, int st, VALUE channel,
417
+ static void up_client_cluster_publish(char *scrt, int st, VALUE channel,
399
418
  VALUE message) {
400
419
  const char *opening_line = "POST " INTERNAL_PUBLISH_PATH " HTTP/1.1\r\n";
401
420
  const char *host_header = "Host: localhost\r\n";
402
421
  const char *secret = "Secret: ";
403
422
  char secret_header[50];
404
423
  memcpy(secret_header, secret, 8);
405
- memcpy(secret_header + 8, s->secret, 36);
424
+ memcpy(secret_header + 8, scrt, 36);
406
425
  memcpy(secret_header + 8 + 36, "\r\n", 2);
407
426
  const char *content_type = "Content-Type: text/plain\r\n";
408
427
  long c_length = RSTRING_LEN(channel) + RSTRING_LEN(message) + 2;
@@ -431,12 +450,25 @@ static void up_client_cluster_publish(server_s *s, int st, VALUE channel,
431
450
  // fprintf(stderr, "read: %s\n", read_buf);
432
451
  }
433
452
 
434
- static VALUE up_client_publish(int argc, VALUE *argv, VALUE self) {
453
+ static void up_internal_publish_to_member(server_s *s, VALUE channel,
454
+ VALUE message, int member_idx) {
455
+ struct sockaddr_in member_addr = {.sin_addr.s_addr = inet_addr("127.0.0.1"),
456
+ .sin_family = AF_INET};
457
+ int st = socket(AF_INET, SOCK_STREAM, 0);
458
+ if (st) {
459
+ member_addr.sin_port = htons(FIX2INT(s->port) + member_idx);
460
+ if (connect(st, (struct sockaddr *)&member_addr,
461
+ sizeof(struct sockaddr_in)) == 0) {
462
+ up_client_cluster_publish(s->secret, st, channel, message);
463
+ close(st);
464
+ }
465
+ }
466
+ }
467
+
468
+ static VALUE up_client_publish(VALUE self, VALUE channel, VALUE message) {
435
469
  uws_websocket_t *ws = DATA_PTR(self);
436
470
  if (!ws)
437
471
  return Qnil;
438
- VALUE channel, message, engine;
439
- rb_scan_args(argc, argv, "21", &channel, &message, &engine);
440
472
  if (TYPE(channel) != T_STRING)
441
473
  channel = rb_obj_as_string(channel);
442
474
  if (TYPE(message) != T_STRING)
@@ -451,20 +483,9 @@ static VALUE up_client_publish(int argc, VALUE *argv, VALUE self) {
451
483
 
452
484
  // publish to cluster members
453
485
  int i;
454
- struct sockaddr_in member_addr = {
455
- .sin_addr.s_addr = inet_addr("127.0.0.1"), .sin_family = AF_INET};
456
486
  for (i = 1; i <= s->workers; i++) {
457
- if (i != s->member_id) {
458
- int st = socket(AF_INET, SOCK_STREAM, 0);
459
- if (st) {
460
- member_addr.sin_port = htons(FIX2INT(s->port) + i);
461
- if (connect(st, (struct sockaddr *)&member_addr,
462
- sizeof(struct sockaddr_in)) == 0) {
463
- up_client_cluster_publish(s, st, channel, message);
464
- close(st);
465
- }
466
- }
467
- }
487
+ if (i != s->member_id)
488
+ up_internal_publish_to_member(s, channel, message, i);
468
489
  }
469
490
  }
470
491
  return res ? Qtrue : Qfalse;
@@ -721,9 +742,17 @@ upgrade_error:
721
742
  fprintf(stderr, "upgrade error");
722
743
  }
723
744
 
745
+ static void up_internal_close_sockets(int signal) {
746
+ if (cluster_socket)
747
+ us_listen_socket_close(false, cluster_socket);
748
+ if (cluster_app)
749
+ uws_app_close(USE_SSL, cluster_app);
750
+ }
751
+
724
752
  static VALUE up_server_listen(VALUE self) {
725
753
  server_s *s = DATA_PTR(self);
726
754
  up_internal_check_arg_types(s->rapp, &s->host, &s->port);
755
+ rb_ivar_set(mUp, at_instance, self);
727
756
 
728
757
  s->env_template = rb_hash_dup(rack_env_template);
729
758
  // When combined with SCRIPT_NAME and PATH_INFO, these variables can be used
@@ -747,8 +776,16 @@ static VALUE up_server_listen(VALUE self) {
747
776
  .port = FIX2INT(s->port), .host = RSTRING_PTR(s->host), .options = 0};
748
777
  VALUE rmember_id = rb_ivar_get(self, at_member_id);
749
778
  if (rmember_id != Qnil) {
779
+ // got a cluster
750
780
  s->member_id = FIX2INT(rmember_id);
751
- // got a cluster, open publish ports
781
+ // install signal handler
782
+ cluster_app = s->app;
783
+ cluster_socket = NULL;
784
+ struct sigaction upclcl = {.sa_handler = up_internal_close_sockets,
785
+ .sa_flags = 0};
786
+ sigemptyset(&upclcl.sa_mask);
787
+ sigaction(SIGINT, &upclcl, NULL);
788
+ // open publish ports
752
789
  VALUE rworkers = rb_ivar_get(self, at_workers);
753
790
  s->workers = FIX2INT(rworkers);
754
791
  VALUE rsecret = rb_ivar_get(self, at_secret);
@@ -761,7 +798,14 @@ static VALUE up_server_listen(VALUE self) {
761
798
  uws_app_listen_config_t config_internal = {
762
799
  .port = config.port + s->member_id, .host = "localhost", .options = 0};
763
800
  uws_app_listen_with_config(false, s->app, config_internal,
764
- up_server_listen_handler, NULL);
801
+ up_server_cluster_listen_handler, NULL);
802
+ } else {
803
+ cluster_app = s->app;
804
+ cluster_socket = NULL;
805
+ struct sigaction upclcl = {.sa_handler = up_internal_close_sockets,
806
+ .sa_flags = 0};
807
+ sigemptyset(&upclcl.sa_mask);
808
+ sigaction(SIGINT, &upclcl, NULL);
765
809
  }
766
810
  uws_app_any(USE_SSL, s->app, "/*", up_server_request_handler, (void *)s);
767
811
  uws_ws(USE_SSL, s->app, "/*",
@@ -782,6 +826,33 @@ static VALUE up_server_listen(VALUE self) {
782
826
  return self;
783
827
  }
784
828
 
829
+ static VALUE up_server_publish(VALUE self, VALUE channel, VALUE message) {
830
+ if (TYPE(channel) != T_STRING)
831
+ channel = rb_obj_as_string(channel);
832
+ if (TYPE(message) != T_STRING)
833
+ message = rb_obj_as_string(message);
834
+ server_s *s = DATA_PTR(self);
835
+ VALUE members = rb_ivar_get(self, at_members);
836
+ if (members != Qnil) {
837
+ long i, mb_cnt = RARRAY_LEN(members);
838
+ for (i = 0; i < mb_cnt; i++) {
839
+ up_internal_publish_to_member(s, channel, message, i);
840
+ }
841
+ } else {
842
+ uws_publish(USE_SSL, s->app, RSTRING_PTR(channel), RSTRING_LEN(channel),
843
+ RSTRING_PTR(message), RSTRING_LEN(message), TEXT, false);
844
+ if (s->member_id > 0) {
845
+ // publish to cluster members
846
+ int i;
847
+ for (i = 1; i <= s->workers; i++) {
848
+ if (i != s->member_id)
849
+ up_internal_publish_to_member(s, channel, message, i);
850
+ }
851
+ }
852
+ }
853
+ return Qtrue;
854
+ }
855
+
785
856
  static VALUE up_server_stop(VALUE self) {
786
857
  server_s *s = DATA_PTR(self);
787
858
  if (!s->app)
@@ -853,10 +924,19 @@ void up_setup_rack_env_template(void) {
853
924
  rb_hash_aset(rack_env_template, HTTP_VERSION, http11);
854
925
  }
855
926
 
927
+ static VALUE up_publish(VALUE self, VALUE channel, VALUE message) {
928
+ VALUE instance = rb_ivar_get(mUp, at_instance);
929
+ if (instance != Qnil)
930
+ return rb_funcall(instance, id_publish, 2, channel, message);
931
+ return Qfalse;
932
+ }
933
+
856
934
  void Init_up_ext(void) {
857
935
  at_env = rb_intern("@env");
858
936
  at_handler = rb_intern("@handler");
937
+ at_instance = rb_intern("@instance");
859
938
  at_member_id = rb_intern("@member_id");
939
+ at_members = rb_intern("@members");
860
940
  at_open = rb_intern("@open");
861
941
  at_protocol = rb_intern("@protocol");
862
942
  at_secret = rb_intern("@secret");
@@ -874,6 +954,7 @@ void Init_up_ext(void) {
874
954
  id_on_message = rb_intern("on_message");
875
955
  id_on_open = rb_intern("on_open");
876
956
  id_port = rb_intern("port");
957
+ id_publish = rb_intern("publish");
877
958
 
878
959
  utf8_encoding = rb_enc_find("UTF-8");
879
960
  binary_encoding = rb_enc_find("binary");
@@ -911,11 +992,13 @@ void Init_up_ext(void) {
911
992
  up_setup_rack_env_template();
912
993
 
913
994
  mUp = rb_define_module("Up");
995
+ rb_define_singleton_method(mUp, "publish", up_publish, 2);
996
+
914
997
  cClient = rb_define_class_under(mUp, "Client", rb_cObject);
915
998
  rb_define_alloc_func(cClient, up_client_alloc);
916
999
  rb_define_method(cClient, "close", up_client_close, 0);
917
1000
  rb_define_method(cClient, "pending", up_client_pending, 0);
918
- rb_define_method(cClient, "publish", up_client_publish, -1);
1001
+ rb_define_method(cClient, "publish", up_client_publish, 2);
919
1002
  rb_define_method(cClient, "subscribe", up_client_subscribe, -1);
920
1003
  rb_define_method(cClient, "unsubscribe", up_client_unsubscribe, -1);
921
1004
  rb_define_method(cClient, "write", up_client_write, 1);
@@ -926,5 +1009,6 @@ void Init_up_ext(void) {
926
1009
  rb_define_alloc_func(cServer, up_server_alloc);
927
1010
  rb_define_method(cServer, "initialize", up_server_init, -1);
928
1011
  rb_define_method(cServer, "listen", up_server_listen, 0);
1012
+ rb_define_method(cServer, "publish", up_server_publish, 2);
929
1013
  rb_define_method(cServer, "stop", up_server_stop, 0);
930
1014
  }
data/lib/up/bun/server.rb CHANGED
@@ -4,6 +4,13 @@ require 'up/cli'
4
4
  require 'up/client'
5
5
 
6
6
  module Up
7
+ class << self
8
+ def publish(channel, message)
9
+ raise 'no instance running' unless @instance
10
+ @instance&.publish(channel, message)
11
+ end
12
+ end
13
+
7
14
  module Bun
8
15
  class Server
9
16
  def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, logger: Logger.new(STDERR))
@@ -49,6 +56,7 @@ module Up
49
56
  }
50
57
  def listen
51
58
  raise "already running" if @server
59
+ ::Up.instance_variable_set(:@instance, self)
52
60
  %x{
53
61
  const oubs = Opal.Up.Bun.Server;
54
62
  const ouwc = Opal.Up.Client;
@@ -152,6 +160,15 @@ module Up
152
160
  }
153
161
  end
154
162
 
163
+ def publish(channel, message)
164
+ %x{
165
+ if (!message.$$is_string) {
166
+ message = JSON.stringify(message);
167
+ }
168
+ #@server.publish(channel, message);
169
+ }
170
+ end
171
+
155
172
  def stop
156
173
  if Up::CLI::stoppable?
157
174
  `#@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,24 +15,48 @@ 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
21
22
  super
22
23
  end
23
24
  end
24
-
25
- Process.waitall
26
- @members.each do |member|
27
- Process.kill("KILL", member)
25
+ unless @member_id
26
+ install_signal_handlers
27
+ Process.waitall
28
28
  end
29
29
  end
30
30
 
31
31
  def stop
32
32
  if Up::CLI::stoppable?
33
- @members.each { |m| Process.kill(m) }
34
- @members.clear
33
+ kill_members
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def install_signal_handlers
40
+ Signal.trap('CHLD') do
41
+ warn "\nError: a cluster member died!"
42
+ kill_members
43
+ end
44
+ Signal.trap('INT') do
45
+ warn "\nReceived CTRL-C!"
46
+ kill_members
47
+ end
48
+ end
49
+
50
+ def kill_members
51
+ Signal.trap('CHLD', 'IGNORE')
52
+ STDERR.print "Stopping workers: "
53
+ @members.each do |mid|
54
+ Process.kill('INT', mid) rescue nil
55
+ STDERR.print '.'
35
56
  end
57
+ @members.clear
58
+ warn "\nCluster stopped."
59
+ Signal.trap('CHLD', 'DEFAULT')
36
60
  end
37
61
  end
38
62
  end
@@ -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()` }
@@ -10,6 +10,13 @@ require 'up/client'
10
10
  }
11
11
 
12
12
  module Up
13
+ class << self
14
+ def publish(channel, message)
15
+ raise 'no instance running' unless @instance
16
+ @instance&.publish(channel, message)
17
+ end
18
+ end
19
+
13
20
  module UWebSocket
14
21
  class Server
15
22
  def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, logger: Logger.new(STDERR))
@@ -74,6 +81,7 @@ module Up
74
81
 
75
82
  def listen
76
83
  raise "already running" if @server
84
+ ::Up.instance_variable_set(:@instance, self)
77
85
  %x{
78
86
  const ouws = Opal.Up.UWebSocket.Server;
79
87
  const ouwc = Opal.Up.Client;
@@ -138,9 +146,9 @@ module Up
138
146
  client.open = false;
139
147
  client.handler = handler
140
148
  client.protocol = #{:websocket};
141
- client.server = self.server;
149
+ client.server = #@server;
142
150
  client.timeout = 120;
143
- if (self.worker) {
151
+ if (#@worker) {
144
152
  client.worker = true;
145
153
  }
146
154
  res.upgrade({ client: client },
@@ -164,6 +172,15 @@ module Up
164
172
  }
165
173
  end
166
174
 
175
+ def publish(channel, message)
176
+ %x{
177
+ if (!message.$$is_string) {
178
+ message = JSON.stringify(message);
179
+ }
180
+ #@server.publish(channel, message);
181
+ }
182
+ end
183
+
167
184
  def stop
168
185
  if Up::CLI::stoppable?
169
186
  `#@server.close()`
data/lib/up/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Up
2
- VERSION = '0.0.4'.freeze
2
+ VERSION = '0.0.6'.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.4
4
+ version: 0.0.6
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-12 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: []