anycable 0.6.5 → 1.0.0.preview1

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: cb889c8917f8196bd4323767a9d73774a47d9d73d5b76ce98b601082df456d88
4
- data.tar.gz: 64946890d603cb9395863d93bb146d7a15011062fab46e2ee5a1d6e929d93880
3
+ metadata.gz: 65d07fe802d850f89351ed8c054d2cba9c393b36f7a5aa318183362f09d53e58
4
+ data.tar.gz: b4167ba77ce930ddcb77a9e38d205215fb3ec085a93bfd3876f2bf8a5e669373
5
5
  SHA512:
6
- metadata.gz: bffef6466c0368d6950a2a46d5b80840c20954c097cbb05cbfab2a31d578d5ded652d23d612fd47806d1e0720d6e0c4f1fd986c5051c21f35bf39e3660f3d32e
7
- data.tar.gz: f738b1c94a96aada66bcfd40e51534c7d3917e209f05f3c5f01bd80afa1300ee51973e8b2bf1524a3260481549fc2462458f8d68e97d0eb34b30599dde626bf7
6
+ metadata.gz: 456af4dd28de6d32a8436ce8702ddb3ba5523b25b531bce8dac00c5bf6cd181e79c57e4d4a89d1a2c1ff3e21eda1108be3f023c65edcf1bbf5790594216ae971
7
+ data.tar.gz: b25fa2a291a6cd6bbf4734c9c59dcf991dead19c0e25c004ac696ec524e6de63bb5ea4251f8d06ce1d21699a44e178a7e445bfedcd4d70179535c6648ed67854
@@ -1,14 +1,30 @@
1
1
  # Change log
2
2
 
3
- ## master
3
+ ## 🚧 1.0.0 (_coming soon_)
4
4
 
5
- ## 0.6.5 (2020-04-01)
5
+ - **RPC schema has changed**. ([@palkan][])
6
6
 
