anycable-rack-server 0.0.1 → 0.3.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,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'redis'
4
-
5
- module AnyCable
6
- module RackServer
7
- module BroadcastSubscribers
8
- class RedisSubscriber
9
- attr_reader :hub, :coder, :redis_conn, :threads
10
-
11
- def initialize(hub:, coder:, options:)
12
- @hub = hub
13
- @coder = coder
14
- @redis_conn = ::Redis.new(options)
15
- @threads = {}
16
- end
17
-
18
- def subscribe(channel)
19
- @threads[channel] = Thread.new do
20
- redis_conn.subscribe(channel) do |on|
21
- on.message { |_channel, msg| handle_message(msg) }
22
- end
23
- end
24
- end
25
-
26
- def unsubscribe(channel)
27
- @threads[channel].terminate unless @threads[channel].nil?
28
- @threads.delete(channel)
29
- end
30
-
31
- private
32
-
33
- def handle_message(msg)
34
- data = JSON.parse(msg)
35
- hub.broadcast(data['stream'], data['data'], coder)
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,189 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'anycable/rack-server/rpc/client'
4
- require 'anycable/rack-server/logging'
5
- require 'anycable/rack-server/errors'
6
-
7
- module AnyCable
8
- # rubocop:disable Metrics/LineLength
9
- module RackServer
10
- class Connection
11
- # rubocop:enable Metrics/LineLength
12
- include Logging
13
-
14
- attr_reader :coder,
15
- :header_names,
16
- :hub,
17
- :socket,
18
- :rpc_client,
19
- :server_id
20
-
21
- def initialize(socket, hub, coder, host, header_names, server_id)
22
- @socket = socket
23
- @coder = coder
24
- @hub = hub
25
- @header_names = header_names
26
- @server_id = server_id
27
-
28
- @rpc_client = RPC::Client.new(host)
29
-
30
- @_identifiers = '{}'
31
- @_subscriptions = Set.new
32
- end
33
-
34
- def handle_open
35
- response = rpc_connect
36
- process_open(response)
37
- end
38
-
39
- def handle_close
40
- response = rpc_disconnect
41
- process_close(response)
42
- reset_connection
43
- end
44
-
45
- def handle_command(websocket_message)
46
- decoded = decode(websocket_message)
47
- command = decoded.delete('command')
48
-
49
- channel_identifier = decoded['identifier']
50
-
51
- case command
52
- when 'subscribe' then subscribe(channel_identifier)
53
- when 'unsubscribe' then unsubscribe(channel_identifier)
54
- when 'message' then send_message(channel_identifier, decoded['data'])
55
- else
56
- raise Errors::UnknownCommand, "Command not found #{command}"
57
- end
58
- end
59
-
60
- private
61
-
62
- def transmit(cable_message)
63
- socket.transmit(encode(cable_message))
64
- end
65
-
66
- def close
67
- socket.close
68
- end
69
-
70
- def request
71
- socket.request
72
- end
73
-
74
- def request_path
75
- request.fullpath
76
- end
77
-
78
- def rpc_connect
79
- rpc_client.connect(headers: headers, path: request_path)
80
- end
81
-
82
- def rpc_disconnect
83
- rpc_client.disconnect(
84
- identifiers: @_identifiers,
85
- subscriptions: @_subscriptions.to_a,
86
- headers: headers,
87
- path: request_path
88
- )
89
- end
90
-
91
- def rpc_command(command, identifier, data = '')
92
- rpc_client.command(
93
- command: command,
94
- identifier: identifier,
95
- connection_identifiers: @_identifiers,
96
- data: data
97
- )
98
- end
99
-
100
- def subscribe(identifier)
101
- response = rpc_command('subscribe', identifier)
102
- if response.status == :SUCCESS
103
- @_subscriptions.add(identifier)
104
- else
105
- log(:debug, log_fmt("RPC subscribe command failed: #{response.inspect}"))
106
- end
107
- process_command(response, identifier)
108
- end
109
-
110
- def unsubscribe(identifier)
111
- response = rpc_command('unsubscribe', identifier)
112
- if response.status == :SUCCESS
113
- @_subscriptions.delete(identifier)
114
- else
115
- log(:debug, log_fmt("RPC unsubscribe command failed: #{response.inspect}"))
116
- end
117
- process_command(response, identifier)
118
- end
119
-
120
- def send_message(identifier, data)
121
- response = rpc_command('message', identifier, data)
122
- unless response.status == :SUCCESS
123
- log(:debug, log_fmt("RPC message command failed: #{response.inspect}"))
124
- end
125
- process_command(response, identifier)
126
- end
127
-
128
- def headers
129
- @headers ||= begin
130
- header_names.inject({}) do |acc, name|
131
- header_val = request.env["HTTP_#{name.gsub(/-/,'_').upcase}"]
132
- acc[name] = header_val unless header_val.nil? || header_val.empty?
133
- acc
134
- end
135
- end
136
- end
137
-
138
- def process_command(response, identifier)
139
- response.transmissions.each { |transmission| transmit(decode(transmission)) }
140
- hub.remove_channel(socket, identifier) if response.stop_streams
141
- response.streams.each { |stream| hub.add_subscriber(stream, socket, identifier) }
142
- close_connection if response.disconnect
143
- end
144
-
145
- def process_open(response)
146
- if response.status == :SUCCESS
147
- @_identifiers = response.identifiers
148
- response.transmissions.each { |transmission| transmit(decode(transmission)) }
149
- log(:debug) { log_fmt('Opened') }
150
- else
151
- log(:error, log_fmt("RPC connection command failed: #{response.inspect}"))
152
- close_connection
153
- end
154
- end
155
-
156
- def process_close(response)
157
- if response.status == :SUCCESS
158
- log(:debug) { log_fmt('Closed') }
159
- else
160
- log(:error, log_fmt("RPC disconnection command failed: #{response.inspect}"))
161
- end
162
- end
163
-
164
- def reset_connection
165
- @_identifiers = '{}'
166
- @_subscriptions = []
167
-
168
- hub.remove_socket(socket)
169
- end
170
-
171
- def close_connection
172
- reset_connection
173
- close
174
- end
175
-
176
- def encode(cable_message)
177
- coder.encode(cable_message)
178
- end
179
-
180
- def decode(websocket_message)
181
- coder.decode(websocket_message)
182
- end
183
-
184
- def log_fmt(msg)
185
- "[connection:#{server_id}] #{msg}"
186
- end
187
- end
188
- end
189
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'anycable/rack-server/connection'
4
- require 'anycable/rack-server/errors'
5
- require 'anycable/rack-server/socket'
6
-
7
- module AnyCable
8
- module RackServer
9
- class Middleware
10
- PROTOCOLS = ['actioncable-v1-json', 'actioncable-unsupported'].freeze
11
- attr_reader :pinger,
12
- :hub,
13
- :coder,
14
- :rpc_host,
15
- :headers,
16
- :server_id
17
-
18
- def initialize(_app, pinger:, hub:, coder:, rpc_host:, headers:, server_id:)
19
- @pinger = pinger
20
- @hub = hub
21
- @coder = coder
22
- @rpc_host = rpc_host
23
- @headers = headers
24
- @server_id = server_id
25
- end
26
-
27
- def call(env)
28
- return not_found unless websocket?(env)
29
-
30
- rack_hijack(env)
31
- listen_socket(env)
32
-
33
- [-1, {}, []]
34
- end
35
-
36
- private
37
-
38
- def handshake
39
- @handshake ||= WebSocket::Handshake::Server.new(protocols: PROTOCOLS)
40
- end
41
-
42
- def rack_hijack(env)
43
- raise Errors::HijackNotAvailable unless env['rack.hijack']
44
-
45
- env['rack.hijack'].call
46
- send_handshake(env)
47
- end
48
-
49
- def send_handshake(env)
50
- handshake.from_rack(env)
51
- env['rack.hijack_io'].write(handshake.to_s)
52
- end
53
-
54
- def listen_socket(env)
55
- socket = Socket.new(env, env['rack.hijack_io'], handshake.version)
56
- init_connection(socket)
57
- init_pinger(socket)
58
- socket.listen
59
- end
60
-
61
- def not_found
62
- [404, { 'Content-Type' => 'text/plain' }, ['Not Found']]
63
- end
64
-
65
- def websocket?(env)
66
- env['HTTP_UPGRADE'] == 'websocket'
67
- end
68
-
69
- def init_connection(socket)
70
- connection = Connection.new(socket, hub, coder, rpc_host, headers, server_id)
71
- socket.onopen { connection.handle_open }
72
- socket.onclose { connection.handle_close }
73
- socket.onmessage { |data| connection.handle_command(data) }
74
- end
75
-
76
- def init_pinger(socket)
77
- pinger.add(socket)
78
- socket.onclose { pinger.remove(socket) }
79
- end
80
- end
81
- end
82
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'grpc'
4
-
5
- module AnyCable
6
- module RackServer
7
- module RPC
8
- class Client
9
- attr_reader :stub
10
-
11
- def initialize(host)
12
- @stub = AnyCable::RPC::Service.rpc_stub_class.new(host, :this_channel_is_insecure)
13
- end
14
-
15
- def connect(headers:, path:)
16
- request = ConnectionRequest.new(headers: headers, path: path)
17
- stub.connect(request)
18
- end
19
-
20
- def command(command:, identifier:, connection_identifiers:, data:)
21
- message = CommandMessage.new(
22
- command: command,
23
- identifier: identifier,
24
- connection_identifiers: connection_identifiers,
25
- data: data
26
- )
27
- stub.command(message)
28
- end
29
-
30
- def disconnect(identifiers:, subscriptions:, headers:, path:)
31
- request = DisconnectRequest.new(
32
- identifiers: identifiers,
33
- subscriptions: subscriptions,
34
- headers: headers,
35
- path: path
36
- )
37
- stub.disconnect(request)
38
- end
39
- end
40
- end
41
- end
42
- end