anycable-rack-server 0.2.1 → 0.5.0

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: 0515017e6e7f746e09105d2feb3fdbbcad873c406ad0e4c3d11ae109eb952b26
4
- data.tar.gz: fda34861fae7508fc64a1321d2346bd1ed47f5cce1d71044b4ef3b2d2d36e06b
3
+ metadata.gz: 1e0fb06453d826cf3c314edb0dbb6ab1f1ab8774760b40985b105aaf87ca1316
4
+ data.tar.gz: a6fc8f21bf0944cf1cacb58b779b222782e485af7fe8dcec1bf719b31e05d38f
5
5
  SHA512:
6
- metadata.gz: 8e82a0ddf031e13515d9d8df0a645f8aea5ab1c59241ffd3b9202906cca1c6c4b05393a2e51685e28da1e27a702d41f26a14fdb8d32bcf5ec42e9773b404348d
7
- data.tar.gz: 1e0d334e324695a6b583be61da56bc28a40dd030aef7f8c35941085c0dea7cbb963fdd2da115adafac6d2459b3dc4951909c417433371577343d28c609928fb5
6
+ metadata.gz: cba311e11d372c3c6b42f530350bd81729102814a24209c63ae37460fc810b638db350afc82b930b5f8623acdc9423182e70489e220247f82c7c89f09efb8b74
7
+ data.tar.gz: a8541982f21133340285977c16852f3bbc41c18f4ce431e5b0261f4d3509f8bdf7f9d3816f500c8401eeac3de7223da39b8f5e008fd9c28e131322d4ab8588ad
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2019-2020 Yulia Oletskaya, Vladimir Dementyev
3
+ Copyright (c) 2019-2022 Yulia Oletskaya, Vladimir Dementyev
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -74,7 +74,8 @@ config.any_cable_rack.rpc_port = 50051
74
74
 
75
75
  ## Broadcast adapters
76
76
 
