anycable 1.0.3 → 1.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +1 -3
- metadata +17 -144
- 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 -135
- data/lib/anycable/exceptions_handling.rb +0 -35
- data/lib/anycable/health_server.rb +0 -73
- 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,135 +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_method :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_method :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? || redis_sentinels.empty?
|
102
|
-
|
103
|
-
sentinels = Array(redis_sentinels)
|
104
|
-
|
105
|
-
next if sentinels.empty?
|
106
|
-
|
107
|
-
params[:sentinels] = sentinels.map(&method(:parse_sentinel))
|
108
|
-
end.tap do |params|
|
109
|
-
next unless redis_url.match?(/rediss:\/\//)
|
110
|
-
|
111
|
-
params[:ssl_params] = {verify_mode: OpenSSL::SSL::VERIFY_NONE}
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Build HTTP health server parameters
|
116
|
-
def to_http_health_params
|
117
|
-
{
|
118
|
-
port: http_health_port,
|
119
|
-
path: http_health_path
|
120
|
-
}
|
121
|
-
end
|
122
|
-
|
123
|
-
private
|
124
|
-
|
125
|
-
def parse_sentinel(sentinel)
|
126
|
-
return sentinel.transform_keys!(&:to_sym) if sentinel.is_a?(Hash)
|
127
|
-
|
128
|
-
uri = URI.parse("redis://#{sentinel}")
|
129
|
-
|
130
|
-
{host: uri.host, port: uri.port}.tap do |opts|
|
131
|
-
opts[:password] = uri.password if uri.password
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
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_method :<<, :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,73 +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
|
-
begin
|
57
|
-
require "webrick"
|
58
|
-
rescue LoadError
|
59
|
-
raise "Please, install webrick gem to use health server"
|
60
|
-
end
|
61
|
-
|
62
|
-
WEBrick::HTTPServer.new(
|
63
|
-
Port: port,
|
64
|
-
Logger: logger,
|
65
|
-
AccessLog: []
|
66
|
-
).tap do |server|
|
67
|
-
server.mount_proc path do |_, res|
|
68
|
-
res.status, res.body = grpc_server.running? ? SUCCESS_RESPONSE : FAILURE_RESPONSE
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
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
|