7
- - Relax `anyway_config` dependency. ([@palkan][])
7
+ Using `anycable-go` v1.x is required.
8
+
9
+ - **Ruby 2.5+ is required**. ([@palkan][])
10
+
11
+ - Added RPC proto version check. ([@palkan][])
12
+
13
+ Server must sent `protov` metadata with the supported versions (comma-separated list). If there is no matching version an exception is raised.
14
+
15
+ Current RPC proto version is **v1**.
16
+
17
+ - Added `request` support to channels. ([@palkan][])
18
+
19
+ Now you can access `request` object in channels, too (e.g., to read headers/cookies/URL/etc).
20
+
21
+ - Change default server address from `[::]:50051` to `127.0.0.1:50051`. ([@palkan][])
22
+
23
+ See [#71](https://github.com/anycable/anycable/pull/71).
8
24
 
9
25
  ## 0.6.4 (2020-01-24)
10
26
 
11
- - Fix Ruby 2.7 warnings. ([@palkan][])
27
+ - Fix Ruby 2.7 warnings. ([@palkan])
12
28
 
13
29
  – Add `REMOTE_ADDR` socket env variable using a synthetic header passed from a websocket
14
30
  server. ([@sponomarev][])
@@ -84,7 +100,7 @@ AnyCable allows you to use custom broadcasting adapters (Redis is used by defaul
84
100
  ```ruby
85
101
  # Specify by name (tries to load `AnyCable::BroadcastAdapters::MyAdapter` from
86
102
  # "anycable/broadcast_adapters/my_adapter")
87
- AnyCable.broadcast_adapter = :my_adapter, { option: "value" }
103
+ AnyCable.broadcast_adapter = :my_adapter, {option: "value"}
88
104
  # or provide an instance (should respond_to #broadcast)
89
105
  AnyCable.broadcast_adapter = MyAdapter.new
90
106
  ```
data/README.md CHANGED
@@ -16,7 +16,7 @@ AnyCable uses the same protocol as ActionCable, so you can use its [JavaScript c
16
16
 
17
17
  ## Requirements
18
18
 
19
- - Ruby >= 2.4
19
+ - Ruby >= 2.5
20
20
  - Redis (for broadcasting, [discuss other options](https://github.com/anycable/anycable/issues/2) with us!)
21
21
 
22
22
  ## Usage
@@ -52,27 +52,27 @@ Check out our 📑 [Documentation](https://docs.anycable.io).
52
52
 
53
53
  - Install required GRPC gems:
54
54
 
55
- ```
55
+ ```sh
56
56
  gem install grpc
57
57
  gem install grpc-tools
58
58
  ```
59
59
 
60
60
  - Re-generate GRPC files (if necessary):
61
61
 
62
- ```
62
+ ```sh
63
63
  make
64
64
  ```
65
65
 
66
66
  ## Contributing
67
67
 
68
- Bug reports and pull requests are welcome on GitHub at https://github.com/anycable/anycable.
68
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/anycable/anycable](https://github.com/anycable/anycable).
69
69
 
70
70
  Please, provide reproduction script (using [this template](https://github.com/anycable/anycable/blob/master/etc/bug_report_template.rb)) when submitting bugs if possible.
71
71
 
72
72
  ## License
73
+
73
74
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
74
75
 
75
76
  ## Security Contact
76
77
 
77
78
  To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
78
-
@@ -56,14 +56,6 @@ module AnyCable
56
56
  ExceptionsHandling << block
57
57
  end
58
58
 
59
- def error_handlers
60
- warn <<~DEPRECATION
61
- Using `AnyCable.error_handlers` is deprecated!
62
- Please, use `AnyCable.capture_exception` instead.
63
- DEPRECATION
64
- ExceptionsHandling
65
- end
66
-
67
59
  # Register a callback to be invoked before
68
60
  # the server starts
69
61
  def configure_server(&block)
@@ -103,8 +95,7 @@ module AnyCable
103
95
  attr_writer :middleware
104
96
  end
105
97
 
98
+ require "anycable/middlewares/check_version"
99
+
106
100
  self.middleware = MiddlewareChain.new
107
101
  end
108
-
109
- # Backward compatibility
110
- Anycable = AnyCable
@@ -17,11 +17,11 @@ module AnyCable
17
17
  # We couldn't require the adapter itself.
18
18
  if e.path == path_to_adapter
19
19
  raise e.class, "Couldn't load the '#{adapter}' broadcast adapter for AnyCable",
20
- e.backtrace
20
+ e.backtrace
21
21
  # Bubbled up from the adapter require.
22
22
  else
23
23
  raise e.class, "Error loading the '#{adapter}' broadcast adapter for AnyCable",
24
- e.backtrace
24
+ e.backtrace
25
25
  end
26
26
  end
27
27
  end
@@ -34,7 +34,7 @@ module AnyCable
34
34
  def broadcast(stream, payload)
35
35
  redis_conn.publish(
36
36
  channel,
37
- { stream: stream, data: payload }.to_json
37
+ {stream: stream, data: payload}.to_json
38
38
  )
39
39
  end
40
40
  end
@@ -36,11 +36,11 @@ module AnyCable
36
36
 
37
37
  configure_server!
38
38
 
39
- logger.info "Starting AnyCable gRPC server (pid: #{Process.pid})"
39
+ logger.info "Starting AnyCable gRPC server (pid: #{Process.pid}, workers_num: #{config.rpc_pool_size})"
40
40
 
41
41
  print_versions!
42
42
 
43
- logger.info "Serving #{defined?(::Rails) ? 'Rails ' : ''}application from #{boot_file}"
43
+ logger.info "Serving #{defined?(::Rails) ? "Rails " : ""}application from #{boot_file}"
44
44
 
45
45
  verify_connection_factory!
46
46
 
@@ -48,6 +48,8 @@ module AnyCable
48
48
 
49
49
  log_errors!
50
50
 
51
+ use_version_check! if config.version_check_enabled?
52
+
51
53
  @server = AnyCable::Server.new(
52
54
  host: config.rpc_host,
53
55
  **config.to_grpc_params,
@@ -124,7 +126,7 @@ module AnyCable
124
126
  end
125
127
 
126
128
  def print_versions!
127
- logger.info "AnyCable version: #{AnyCable::VERSION}"
129
+ logger.info "AnyCable version: #{AnyCable::VERSION} (proto_version: #{AnyCable::PROTO_VERSION})"
128
130
  logger.info "gRPC version: #{GRPC::VERSION}"
129
131
  end
130
132
 
@@ -162,6 +164,14 @@ module AnyCable
162
164
  AnyCable.server_callbacks.each(&:call)
163
165
  end
164
166
 
167
+ def use_version_check!
168
+ require "anycable/middlewares/check_version"
169
+
170
+ AnyCable.middleware.use(
171
+ AnyCable::Middlewares::CheckVersion.new(AnyCable::PROTO_VERSION)
172
+ )
173
+ end
174
+
165
175
  def start_health_server!
166
176
  @health_server = AnyCable::HealthServer.new(
167
177
  server,
@@ -8,11 +8,9 @@ module AnyCable
8
8
  class Config < Anyway::Config
9
9
  config_name :anycable
10
10
 
11
- DefaultHostWrapper = Class.new(String)
12
-
13
11
  attr_config(
14
12
  ### gRPC options
15
- rpc_host: DefaultHostWrapper.new("[::]:50051"),
13
+ rpc_host: "127.0.0.1:50051",
16
14
  # For defaults see https://github.com/grpc/grpc/blob/51f0d35509bcdaba572d422c4f856208162022de/src/ruby/lib/grpc/generic/rpc_server.rb#L186-L216
17
15
  rpc_pool_size: GRPC::RpcServer::DEFAULT_POOL_SIZE,
18
16
  rpc_max_waiting_requests: GRPC::RpcServer::DEFAULT_MAX_WAITING_REQUESTS,
@@ -34,9 +32,14 @@ module AnyCable
34
32
 
35
33
  ### Health check options
36
34
  http_health_port: nil,
37
- http_health_path: "/health"
35
+ http_health_path: "/health",
36
+
37
+ ### Misc options
38
+ version_check_enabled: true
38
39
  )
39
40
 
41
+ alias version_check_enabled? version_check_enabled
42
+
40
43
  ignore_options :rpc_server_args
41
44
  flag_options :log_grpc, :debug
42
45
 
@@ -71,7 +74,7 @@ module AnyCable
71
74
 
72
75
  # Build Redis parameters
73
76
  def to_redis_params
74
- { url: redis_url }.tap do |params|
77
+ {url: redis_url}.tap do |params|
75
78
  next if redis_sentinels.nil?
76
79
 
77
80
  raise ArgumentError, "redis_sentinels must be an array; got #{redis_sentinels}" unless
@@ -102,7 +105,7 @@ module AnyCable
102
105
 
103
106
  raise ArgumentError, "Invalid Sentinel value: #{sentinel}" if matches.nil?
104
107
 
105
- { "host" => matches[1], "port" => matches[2].to_i }
108
+ {"host" => matches[1], "port" => matches[2].to_i}
106
109
  end
107
110
  end
108
111
  end
@@ -11,13 +11,11 @@ module AnyCable
11
11
 
12
12
  def notify(exp, method_name, message)
13
13
  handlers.each do |handler|
14
- begin
15
- handler.call(exp, method_name, message)
16
- rescue StandardError => exp
17
- AnyCable.logger.error "!!! EXCEPTION HANDLER THREW AN ERROR !!!"
18
- AnyCable.logger.error exp
19
- AnyCable.logger.error exp.backtrace.join("\n") unless exp.backtrace.nil?
20
- end
14
+ handler.call(exp, method_name, message)
15
+ rescue => exp
16
+ AnyCable.logger.error "!!! EXCEPTION HANDLER THREW AN ERROR !!!"
17
+ AnyCable.logger.error exp
18
+ AnyCable.logger.error exp.backtrace.join("\n") unless exp.backtrace.nil?
21
19
  end
22
20
  end
23
21
 
@@ -7,6 +7,9 @@ module AnyCable
7
7
  # for request/response calls
8
8
  class Middleware < GRPC::Interceptor
9
9
  def request_response(request: nil, call: nil, method: nil)
10
+ # Call middlewares only for AnyCable service
11
+ return yield unless method.receiver.is_a?(AnyCable::RPCHandler)
12
+
10
13
  call(request, call, method) do
11
14
  yield
12
15
  end
@@ -48,8 +48,8 @@ module AnyCable
48
48
 
49
49
  unless middleware.is_a?(AnyCable::Middleware)
50
50
  raise ArgumentError,
51
- "AnyCable middleware must be a subclass of AnyCable::Middleware, " \
52
- "got #{middleware} instead"
51
+ "AnyCable middleware must be a subclass of AnyCable::Middleware, " \
52
+ "got #{middleware} instead"
53
53
  end
54
54
 
55
55
  middleware
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnyCable
4
+ module Middlewares
5
+ # Checks that RPC client version is compatibly with
6
+ # the current RPC proto version
7
+ class CheckVersion < Middleware
8
+ attr_reader :version
9
+
10
+ def initialize(version)
11
+ @version = version
12
+ end
13
+
14
+ def call(_request, call, _method)
15
+ supported_versions = call.metadata["protov"]&.split(",")
16
+ return yield if supported_versions&.include?(version)
17
+
18
+ raise GRPC::Internal,
19
+ "Incompatible AnyCable RPC client.\nCurrent server version: #{version}.\n" \
20
+ "Client supported versions: #{call.metadata["protov"] || "unknown"}."
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anycable/rpc/rpc_pb"
4
+ require "anycable/rpc/rpc_services_pb"
5
+
6
+ # Extend some PB auto-generated classes
7
+ module AnyCable
8
+ # Current RPC proto version (used for compatibility checks)
9
+ PROTO_VERSION = "v1"
10
+ SESSION_KEY = "_s_"
11
+
12
+ # Add setters/getter for cstate field
13
+ module WithConnectionState
14
+ def initialize(session: nil, **other)
15
+ if session
16
+ other[:cstate] ||= {}
17
+ other[:cstate][SESSION_KEY] = session
18
+ end
19
+ super(**other)
20
+ end
21
+
22
+ def session=(val)
23
+ self.cstate = {} unless cstate
24
+ cstate[SESSION_KEY] = val
25
+ end
26
+
27
+ def session
28
+ cstate[SESSION_KEY]
29
+ end
30
+
31
+ def cstate
32
+ env.cstate
33
+ end
34
+
35
+ def cstate=(val)
36
+ env.cstate = val
37
+ end
38
+ end
39
+
40
+ # Status predicates
41
+ module StatusPredicates
42
+ def success?
43
+ status == :SUCCESS
44
+ end
45
+
46
+ def failure?
47
+ status == :FAILURE
48
+ end
49
+
50
+ def error?
51
+ status == :ERROR
52
+ end
53
+ end
54
+
55
+ class ConnectionResponse
56
+ prepend WithConnectionState
57
+ include StatusPredicates
58
+ end
59
+
60
+ class CommandMessage
61
+ prepend WithConnectionState
62
+ end
63
+
64
+ class CommandResponse
65
+ prepend WithConnectionState
66
+ include StatusPredicates
67
+ end
68
+
69
+ class DisconnectRequest
70
+ prepend WithConnectionState
71
+ end
72
+
73
+ class DisconnectResponse
74
+ include StatusPredicates
75
+ end
76
+ end
@@ -1,51 +1,66 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Generated by the protocol buffer compiler. DO NOT EDIT!
2
4
  # source: rpc.proto
3
5
 
4
- require 'google/protobuf'
6
+ require "google/protobuf"
5
7
 
6
8
  Google::Protobuf::DescriptorPool.generated_pool.build do
7
- add_message "anycable.ConnectionRequest" do
8
- optional :path, :string, 1
9
- map :headers, :string, :string, 2
10
- end
11
- add_message "anycable.ConnectionResponse" do
12
- optional :status, :enum, 1, "anycable.Status"
13
- optional :identifiers, :string, 2
14
- repeated :transmissions, :string, 3
15
- optional :error_msg, :string, 4
16
- end
17
- add_message "anycable.CommandMessage" do
18
- optional :command, :string, 1
19
- optional :identifier, :string, 2
20
- optional :connection_identifiers, :string, 3
21
- optional :data, :string, 4
22
- end
23
- add_message "anycable.CommandResponse" do
24
- optional :status, :enum, 1, "anycable.Status"
25
- optional :disconnect, :bool, 2
26
- optional :stop_streams, :bool, 3
27
- repeated :streams, :string, 4
28
- repeated :transmissions, :string, 5
29
- optional :error_msg, :string, 6
30
- end
31
- add_message "anycable.DisconnectRequest" do
32
- optional :identifiers, :string, 1
33
- repeated :subscriptions, :string, 2
34
- optional :path, :string, 3
35
- map :headers, :string, :string, 4
36
- end
37
- add_message "anycable.DisconnectResponse" do
38
- optional :status, :enum, 1, "anycable.Status"
39
- optional :error_msg, :string, 2
40
- end
41
- add_enum "anycable.Status" do
42
- value :ERROR, 0
43
- value :SUCCESS, 1
44
- value :FAILURE, 2
9
+ add_file("rpc.proto", syntax: :proto3) do
10
+ add_message "anycable.Env" do
11
+ optional :url, :string, 1
12
+ map :headers, :string, :string, 2
13
+ map :cstate, :string, :string, 3
14
+ end
15
+ add_message "anycable.EnvResponse" do
16
+ map :cstate, :string, :string, 1
17
+ end
18
+ add_message "anycable.ConnectionRequest" do
19
+ optional :env, :message, 3, "anycable.Env"
20
+ end
21
+ add_message "anycable.ConnectionResponse" do
22
+ optional :status, :enum, 1, "anycable.Status"
23
+ optional :identifiers, :string, 2
24
+ repeated :transmissions, :string, 3
25
+ optional :error_msg, :string, 4
26
+ optional :env, :message, 5, "anycable.EnvResponse"
27
+ end
28
+ add_message "anycable.CommandMessage" do
29
+ optional :command, :string, 1
30
+ optional :identifier, :string, 2
31
+ optional :connection_identifiers, :string, 3
32
+ optional :data, :string, 4
33
+ optional :env, :message, 5, "anycable.Env"
34
+ end
35
+ add_message "anycable.CommandResponse" do
36
+ optional :status, :enum, 1, "anycable.Status"
37
+ optional :disconnect, :bool, 2
38
+ optional :stop_streams, :bool, 3
39
+ repeated :streams, :string, 4
40
+ repeated :transmissions, :string, 5
41
+ optional :error_msg, :string, 6
42
+ optional :env, :message, 7, "anycable.EnvResponse"
43
+ end
44
+ add_message "anycable.DisconnectRequest" do
45
+ optional :identifiers, :string, 1
46
+ repeated :subscriptions, :string, 2
47
+ optional :env, :message, 5, "anycable.Env"
48
+ end
49
+ add_message "anycable.DisconnectResponse" do
50
+ optional :status, :enum, 1, "anycable.Status"
51
+ optional :error_msg, :string, 2
52
+ end
53
+ add_enum "anycable.Status" do
54
+ value :ERROR, 0
55
+ value :SUCCESS, 1
56
+ value :FAILURE, 2
57
+ end
45
58
  end
46
59
  end
47
60
 
48
61
  module AnyCable
62
+ Env = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.Env").msgclass
63
+ EnvResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.EnvResponse").msgclass
49
64
  ConnectionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.ConnectionRequest").msgclass
50
65
  ConnectionResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.ConnectionResponse").msgclass
51
66
  CommandMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.CommandMessage").msgclass
@@ -1,17 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Generated by the protocol buffer compiler. DO NOT EDIT!
2
4
  # Source: rpc.proto for package 'anycable'
3
5
 
4
- require 'grpc'
6
+ require "grpc"
5
7
 
6
8
  module AnyCable
7
9
  module RPC
8
10
  class Service
9
-
10
11
  include GRPC::GenericService
11
12
 
12
13
  self.marshal_class_method = :encode
13
14
  self.unmarshal_class_method = :decode
14
- self.service_name = 'anycable.RPC'
15
+ self.service_name = "anycable.RPC"
15
16
 
16
17
  rpc :Connect, ConnectionRequest, ConnectionResponse
17
18
  rpc :Command, CommandMessage, CommandResponse
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "anycable/socket"
4
- require "anycable/rpc/rpc_pb"
5
- require "anycable/rpc/rpc_services_pb"
4
+ require "anycable/rpc"
6
5
 
7
6
  # rubocop:disable Metrics/AbcSize
8
7
  # rubocop:disable Metrics/MethodLength
@@ -14,22 +13,26 @@ module AnyCable
14
13
  def connect(request, _unused_call)
15
14
  logger.debug("RPC Connect: #{request.inspect}")
16
15
 
17
- socket = build_socket(env: rack_env(request))
16
+ socket = build_socket(env: rack_env(request.env))
18
17
 
19
18
  connection = factory.call(socket)
20
19
 
21
20
  connection.handle_open
22
21
 
23
22
  if socket.closed?
24
- AnyCable::ConnectionResponse.new(status: AnyCable::Status::FAILURE)
23
+ AnyCable::ConnectionResponse.new(
24
+ status: AnyCable::Status::FAILURE,
25
+ transmissions: socket.transmissions
26
+ )
25
27
  else
26
28
  AnyCable::ConnectionResponse.new(
27
29
  status: AnyCable::Status::SUCCESS,
28
30
  identifiers: connection.identifiers_json,
29
- transmissions: socket.transmissions
31
+ transmissions: socket.transmissions,
32
+ env: build_env_response(socket)
30
33
  )
31
34
  end
32
- rescue StandardError => exp
35
+ rescue => exp
33
36
  notify_exception(exp, :connect, request)
34
37
 
35
38
  AnyCable::ConnectionResponse.new(
@@ -41,7 +44,7 @@ module AnyCable
41
44
  def disconnect(request, _unused_call)
42
45
  logger.debug("RPC Disconnect: #{request.inspect}")
43
46
 
44
- socket = build_socket(env: rack_env(request))
47
+ socket = build_socket(env: rack_env(request.env))
45
48
 
46
49
  connection = factory.call(
47
50
  socket,
@@ -54,7 +57,7 @@ module AnyCable
54
57
  else
55
58
  AnyCable::DisconnectResponse.new(status: AnyCable::Status::FAILURE)
56
59
  end
57
- rescue StandardError => exp
60
+ rescue => exp
58
61
  notify_exception(exp, :disconnect, request)
59
62
 
60
63
  AnyCable::DisconnectResponse.new(
@@ -66,9 +69,7 @@ module AnyCable
66
69
  def command(message, _unused_call)
67
70
  logger.debug("RPC Command: #{message.inspect}")
68
71
 
69
- # We don't have path/headers information here,
70
- # but we still want `connection.env` to work
71
- socket = build_socket(env: base_rack_env)
72
+ socket = build_socket(env: rack_env(message.env))
72
73
 
73
74
  connection = factory.call(
74
75
  socket,
@@ -86,9 +87,10 @@ module AnyCable
86
87
  disconnect: socket.closed?,
87
88
  stop_streams: socket.stop_streams?,
88
89
  streams: socket.streams,
89
- transmissions: socket.transmissions
90
+ transmissions: socket.transmissions,
91
+ env: build_env_response(socket)
90
92
  )
91
- rescue StandardError => exp
93
+ rescue => exp
92
94
  notify_exception(exp, :command, message)
93
95
 
94
96
  AnyCable::CommandResponse.new(
@@ -100,8 +102,8 @@ module AnyCable
100
102
  private
101
103
 
102
104
  # Build Rack env from request
103
- def rack_env(request)
104
- uri = URI.parse(request.path)
105
+ def rack_env(request_env)
106
+ uri = URI.parse(request_env.url)
105
107
 
106
108
  env = base_rack_env
107
109
  env.merge!(
@@ -110,11 +112,13 @@ module AnyCable
110
112
  "SERVER_NAME" => uri.host,
111
113
  "SERVER_PORT" => uri.port.to_s,
112
114
  "HTTP_HOST" => uri.host,
113
- "REMOTE_ADDR" => request.headers.delete("REMOTE_ADDR"),
114
- "rack.url_scheme" => uri.scheme
115
+ "REMOTE_ADDR" => request_env.headers.delete("REMOTE_ADDR"),
116
+ "rack.url_scheme" => uri.scheme,
117
+ # AnyCable specific fields
118
+ "anycable.raw_cstate" => request_env.cstate&.to_h
115
119
  )
116
120
 
117
- env.merge!(build_headers(request.headers))
121
+ env.merge!(build_headers(request_env.headers))
118
122
  end
119
123
 
120
124
  def base_rack_env
@@ -145,6 +149,12 @@ module AnyCable
145
149
  end
146
150
  end
147
151
 
152
+ def build_env_response(socket)
153
+ AnyCable::EnvResponse.new(
154
+ cstate: socket.cstate.changed_fields
155
+ )
156
+ end
157
+
148
158
  def logger
149
159
  AnyCable.logger
150
160
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Utils for testing AnyCable and its plugins
4
+ require "anycable/rspec/rpc_stub_context"
5
+ require "anycable/rspec/rpc_command_context"
6
+ require "anycable/rspec/with_grpc_server"
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context "anycable:rpc:command" do
4
+ include_context "anycable:rpc:stub"
5
+
6
+ let(:command) { "" }
7
+ let(:channel_id) { "" }
8
+ let(:identifiers) { {} }
9
+ let(:data) { {} }
10
+
11
+ let(:request) do
12
+ AnyCable::CommandMessage.new(
13
+ command: command,
14
+ identifier: channel_id,
15
+ connection_identifiers: identifiers.to_json,
16
+ data: data.to_json,
17
+ env: env
18
+ )
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context "anycable:rpc:stub" do
4
+ before(:all) do
5
+ @service = AnyCable::RPC::Stub.new(AnyCable.config.rpc_host, :this_channel_is_insecure)
6
+ end
7
+
8
+ let(:service) { @service }
9
+
10
+ let(:url) { "example.com/cable" }
11
+ let(:headers) { {} }
12
+ let(:env) { AnyCable::Env.new(url: url, headers: headers) }
13
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_context "anycable:rpc:server" do
4
+ before(:all) do
5
+ @server = AnyCable::Server.new(
6
+ host: AnyCable.config.rpc_host,
7
+ **AnyCable.config.to_grpc_params,
8
+ interceptors: AnyCable.middleware.to_a
9
+ )
10
+
11
+ @server.start
12
+ end
13
+
14
+ after(:all) { @server.stop }
15
+ end
@@ -21,48 +21,6 @@ module AnyCable
21
21
  # # stop server
22
22
  # server.stop
23
23
  class Server
24
- class << self
25
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
26
- def start(**options)
27
- warn <<~DEPRECATION
28
- DEPRECATION WARNING: Using AnyCable::Server.start is deprecated!
29
- Please, use anycable CLI instead.
30
-
31
- See https://docs.anycable.io/#upgrade_to_0_6_0
32
- DEPRECATION
33
-
34
- AnyCable.server_callbacks.each(&:call)
35
-
36
- server = new(
37
- host: AnyCable.config.rpc_host,
38
- **AnyCable.config.to_grpc_params,
39
- interceptors: AnyCable.middleware.to_a,
40
- **options
41
- )
42
-
43
- AnyCable.middleware.freeze
44
-
45
- if AnyCable.config.http_health_port_provided?
46
- health_server = AnyCable::HealthServer.new(
47
- server,
48
- **AnyCable.config.to_http_health_params
49
- )
50
- health_server.start
51
- end
52
-
53
- at_exit do
54
- server.stop
55
- health_server&.stop
56
- end
57
-
58
- AnyCable.logger.info "Broadcasting Redis channel: #{AnyCable.config.redis_channel}"
59
-
60
- server.start
61
- server.wait_till_terminated
62
- end
63
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
64
- end
65
-
66
24
  attr_reader :grpc_server, :host
67
25
 
68
26
  def initialize(host:, logger: AnyCable.logger, **options)
@@ -78,8 +36,6 @@ module AnyCable
78
36
 
79
37
  raise "Cannot re-start stopped server" if stopped?
80
38
 
81
- check_default_host
82
-
83
39
  logger.info "RPC server is starting..."
84
40
 
85
41
  @start_thread = Thread.new { grpc_server.run }
@@ -132,21 +88,5 @@ module AnyCable
132
88
  )
133
89
  health_checker
134
90
  end
135
-
136
- def check_default_host
137
- return unless host.is_a?(Anycable::Config::DefaultHostWrapper)
138
-
139
- warn <<~DEPRECATION
140
- DEPRECATION WARNING: You're using default rpc_host configuration which starts AnyCable RPC
141
- server on all available interfaces including external IPv4 and IPv6.
142
- This is about to be changed to loopback interface only in future versions.
143
-
144
- Please, consider switching to the loopback interface or set "[::]:50051"
145
- explicitly in your configuration, if you want to continue with the current
146
- behavior and supress this message.
147
-
148
- See https://docs.anycable.io/#/configuration
149
- DEPRECATION
150
- end
151
91
  end
152
92
  end
@@ -3,11 +3,41 @@
3
3
  module AnyCable
4
4
  # Socket mock to be used with application connection
5
5
  class Socket
6
- attr_reader :transmissions, :env
6
+ # Represents the per-connection store
7
+ # (for example, used to keep session beetween RPC calls)
8
+ class State
9
+ attr_reader :dirty_keys, :source
10
+
11
+ def initialize(from)
12
+ @source = from
13
+ @dirty_keys = nil
14
+ end
15
+
16
+ def read(key)
17
+ source&.[](key)
18
+ end
19
+
20
+ def write(key, val)
21
+ return if source&.[](key) == val
22
+
23
+ @source ||= {}
24
+ @dirty_keys ||= []
25
+ dirty_keys << key
26
+ source[key] = val
27
+ end
28
+
29
+ def changed_fields
30
+ return unless source && dirty_keys
31
+ source.slice(*dirty_keys)
32
+ end
33
+ end
34
+
35
+ attr_reader :transmissions, :env, :cstate
7
36
 
8
37
  def initialize(env: nil)
9
38
  @transmissions = []
10
39
  @env = env
40
+ @cstate = env["anycable.cstate"] = State.new(env["anycable.raw_cstate"])
11
41
  end
12
42
 
13
43
  def transmit(websocket_message)
@@ -32,7 +62,6 @@ module AnyCable
32
62
 
33
63
  def close
34
64
  @closed = true
35
- @transmissions.clear
36
65
  @streams&.clear
37
66
  @stop_all_streams = true
38
67
  end
@@ -44,5 +73,13 @@ module AnyCable
44
73
  def stop_streams?
45
74
  @stop_all_streams == true
46
75
  end
76
+
77
+ def session
78
+ cstate.read(SESSION_KEY)
79
+ end
80
+
81
+ def session=(val)
82
+ cstate.write(SESSION_KEY, val)
83
+ end
47
84
  end
48
85
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AnyCable
4
- VERSION = "0.6.5"
4
+ VERSION = "1.0.0.preview1"
5
5
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anycable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 1.0.0.preview1
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-01 00:00:00.000000000 Z
11
+ date: 2020-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anyway_config
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: 1.4.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.4.2
27
27
  - !ruby/object:Gem::Dependency
@@ -109,19 +109,19 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '3.5'
111
111
  - !ruby/object:Gem::Dependency
112
- name: rubocop
112
+ name: rubocop-md
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: 0.68.0
117
+ version: '0.3'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 0.68.0
124
+ version: '0.3'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: simplecov
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -137,19 +137,19 @@ dependencies:
137
137
  - !ruby/object:Gem::Version
138
138
  version: 0.3.8
139
139
  - !ruby/object:Gem::Dependency
140
- name: pry-byebug
140
+ name: standard
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - ">="
143
+ - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '0'
145
+ version: 0.1.7
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - ">="
150
+ - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '0'
152
+ version: 0.1.7
153
153
  description: AnyCable is a polyglot replacement for ActionCable-compatible servers
154
154
  email:
155
155
  - dementiev.vm@gmail.com
@@ -175,9 +175,15 @@ files:
175
175
  - lib/anycable/health_server.rb
176
176
  - lib/anycable/middleware.rb
177
177
  - lib/anycable/middleware_chain.rb
178
+ - lib/anycable/middlewares/check_version.rb
179
+ - lib/anycable/rpc.rb
178
180
  - lib/anycable/rpc/rpc_pb.rb
179
181
  - lib/anycable/rpc/rpc_services_pb.rb
180
182
  - lib/anycable/rpc_handler.rb
183
+ - lib/anycable/rspec.rb
184
+ - lib/anycable/rspec/rpc_command_context.rb
185
+ - lib/anycable/rspec/rpc_stub_context.rb
186
+ - lib/anycable/rspec/with_grpc_server.rb
181
187
  - lib/anycable/server.rb
182
188
  - lib/anycable/socket.rb
183
189
  - lib/anycable/version.rb
@@ -198,12 +204,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
198
204
  requirements:
199
205
  - - ">="
200
206
  - !ruby/object:Gem::Version
201
- version: 2.4.0
207
+ version: 2.5.0
202
208
  required_rubygems_version: !ruby/object:Gem::Requirement
203
209
  requirements:
204
- - - ">="
210
+ - - ">"
205
211
  - !ruby/object:Gem::Version
206
- version: '0'
212
+ version: 1.3.1
207
213
  requirements: []
208
214
  rubygems_version: 3.0.6
209
215
  signing_key: