anycable 1.0.1 → 1.1.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 +4 -4
- data/CHANGELOG.md +31 -0
- data/MIT-LICENSE +1 -1
- data/README.md +1 -4
- metadata +16 -128
- data/bin/anycable +0 -13
- data/bin/anycabled +0 -30
- data/bin/console +0 -7
- data/bin/setup +0 -6
- data/lib/anycable.rb +0 -101
- data/lib/anycable/broadcast_adapters.rb +0 -35
- data/lib/anycable/broadcast_adapters/base.rb +0 -29
- data/lib/anycable/broadcast_adapters/http.rb +0 -130
- data/lib/anycable/broadcast_adapters/redis.rb +0 -43
- data/lib/anycable/cli.rb +0 -353
- data/lib/anycable/config.rb +0 -131
- data/lib/anycable/exceptions_handling.rb +0 -35
- data/lib/anycable/health_server.rb +0 -69
- data/lib/anycable/middleware.rb +0 -27
- data/lib/anycable/middleware_chain.rb +0 -58
- data/lib/anycable/middlewares/check_version.rb +0 -24
- data/lib/anycable/rpc.rb +0 -84
- data/lib/anycable/rpc/rpc_pb.rb +0 -74
- data/lib/anycable/rpc/rpc_services_pb.rb +0 -24
- data/lib/anycable/rpc_handler.rb +0 -183
- data/lib/anycable/rspec.rb +0 -6
- data/lib/anycable/rspec/rpc_command_context.rb +0 -20
- data/lib/anycable/rspec/rpc_stub_context.rb +0 -13
- data/lib/anycable/rspec/with_grpc_server.rb +0 -16
- data/lib/anycable/server.rb +0 -96
- data/lib/anycable/socket.rb +0 -90
- data/lib/anycable/version.rb +0 -5
data/lib/anycable/config.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "anyway_config"
|
4
|
-
require "grpc"
|
5
|
-
|
6
|
-
require "uri"
|
7
|
-
|
8
|
-
module AnyCable
|
9
|
-
# AnyCable configuration.
|
10
|
-
class Config < Anyway::Config
|
11
|
-
config_name :anycable
|
12
|
-
|
13
|
-
attr_config(
|
14
|
-
### gRPC options
|
15
|
-
rpc_host: "127.0.0.1:50051",
|
16
|
-
# For defaults see https://github.com/grpc/grpc/blob/51f0d35509bcdaba572d422c4f856208162022de/src/ruby/lib/grpc/generic/rpc_server.rb#L186-L216
|
17
|
-
rpc_pool_size: GRPC::RpcServer::DEFAULT_POOL_SIZE,
|
18
|
-
rpc_max_waiting_requests: GRPC::RpcServer::DEFAULT_MAX_WAITING_REQUESTS,
|
19
|
-
rpc_poll_period: GRPC::RpcServer::DEFAULT_POLL_PERIOD,
|
20
|
-
rpc_pool_keep_alive: GRPC::Pool::DEFAULT_KEEP_ALIVE,
|
21
|
-
# See https://github.com/grpc/grpc/blob/f526602bff029b8db50a8d57134d72da33d8a752/include/grpc/impl/codegen/grpc_types.h#L292-L315
|
22
|
-
rpc_server_args: {},
|
23
|
-
|
24
|
-
## PubSub
|
25
|
-
broadcast_adapter: :redis,
|
26
|
-
|
27
|
-
### Redis options
|
28
|
-
redis_url: ENV.fetch("REDIS_URL", "redis://localhost:6379/5"),
|
29
|
-
redis_sentinels: nil,
|
30
|
-
redis_channel: "__anycable__",
|
31
|
-
|
32
|
-
### HTTP broadcasting options
|
33
|
-
http_broadcast_url: "http://localhost:8090/_broadcast",
|
34
|
-
http_broadcast_secret: nil,
|
35
|
-
|
36
|
-
### Logging options
|
37
|
-
log_file: nil,
|
38
|
-
log_level: :info,
|
39
|
-
log_grpc: false,
|
40
|
-
debug: false, # Shortcut to enable GRPC logging and debug level
|
41
|
-
|
42
|
-
### Health check options
|
43
|
-
http_health_port: nil,
|
44
|
-
http_health_path: "/health",
|
45
|
-
|
46
|
-
### Misc options
|
47
|
-
version_check_enabled: true
|
48
|
-
)
|
49
|
-
|
50
|
-
alias version_check_enabled? version_check_enabled
|
51
|
-
|
52
|
-
ignore_options :rpc_server_args
|
53
|
-
flag_options :log_grpc, :debug
|
54
|
-
|
55
|
-
# Support both anyway_config 1.4 and 2.0.
|
56
|
-
# DEPRECATE: Drop <2.0 support in 1.1
|
57
|
-
if respond_to?(:on_load)
|
58
|
-
on_load { self.debug = debug != false }
|
59
|
-
|
60
|
-
def log_level
|
61
|
-
debug? ? :debug : super
|
62
|
-
end
|
63
|
-
|
64
|
-
def log_grpc
|
65
|
-
debug? || super
|
66
|
-
end
|
67
|
-
else
|
68
|
-
def log_level
|
69
|
-
debug ? :debug : @log_level
|
70
|
-
end
|
71
|
-
|
72
|
-
def log_grpc
|
73
|
-
debug || @log_grpc
|
74
|
-
end
|
75
|
-
|
76
|
-
def debug
|
77
|
-
@debug != false
|
78
|
-
end
|
79
|
-
|
80
|
-
alias debug? debug
|
81
|
-
end
|
82
|
-
|
83
|
-
def http_health_port_provided?
|
84
|
-
!http_health_port.nil? && http_health_port != ""
|
85
|
-
end
|
86
|
-
|
87
|
-
# Build gRPC server parameters
|
88
|
-
def to_grpc_params
|
89
|
-
{
|
90
|
-
pool_size: rpc_pool_size,
|
91
|
-
max_waiting_requests: rpc_max_waiting_requests,
|
92
|
-
poll_period: rpc_poll_period,
|
93
|
-
pool_keep_alive: rpc_pool_keep_alive,
|
94
|
-
server_args: rpc_server_args
|
95
|
-
}
|
96
|
-
end
|
97
|
-
|
98
|
-
# Build Redis parameters
|
99
|
-
def to_redis_params
|
100
|
-
{url: redis_url}.tap do |params|
|
101
|
-
next if redis_sentinels.nil?
|
102
|
-
|
103
|
-
sentinels = Array(redis_sentinels)
|
104
|
-
|
105
|
-
next if sentinels.empty?
|
106
|
-
|
107
|
-
params[:sentinels] = sentinels.map(&method(:parse_sentinel))
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# Build HTTP health server parameters
|
112
|
-
def to_http_health_params
|
113
|
-
{
|
114
|
-
port: http_health_port,
|
115
|
-
path: http_health_path
|
116
|
-
}
|
117
|
-
end
|
118
|
-
|
119
|
-
private
|
120
|
-
|
121
|
-
def parse_sentinel(sentinel)
|
122
|
-
return sentinel.transform_keys!(&:to_sym) if sentinel.is_a?(Hash)
|
123
|
-
|
124
|
-
uri = URI.parse("redis://#{sentinel}")
|
125
|
-
|
126
|
-
{host: uri.host, port: uri.port}.tap do |opts|
|
127
|
-
opts[:password] = uri.password if uri.password
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AnyCable
|
4
|
-
module ExceptionsHandling # :nodoc:
|
5
|
-
class << self
|
6
|
-
def add_handler(block)
|
7
|
-
handlers << procify(block)
|
8
|
-
end
|
9
|
-
|
10
|
-
alias << add_handler
|
11
|
-
|
12
|
-
def notify(exp, method_name, message)
|
13
|
-
handlers.each do |handler|
|
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?
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def procify(block)
|
25
|
-
return block unless block.lambda?
|
26
|
-
|
27
|
-
proc { |*args| block.call(*args.take(block.arity)) }
|
28
|
-
end
|
29
|
-
|
30
|
-
def handlers
|
31
|
-
@handlers ||= []
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AnyCable
|
4
|
-
# Server for HTTP healthchecks.
|
5
|
-
#
|
6
|
-
# Basic usage:
|
7
|
-
#
|
8
|
-
# # create a new healthcheck server for a specified
|
9
|
-
# # gRPC server lisening on the port
|
10
|
-
# health_server = AnyCable::HealthServer.new(grpc_server, port)
|
11
|
-
#
|
12
|
-
# # start health server in background
|
13
|
-
# health_server.start
|
14
|
-
#
|
15
|
-
# # stop health server
|
16
|
-
# health_server.stop
|
17
|
-
class HealthServer
|
18
|
-
SUCCESS_RESPONSE = [200, "Ready"].freeze
|
19
|
-
FAILURE_RESPONSE = [503, "Not Ready"].freeze
|
20
|
-
|
21
|
-
attr_reader :grpc_server, :port, :path, :server
|
22
|
-
|
23
|
-
def initialize(grpc_server, port:, logger: nil, path: "/health")
|
24
|
-
@grpc_server = grpc_server
|
25
|
-
@port = port
|
26
|
-
@path = path
|
27
|
-
@logger = logger
|
28
|
-
@server = build_server
|
29
|
-
end
|
30
|
-
|
31
|
-
def start
|
32
|
-
return if running?
|
33
|
-
|
34
|
-
Thread.new { server.start }
|
35
|
-
|
36
|
-
logger.info "HTTP health server is listening on localhost:#{port} and mounted at \"#{path}\""
|
37
|
-
end
|
38
|
-
|
39
|
-
def stop
|
40
|
-
return unless running?
|
41
|
-
|
42
|
-
server.shutdown
|
43
|
-
end
|
44
|
-
|
45
|
-
def running?
|
46
|
-
server.status == :Running
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def logger
|
52
|
-
@logger ||= AnyCable.logger
|
53
|
-
end
|
54
|
-
|
55
|
-
def build_server
|
56
|
-
require "webrick"
|
57
|
-
|
58
|
-
WEBrick::HTTPServer.new(
|
59
|
-
Port: port,
|
60
|
-
Logger: logger,
|
61
|
-
AccessLog: []
|
62
|
-
).tap do |server|
|
63
|
-
server.mount_proc path do |_, res|
|
64
|
-
res.status, res.body = grpc_server.running? ? SUCCESS_RESPONSE : FAILURE_RESPONSE
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
data/lib/anycable/middleware.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "grpc"
|
4
|
-
|
5
|
-
module AnyCable
|
6
|
-
# Middleware is a wrapper over gRPC interceptors
|
7
|
-
# for request/response calls
|
8
|
-
class Middleware < GRPC::ServerInterceptor
|
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
|
-
|
13
|
-
call(request, call, method) do
|
14
|
-
yield
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def server_streamer(**kwargs)
|
19
|
-
p kwargs
|
20
|
-
yield
|
21
|
-
end
|
22
|
-
|
23
|
-
def call(*)
|
24
|
-
raise NotImplementedError
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,58 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "anycable/middleware"
|
4
|
-
require "monitor"
|
5
|
-
|
6
|
-
module AnyCable
|
7
|
-
# Middleware chain is used to build the list of
|
8
|
-
# gRPC server interceptors.
|
9
|
-
#
|
10
|
-
# Each interceptor should be a subsclass of
|
11
|
-
# AnyCable::Middleware and implement `#call` method.
|
12
|
-
class MiddlewareChain
|
13
|
-
def initialize
|
14
|
-
@registry = []
|
15
|
-
@mu = Monitor.new
|
16
|
-
end
|
17
|
-
|
18
|
-
def use(middleware)
|
19
|
-
check_frozen!
|
20
|
-
middleware = build_middleware(middleware)
|
21
|
-
sync { registry << middleware }
|
22
|
-
end
|
23
|
-
|
24
|
-
def freeze
|
25
|
-
registry.freeze
|
26
|
-
super
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_a
|
30
|
-
registry
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
attr_reader :mu, :registry
|
36
|
-
|
37
|
-
def sync
|
38
|
-
mu.synchronize { yield }
|
39
|
-
end
|
40
|
-
|
41
|
-
def check_frozen!
|
42
|
-
raise "Cannot modify AnyCable middlewares after server started" if frozen?
|
43
|
-
end
|
44
|
-
|
45
|
-
def build_middleware(middleware)
|
46
|
-
middleware = middleware.new if
|
47
|
-
middleware.is_a?(Class) && middleware <= AnyCable::Middleware
|
48
|
-
|
49
|
-
unless middleware.is_a?(AnyCable::Middleware)
|
50
|
-
raise ArgumentError,
|
51
|
-
"AnyCable middleware must be a subclass of AnyCable::Middleware, " \
|
52
|
-
"got #{middleware} instead"
|
53
|
-
end
|
54
|
-
|
55
|
-
middleware
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AnyCable
|
4
|
-
module Middlewares
|
5
|
-
# Checks that RPC client version is compatible 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
DELETED
@@ -1,84 +0,0 @@
|
|
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
|
-
|
39
|
-
def istate
|
40
|
-
env.istate
|
41
|
-
end
|
42
|
-
|
43
|
-
def istate=(val)
|
44
|
-
env.istate = val
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Status predicates
|
49
|
-
module StatusPredicates
|
50
|
-
def success?
|
51
|
-
status == :SUCCESS
|
52
|
-
end
|
53
|
-
|
54
|
-
def failure?
|
55
|
-
status == :FAILURE
|
56
|
-
end
|
57
|
-
|
58
|
-
def error?
|
59
|
-
status == :ERROR
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
class ConnectionResponse
|
64
|
-
prepend WithConnectionState
|
65
|
-
include StatusPredicates
|
66
|
-
end
|
67
|
-
|
68
|
-
class CommandMessage
|
69
|
-
prepend WithConnectionState
|
70
|
-
end
|
71
|
-
|
72
|
-
class CommandResponse
|
73
|
-
prepend WithConnectionState
|
74
|
-
include StatusPredicates
|
75
|
-
end
|
76
|
-
|
77
|
-
class DisconnectRequest
|
78
|
-
prepend WithConnectionState
|
79
|
-
end
|
80
|
-
|
81
|
-
class DisconnectResponse
|
82
|
-
include StatusPredicates
|
83
|
-
end
|
84
|
-
end
|
data/lib/anycable/rpc/rpc_pb.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
4
|
-
# source: rpc.proto
|
5
|
-
|
6
|
-
require "google/protobuf"
|
7
|
-
|
8
|
-
Google::Protobuf::DescriptorPool.generated_pool.build do
|
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
|
-
map :istate, :string, :string, 4
|
15
|
-
end
|
16
|
-
add_message "anycable.EnvResponse" do
|
17
|
-
map :cstate, :string, :string, 1
|
18
|
-
map :istate, :string, :string, 2
|
19
|
-
end
|
20
|
-
add_message "anycable.ConnectionRequest" do
|
21
|
-
optional :env, :message, 3, "anycable.Env"
|
22
|
-
end
|
23
|
-
add_message "anycable.ConnectionResponse" do
|
24
|
-
optional :status, :enum, 1, "anycable.Status"
|
25
|
-
optional :identifiers, :string, 2
|
26
|
-
repeated :transmissions, :string, 3
|
27
|
-
optional :error_msg, :string, 4
|
28
|
-
optional :env, :message, 5, "anycable.EnvResponse"
|
29
|
-
end
|
30
|
-
add_message "anycable.CommandMessage" do
|
31
|
-
optional :command, :string, 1
|
32
|
-
optional :identifier, :string, 2
|
33
|
-
optional :connection_identifiers, :string, 3
|
34
|
-
optional :data, :string, 4
|
35
|
-
optional :env, :message, 5, "anycable.Env"
|
36
|
-
end
|
37
|
-
add_message "anycable.CommandResponse" do
|
38
|
-
optional :status, :enum, 1, "anycable.Status"
|
39
|
-
optional :disconnect, :bool, 2
|
40
|
-
optional :stop_streams, :bool, 3
|
41
|
-
repeated :streams, :string, 4
|
42
|
-
repeated :transmissions, :string, 5
|
43
|
-
optional :error_msg, :string, 6
|
44
|
-
optional :env, :message, 7, "anycable.EnvResponse"
|
45
|
-
repeated :stopped_streams, :string, 8
|
46
|
-
end
|
47
|
-
add_message "anycable.DisconnectRequest" do
|
48
|
-
optional :identifiers, :string, 1
|
49
|
-
repeated :subscriptions, :string, 2
|
50
|
-
optional :env, :message, 5, "anycable.Env"
|
51
|
-
end
|
52
|
-
add_message "anycable.DisconnectResponse" do
|
53
|
-
optional :status, :enum, 1, "anycable.Status"
|
54
|
-
optional :error_msg, :string, 2
|
55
|
-
end
|
56
|
-
add_enum "anycable.Status" do
|
57
|
-
value :ERROR, 0
|
58
|
-
value :SUCCESS, 1
|
59
|
-
value :FAILURE, 2
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
module AnyCable
|
65
|
-
Env = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.Env").msgclass
|
66
|
-
EnvResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.EnvResponse").msgclass
|
67
|
-
ConnectionRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.ConnectionRequest").msgclass
|
68
|
-
ConnectionResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.ConnectionResponse").msgclass
|
69
|
-
CommandMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.CommandMessage").msgclass
|
70
|
-
CommandResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.CommandResponse").msgclass
|
71
|
-
DisconnectRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.DisconnectRequest").msgclass
|
72
|
-
DisconnectResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.DisconnectResponse").msgclass
|
73
|
-
Status = Google::Protobuf::DescriptorPool.generated_pool.lookup("anycable.Status").enummodule
|
74
|
-
end
|