anycable 0.6.5 → 1.0.0.preview1
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 +4 -4
- data/CHANGELOG.md +21 -5
- data/README.md +5 -5
- data/lib/anycable.rb +2 -11
- data/lib/anycable/broadcast_adapters.rb +2 -2
- data/lib/anycable/broadcast_adapters/redis.rb +1 -1
- data/lib/anycable/cli.rb +13 -3
- data/lib/anycable/config.rb +9 -6
- data/lib/anycable/exceptions_handling.rb +5 -7
- data/lib/anycable/middleware.rb +3 -0
- data/lib/anycable/middleware_chain.rb +2 -2
- data/lib/anycable/middlewares/check_version.rb +24 -0
- data/lib/anycable/rpc.rb +76 -0
- data/lib/anycable/rpc/rpc_pb.rb +54 -39
- data/lib/anycable/rpc/rpc_services_pb.rb +4 -3
- data/lib/anycable/rpc_handler.rb +28 -18
- data/lib/anycable/rspec.rb +6 -0
- data/lib/anycable/rspec/rpc_command_context.rb +20 -0
- data/lib/anycable/rspec/rpc_stub_context.rb +13 -0
- data/lib/anycable/rspec/with_grpc_server.rb +15 -0
- data/lib/anycable/server.rb +0 -60
- data/lib/anycable/socket.rb +39 -2
- data/lib/anycable/version.rb +1 -1
- metadata +21 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65d07fe802d850f89351ed8c054d2cba9c393b36f7a5aa318183362f09d53e58
|
4
|
+
data.tar.gz: b4167ba77ce930ddcb77a9e38d205215fb3ec085a93bfd3876f2bf8a5e669373
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 456af4dd28de6d32a8436ce8702ddb3ba5523b25b531bce8dac00c5bf6cd181e79c57e4d4a89d1a2c1ff3e21eda1108be3f023c65edcf1bbf5790594216ae971
|
7
|
+
data.tar.gz: b25fa2a291a6cd6bbf4734c9c59dcf991dead19c0e25c004ac696ec524e6de63bb5ea4251f8d06ce1d21699a44e178a7e445bfedcd4d70179535c6648ed67854
|
data/CHANGELOG.md
CHANGED
@@ -1,14 +1,30 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
-
##
|
3
|
+
## 🚧 1.0.0 (_coming soon_)
|
4
4
|
|
5
|
-
|
5
|
+
- **RPC schema has changed**. ([@palkan][])
|
6
6
|
|
7
|
-
|
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, {
|
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.
|
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
|
-
|
data/lib/anycable.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
24
|
+
e.backtrace
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/anycable/cli.rb
CHANGED
@@ -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) ?
|
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,
|
data/lib/anycable/config.rb
CHANGED
@@ -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:
|
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
|
-
{
|
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
|
-
{
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
|
data/lib/anycable/middleware.rb
CHANGED
@@ -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
|
-
|
52
|
-
|
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
|
data/lib/anycable/rpc.rb
ADDED
@@ -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
|
data/lib/anycable/rpc/rpc_pb.rb
CHANGED
@@ -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
|
6
|
+
require "google/protobuf"
|
5
7
|
|
6
8
|
Google::Protobuf::DescriptorPool.generated_pool.build do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
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 =
|
15
|
+
self.service_name = "anycable.RPC"
|
15
16
|
|
16
17
|
rpc :Connect, ConnectionRequest, ConnectionResponse
|
17
18
|
rpc :Command, CommandMessage, CommandResponse
|
data/lib/anycable/rpc_handler.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "anycable/socket"
|
4
|
-
require "anycable/rpc
|
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(
|
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
|
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
|
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
|
-
|
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
|
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(
|
104
|
-
uri = URI.parse(
|
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" =>
|
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(
|
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,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
|
data/lib/anycable/server.rb
CHANGED
@@ -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
|
data/lib/anycable/socket.rb
CHANGED
@@ -3,11 +3,41 @@
|
|
3
3
|
module AnyCable
|
4
4
|
# Socket mock to be used with application connection
|
5
5
|
class Socket
|
6
|
-
|
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
|
data/lib/anycable/version.rb
CHANGED
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.
|
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-
|
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.
|
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.
|
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:
|
140
|
+
name: standard
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- - "
|
143
|
+
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
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:
|
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.
|
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:
|
212
|
+
version: 1.3.1
|
207
213
|
requirements: []
|
208
214
|
rubygems_version: 3.0.6
|
209
215
|
signing_key:
|