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.
@@ -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
@@ -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
@@ -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