77
- AnyCable Rack supports Redis (default) and HTTP broadcast adapters (see [the documentation](https://docs.anycable.io/#/ruby/broadcast_adapters)).
77
+ AnyCable Rack supports Redis (default) and HTTP broadcast adapters
78
+ (see [the documentation](https://docs.anycable.io/ruby/broadcast_adapters)).
78
79
 
79
80
  Broadcast adapter is inherited from AnyCable configuration (so, you don't need to configure it twice).
80
81
 
@@ -110,24 +111,6 @@ config.any_cable_rack.http_broadcast_path = "/_my_broadcast"
110
111
 
111
112
  **NOTE:** Don't forget to configure `http_broadcast_url` for AnyCable pointing to your web server and the specified broadcast path.
112
113
 
113
- ## Running RPC from the same process
114
-
115
- The goal of the Rack server is to simplify the development/testing process. But we still have to run the RPC server.
116
-
117
- This gem also provides a way to run RPC server within the same process.
118
- All you need to do is set `run_rpc = true` in the configuration:
119
-
120
- ```ruby
121
- # in Rack app
122
- AnyCable::Rack.config.run_rpc = true
123
-
124
- # and only after that
125
- ws_server.start!
126
-
127
- # in Rails
128
- config.any_cable_rack.run_rpc = true
129
- ```
130
-
131
114
  ## Testing
132
115
 
133
116
  Run units with `bundle exec rake`.
@@ -32,7 +32,7 @@ module AnyCable
32
32
  if data["stream"]
33
33
  hub.broadcast(data["stream"], data["data"], coder)
34
34
  elsif data["command"] == "disconnect"
35
- hub.disconnect(data["payload"]["identifier"], data["payload"]["reconnect"])
35
+ hub.disconnect(data["payload"]["identifier"], data["payload"]["reconnect"], coder)
36
36
  end
37
37
  end
38
38
  end
@@ -5,7 +5,7 @@ require "json"
5
5
  module AnyCable
6
6
  module Rack
7
7
  module Coders
8
- module JSON # :nodoc:
8
+ module Json # :nodoc:
9
9
  class << self
10
10
  def decode(json_str)
11
11
  ::JSON.parse(json_str)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem "msgpack", "~> 1.4"
4
+ require "msgpack"
5
+
6
+ module AnyCable
7
+ module Rack
8
+ module Coders
9
+ module Msgpack # :nodoc:
10
+ class << self
11
+ def decode(bin)
12
+ MessagePack.unpack(bin)
13
+ end
14
+
15
+ def encode(ruby_obj, binary_frame_wrap: true)
16
+ message_packed = MessagePack.pack(ruby_obj)
17
+
18
+ return message_packed unless binary_frame_wrap
19
+
20
+ BinaryFrame.new(message_packed)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "google/protobuf"
4
+
5
+ Google::Protobuf::DescriptorPool.generated_pool.build do
6
+ add_message "action_cable.Message" do
7
+ optional :type, :enum, 1, "action_cable.Type"
8
+ optional :command, :enum, 2, "action_cable.Command"
9
+ optional :identifier, :string, 3
10
+ optional :data, :string, 4
11
+ optional :message, :bytes, 5
12
+ optional :reason, :string, 6
13
+ optional :reconnect, :bool, 7
14
+ end
15
+ add_enum "action_cable.Type" do
16
+ value :no_type, 0
17
+ value :welcome, 1
18
+ value :disconnect, 2
19
+ value :ping, 3
20
+ value :confirm_subscription, 4
21
+ value :reject_subscription, 5
22
+ end
23
+ add_enum "action_cable.Command" do
24
+ value :unknown_command, 0
25
+ value :subscribe, 1
26
+ value :unsubscribe, 2
27
+ value :message, 3
28
+ end
29
+ end
30
+
31
+ module ActionCable
32
+ Message = Google::Protobuf::DescriptorPool.generated_pool.lookup("action_cable.Message").msgclass
33
+ Type = Google::Protobuf::DescriptorPool.generated_pool.lookup("action_cable.Type").enummodule
34
+ Command = Google::Protobuf::DescriptorPool.generated_pool.lookup("action_cable.Command").enummodule
35
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem "google-protobuf", "~> 3.19", ">= 3.19.1"
4
+ require_relative "./proto/message_pb"
5
+ require_relative "./msgpack"
6
+
7
+ module AnyCable
8
+ module Rack
9
+ module Coders
10
+ module Protobuf # :nodoc:
11
+ class << self
12
+ def decode(bin)
13
+ decoded_message = ActionCable::Message.decode(bin).to_h
14
+
15
+ decoded_message[:command] = decoded_message[:command].to_s
16
+ if decoded_message[:message].present?
17
+ decoded_message[:message] = Msgpack.decode(decoded_message[:message])
18
+ end
19
+
20
+ decoded_message.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
21
+ end
22
+
23
+ def encode(ruby_obj)
24
+ message = ruby_obj.delete(:message)
25
+
26
+ data = ActionCable::Message.new(ruby_obj)
27
+
28
+ if message
29
+ data.message = Msgpack.encode(message, binary_frame_wrap: false)
30
+ end
31
+
32
+ BinaryFrame.new(ActionCable::Message.encode(data))
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -12,11 +12,11 @@ module AnyCable
12
12
 
13
13
  attr_config mount_path: "/cable",
14
14
  headers: DEFAULT_HEADERS,
15
+ coder: :json,
15
16
  rpc_addr: "localhost:50051",
16
17
  rpc_client_pool_size: 5,
17
18
  rpc_client_timeout: 5,
18
- http_broadcast_path: "/_anycable_rack_broadcast",
19
- run_rpc: false
19
+ http_broadcast_path: "/_anycable_rack_broadcast"
20
20
  end
21
21
  end
22
22
  end
@@ -67,7 +67,8 @@ module AnyCable
67
67
  private
68
68
 
69
69
  def transmit(cable_message)
70
- socket.transmit(encode(cable_message))
70
+ encoded = encode(cable_message)
71
+ socket.transmit(encoded)
71
72
  end
72
73
 
73
74
  def close
@@ -133,7 +134,7 @@ module AnyCable
133
134
  end
134
135
 
135
136
  def process_command(response, identifier)
136
- response.transmissions.each { |transmission| transmit(decode(transmission)) }
137
+ response.transmissions.each { |transmission| transmit(decode_transmission(transmission)) }
137
138
  hub.remove_channel(socket, identifier) if response.stop_streams
138
139
  response.streams.each { |stream| hub.add_subscriber(stream, socket, identifier) }
139
140
  response.stopped_streams.each { |stream| hub.remove_subscriber(stream, socket, identifier) }
@@ -145,7 +146,7 @@ module AnyCable
145
146
  end
146
147
 
147
148
  def process_open(response)
148
- response.transmissions&.each { |transmission| transmit(decode(transmission)) }
149
+ response.transmissions&.each { |transmission| transmit(decode_transmission(transmission)) }
149
150
  if response.status == :SUCCESS
150
151
  @_identifiers = response.identifiers
151
152
  @_cstate = response.env.cstate&.to_h || {}
@@ -181,6 +182,10 @@ module AnyCable
181
182
  coder.encode(cable_message)
182
183
  end
183
184
 
185
+ def decode_transmission(json_message)
186
+ JSON.parse(json_message)
187
+ end
188
+
184
189
  def decode(websocket_message)
185
190
  coder.decode(websocket_message)
186
191
  end
@@ -71,7 +71,7 @@ module AnyCable
71
71
  end
72
72
 
73
73
  list.each do |(channel_id, sockets)|
74
- decoded = coder.decode(message)
74
+ decoded = JSON.parse(message)
75
75
  cmessage = channel_message(channel_id, decoded, coder)
76
76
  sockets.each { |socket| socket.transmit(cmessage) }
77
77
  end
@@ -81,14 +81,14 @@ module AnyCable
81
81
  sockets.each_key { |socket| socket.transmit(message) }
82
82
  end
83
83
 
84
- def disconnect(identifier, reconnect)
84
+ def disconnect(identifier, reconnect, coder)
85
85
  sockets = @sync.synchronize do
86
86
  return unless @streams[INTERNAL_STREAM].key?(identifier)
87
87
 
88
88
  @streams[INTERNAL_STREAM][identifier].to_a
89
89
  end
90
90
 
91
- msg = disconnect_message("remote", reconnect)
91
+ msg = disconnect_message("remote", reconnect, coder)
92
92
 
93
93
  sockets.each do |socket|
94
94
  socket.transmit(msg)
@@ -115,9 +115,8 @@ module AnyCable
115
115
  coder.encode(identifier: channel_id, message: message)
116
116
  end
117
117
 
118
- # FIXME: coder support?
119
- def disconnect_message(reason, reconnect)
120
- {type: :disconnect, reason: reason, reconnect: reconnect}.to_json
118
+ def disconnect_message(reason, reconnect, coder)
119
+ coder.encode({type: :disconnect, reason: reason, reconnect: reconnect})
121
120
  end
122
121
  end
123
122
  end
@@ -9,7 +9,8 @@ require "anycable/rack/socket"
9
9
  module AnyCable
10
10
  module Rack
11
11
  class Middleware # :nodoc:
12
- PROTOCOLS = ["actioncable-v1-json", "actioncable-unsupported"].freeze
12
+ PROTOCOLS = %w[actioncable-v1-json actioncable-v1-msgpack actioncable-unsupported actioncable-v1-protobuf].freeze
13
+
13
14
  attr_reader :pinger,
14
15
  :hub,
15
16
  :coder,
@@ -8,7 +8,10 @@ module AnyCable
8
8
  class Pinger
9
9
  INTERVAL = 3
10
10
 
11
- def initialize
11
+ attr_reader :coder
12
+
13
+ def initialize(coder)
14
+ @coder = coder
12
15
  @_sockets = []
13
16
  @_stopped = false
14
17
  end
@@ -45,7 +48,7 @@ module AnyCable
45
48
  private
46
49
 
47
50
  def ping_message(time)
48
- {type: :ping, message: time}.to_json
51
+ coder.encode({type: :ping, message: time})
49
52
  end
50
53
  end
51
54
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "connection_pool"
4
- require "grpc"
4
+ require "anycable/grpc"
5
5
 
6
6
  module AnyCable
7
7
  module Rack
@@ -12,7 +12,7 @@ module AnyCable
12
12
 
13
13
  def initialize(host:, size:, timeout:)
14
14
  @pool = ConnectionPool.new(size: size, timeout: timeout) do
15
- AnyCable::RPC::Service.rpc_stub_class.new(host, :this_channel_is_insecure)
15
+ AnyCable::GRPC::Service.rpc_stub_class.new(host, :this_channel_is_insecure)
16
16
  end
17
17
  @metadata = {metadata: {"protov" => "v1"}}.freeze
18
18
  end
@@ -8,7 +8,6 @@ require "anycable/rack/errors"
8
8
  require "anycable/rack/middleware"
9
9
  require "anycable/rack/logging"
10
10
  require "anycable/rack/broadcast_subscribers/base_subscriber"
11
- require "anycable/rack/coders/json"
12
11
 
13
12
  module AnyCable # :nodoc: all
14
13
  module Rack
@@ -28,9 +27,8 @@ module AnyCable # :nodoc: all
28
27
  def initialize(config: AnyCable::Rack.config)
29
28
  @config = config
30
29
  @hub = Hub.new
31
- @pinger = Pinger.new
32
- # TODO: Support other coders
33
- @coder = Coders::JSON
30
+ @coder = resolve_coder(config.coder)
31
+ @pinger = Pinger.new(coder)
34
32
 
35
33
  @broadcast = resolve_broadcast_adapter
36
34
  @rpc_client = RPC::Client.new(
@@ -58,8 +56,6 @@ module AnyCable # :nodoc: all
58
56
 
59
57
  broadcast.start
60
58
 
61
- Rack.rpc_server.run if config.run_rpc
62
-
63
59
  @_started = true
64
60
  end
65
61
 
@@ -114,6 +110,11 @@ module AnyCable # :nodoc: all
114
110
  raise ArgumentError, "Unsupported broadcast adatper: #{adapter}. AnyCable Rack server only supports: redis, http"
115
111
  end
116
112
  end
113
+
114
+ def resolve_coder(name)
115
+ require "anycable/rack/coders/#{name}"
116
+ AnyCable::Rack::Coders.const_get(name.capitalize)
117
+ end
117
118
  end
118
119
  end
119
120
  end
@@ -4,6 +4,17 @@ require "anycable/rack/logging"
4
4
 
5
5
  module AnyCable
6
6
  module Rack
7
+ # Wrapper for outgoing data used to correctly set the WS frame type
8
+ class BinaryFrame
9
+ def initialize(data)
10
+ @data = data
11
+ end
12
+
13
+ def to_s
14
+ @data.to_s
15
+ end
16
+ end
17
+
7
18
  # Socket wrapper
8
19
  class Socket
9
20
  include Logging
@@ -22,7 +33,9 @@ module AnyCable
22
33
  @_active = true
23
34
  end
24
35
 
25
- def transmit(data, type: :text)
36
+ def transmit(data, type: nil)
37
+ # p "DATA: #{data.class} — #{data.to_s}"
38
+ type ||= data.is_a?(BinaryFrame) ? :binary : :text
26
39
  frame = WebSocket::Frame::Outgoing::Server.new(
27
40
  version: version,
28
41
  data: data,
@@ -144,7 +157,7 @@ module AnyCable
144
157
  end
145
158
  end
146
159
  rescue Exception => e # rubocop:disable Lint/RescueException
147
- log(:error, "Socket frame error: #{e}")
160
+ log(:error, "Socket frame error: #{e}\n #{e.backtrace.take(4).join("\n")}")
148
161
  nil # client disconnected or timed out
149
162
  end
150
163
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AnyCable
4
4
  module Rack
5
- VERSION = "0.2.1"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
@@ -10,13 +10,6 @@ module AnyCable
10
10
  def config
11
11
  @config ||= Config.new
12
12
  end
13
-
14
- def rpc_server
15
- return @rpc_server if instance_variable_defined?(:@rpc_server)
16
-
17
- require "anycable/cli"
18
- @rpc_server = AnyCable::CLI.new(embedded: true)
19
- end
20
13
  end
21
14
  end
22
15
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anycable-rack-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yulia Oletskaya
8
8
  - Vladimir Dementyev
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-09-08 00:00:00.000000000 Z
12
+ date: 2022-04-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: anyway_config
@@ -17,28 +17,34 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: 1.4.2
20
+ version: 2.1.0
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: 1.4.2
27
+ version: 2.1.0
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: anycable
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ">="
32
+ - - ">"
33
+ - !ruby/object:Gem::Version
34
+ version: 1.0.99
35
+ - - "<"
33
36
  - !ruby/object:Gem::Version
34
- version: 1.0.0
37
+ version: '2.0'
35
38
  type: :runtime
36
39
  prerelease: false
37
40
  version_requirements: !ruby/object:Gem::Requirement
38
41
  requirements:
39
- - - ">="
42
+ - - ">"
43
+ - !ruby/object:Gem::Version
44
+ version: 1.0.99
45
+ - - "<"
40
46
  - !ruby/object:Gem::Version
41
- version: 1.0.0
47
+ version: '2.0'
42
48
  - !ruby/object:Gem::Dependency
43
49
  name: connection_pool
44
50
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +170,9 @@ files:
164
170
  - lib/anycable/rack/broadcast_subscribers/http_subscriber.rb
165
171
  - lib/anycable/rack/broadcast_subscribers/redis_subscriber.rb
166
172
  - lib/anycable/rack/coders/json.rb
173
+ - lib/anycable/rack/coders/msgpack.rb
174
+ - lib/anycable/rack/coders/proto/message_pb.rb
175
+ - lib/anycable/rack/coders/protobuf.rb
167
176
  - lib/anycable/rack/config.rb
168
177
  - lib/anycable/rack/connection.rb
169
178
  - lib/anycable/rack/errors.rb
@@ -177,11 +186,11 @@ files:
177
186
  - lib/anycable/rack/server.rb
178
187
  - lib/anycable/rack/socket.rb
179
188
  - lib/anycable/rack/version.rb
180
- homepage:
189
+ homepage:
181
190
  licenses:
182
191
  - MIT
183
192
  metadata: {}
184
- post_install_message:
193
+ post_install_message:
185
194
  rdoc_options: []
186
195
  require_paths:
187
196
  - lib
@@ -196,8 +205,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
196
205
  - !ruby/object:Gem::Version
197
206
  version: '0'
198
207
  requirements: []
199
- rubygems_version: 3.0.6
200
- signing_key:
208
+ rubygems_version: 3.3.7
209
+ signing_key:
201
210
  specification_version: 4
202
211
  summary: AnyCable Rack Server
203
212
  test_files: []