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