anycable-rails 1.0.7 → 1.3.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,222 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "action_cable/connection"
4
- require "anycable/rails/actioncable/connection/serializable_identification"
5
- require "anycable/rails/refinements/subscriptions"
6
- require "anycable/rails/actioncable/channel"
7
- require "anycable/rails/actioncable/remote_connections"
8
- require "anycable/rails/session_proxy"
9
-
10
- module ActionCable
11
- module Connection
12
- # rubocop: disable Metrics/ClassLength
13
- class Base # :nodoc:
14
- # We store logger tags in the connection state to be able
15
- # to re-use them in the subsequent calls
16
- LOG_TAGS_IDENTIFIER = "__ltags__"
17
-
18
- using AnyCable::Refinements::Subscriptions
19
-
20
- include SerializableIdentification
21
-
22
- attr_reader :socket
23
-
24
- alias_method :anycable_socket, :socket
25
-
26
- delegate :env, :session, to: :request
27
-
28
- class << self
29
- def call(socket, **options)
30
- new(socket, nil, **options)
31
- end
32
- end
33
-
34
- def initialize(socket, env, identifiers: "{}", subscriptions: nil)
35
- if env
36
- # If env is set, then somehow we're in the context of Action Cable
37
- # Return and print a warning in #process
38
- @request = ActionDispatch::Request.new(env)
39
- return
40
- end
41
-
42
- @ids = ActiveSupport::JSON.decode(identifiers)
43
-
44
- @ltags = socket.cstate.read(LOG_TAGS_IDENTIFIER).yield_self do |raw_tags|
45
- next unless raw_tags
46
- ActiveSupport::JSON.decode(raw_tags)
47
- end
48
-
49
- @cached_ids = {}
50
- @coder = ActiveSupport::JSON
51
- @socket = socket
52
- @subscriptions = ActionCable::Connection::Subscriptions.new(self)
53
-
54
- return unless subscriptions
55
-
56
- # Initialize channels (for disconnect)
57
- subscriptions.each do |id|
58
- channel = @subscriptions.fetch(id)
59
- next unless socket.istate[id]
60
-
61
- channel.__istate__ = ActiveSupport::JSON.decode(socket.istate[id])
62
- end
63
- end
64
-
65
- def process
66
- # Use Rails logger here to print to stdout in development
67
- logger.error invalid_request_message
68
- logger.info finished_request_message
69
- [404, {"Content-Type" => "text/plain"}, ["Page not found"]]
70
- end
71
-
72
- def invalid_request_message
73
- "You're trying to connect to Action Cable server while using AnyCable. " \
74
- "See https://docs.anycable.io/#/troubleshooting?id=server-raises-an-argumenterror-exception-when-client-tries-to-connect"
75
- end
76
-
77
- def handle_open
78
- logger.info started_request_message if access_logs?
79
-
80
- verify_origin! || return
81
-
82
- connect if respond_to?(:connect)
83
-
84
- socket.cstate.write(LOG_TAGS_IDENTIFIER, fetch_ltags.to_json)
85
-
86
- send_welcome_message
87
- rescue ActionCable::Connection::Authorization::UnauthorizedError
88
- reject_request(
89
- ActionCable::INTERNAL[:disconnect_reasons]&.[](:unauthorized) || "unauthorized"
90
- )
91
- end
92
-
93
- def handle_close
94
- logger.info finished_request_message if access_logs?
95
-
96
- subscriptions.unsubscribe_from_all
97
- disconnect if respond_to?(:disconnect)
98
- true
99
- end
100
-
101
- # rubocop:disable Metrics/MethodLength
102
- def handle_channel_command(identifier, command, data)
103
- channel = subscriptions.fetch(identifier)
104
- case command
105
- when "subscribe"
106
- channel.handle_subscribe
107
- !channel.subscription_rejected?
108
- when "unsubscribe"
109
- subscriptions.remove_subscription(channel)
110
- true
111
- when "message"
112
- channel.perform_action ActiveSupport::JSON.decode(data)
113
- true
114
- else
115
- false
116
- end
117
- end
118
- # rubocop:enable Metrics/MethodLength
119
-
120
- def close(reason: nil, reconnect: nil)
121
- transmit(
122
- type: ActionCable::INTERNAL[:message_types].fetch(:disconnect, "disconnect"),
123
- reason: reason,
124
- reconnect: reconnect
125
- )
126
- socket.close
127
- end
128
-
129
- def transmit(cable_message)
130
- socket.transmit encode(cable_message)
131
- end
132
-
133
- def logger
134
- @logger ||= TaggedLoggerProxy.new(AnyCable.logger, tags: ltags || [])
135
- end
136
-
137
- def request
138
- @request ||= build_rack_request
139
- end
140
-
141
- private
142
-
143
- attr_reader :ids, :ltags
144
-
145
- def started_request_message
146
- format(
147
- 'Started "%s"%s for %s at %s',
148
- request.filtered_path, " [AnyCable]", request.ip, Time.now.to_s
149
- )
150
- end
151
-
152
- def finished_request_message(reason = "Closed")
153
- format(
154
- 'Finished "%s"%s for %s at %s (%s)',
155
- request.filtered_path, " [AnyCable]", request.ip, Time.now.to_s, reason
156
- )
157
- end
158
-
159
- def access_logs?
160
- AnyCable.config.access_logs_disabled == false
161
- end
162
-
163
- def fetch_ltags
164
- if instance_variable_defined?(:@logger)
165
- logger.tags
166
- else
167
- ltags
168
- end
169
- end
170
-
171
- def server
172
- ActionCable.server
173
- end
174
-
175
- def verify_origin!
176
- return true unless socket.env.key?("HTTP_ORIGIN")
177
-
178
- return true if allow_request_origin?
179
-
180
- reject_request(
181
- ActionCable::INTERNAL[:disconnect_reasons]&.[](:invalid_request) || "invalid_request"
182
- )
183
- false
184
- end
185
-
186
- def reject_request(reason, reconnect = false)
187
- logger.info finished_request_message("Rejected") if access_logs?
188
- close(
189
- reason: reason,
190
- reconnect: reconnect
191
- )
192
- end
193
-
194
- def build_rack_request
195
- environment = Rails.application.env_config.merge(socket.env)
196
- AnyCable::Rails::Rack.app.call(environment)
197
-
198
- ActionDispatch::Request.new(environment)
199
- end
200
-
201
- def request_loaded?
202
- instance_variable_defined?(:@request)
203
- end
204
- end
205
- # rubocop:enable Metrics/ClassLength
206
- end
207
- end
208
-
209
- # Support rescue_from
210
- # https://github.com/rails/rails/commit/d2571e560c62116f60429c933d0c41a0e249b58b
211
- if ActionCable::Connection::Base.respond_to?(:rescue_from)
212
- ActionCable::Connection::Base.prepend(Module.new do
213
- def handle_channel_command(*)
214
- super
215
- rescue Exception => e # rubocop:disable Lint/RescueException
216
- rescue_with_handler(e) || raise
217
- false
218
- end
219
- end)
220
- end
221
-
222
- require "anycable/rails/actioncable/testing" if ::Rails.env.test?
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This file contains patches to Action Cable testing modules
4
-
5
- # Trigger autoload (if constant is defined)
6
- begin
7
- ActionCable::Channel::TestCase # rubocop:disable Lint/Void
8
- ActionCable::Connection::TestCase # rubocop:disable Lint/Void
9
- rescue NameError
10
- return
11
- end
12
-
13
- ActionCable::Channel::ChannelStub.prepend(Module.new do
14
- def subscribe_to_channel
15
- handle_subscribe
16
- end
17
- end)
18
-
19
- ActionCable::Channel::ConnectionStub.prepend(Module.new do
20
- def socket
21
- @socket ||= AnyCable::Socket.new(env: {})
22
- end
23
-
24
- alias_method :anycable_socket, :socket
25
- end)
26
-
27
- ActionCable::Connection::TestConnection.prepend(Module.new do
28
- def initialize(request)
29
- @request = request
30
- @cached_ids = {}
31
- super
32
- end
33
- end)
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AnyCable
4
- module Refinements
5
- module Subscriptions # :nodoc:
6
- refine ActionCable::Connection::Subscriptions do
7
- # Find or add a subscription to the list
8
- def fetch(identifier)
9
- add("identifier" => identifier) unless subscriptions[identifier]
10
-
11
- unless subscriptions[identifier]
12
- raise "Channel not found: #{ActiveSupport::JSON.decode(identifier).fetch("channel")}"
13
- end
14
-
15
- subscriptions[identifier]
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AnyCable
4
- module Rails
5
- # Wrap `request.session` to lazily load values provided
6
- # in the RPC call (set by the previous calls)
7
- class SessionProxy
8
- attr_reader :rack_session, :socket_session
9
-
10
- def initialize(rack_session, socket_session)
11
- @rack_session = rack_session
12
- @socket_session = JSON.parse(socket_session).with_indifferent_access
13
- end
14
-
15
- %i[has_key? [] []= fetch delete dig].each do |mid|
16
- class_eval <<~CODE, __FILE__, __LINE__ + 1
17
- def #{mid}(*args, **kwargs, &block)
18
- restore_key! args.first
19
- rack_session.#{mid}(*args, **kwargs, &block)
20
- end
21
- CODE
22
- end
23
-
24
- alias_method :include?, :has_key?
25
- alias_method :key?, :has_key?
26
-
27
- %i[update merge! to_hash].each do |mid|
28
- class_eval <<~CODE, __FILE__, __LINE__ + 1
29
- def #{mid}(*args, **kwargs, &block)
30
- restore!
31
- rack_session.#{mid}(*args, **kwargs, &block)
32
- end
33
- CODE
34
- end
35
-
36
- alias_method :to_h, :to_hash
37
-
38
- def keys
39
- rack_session.keys + socket_session.keys
40
- end
41
-
42
- # Delegate both publuc and private methods to rack_session
43
- def respond_to_missing?(name, include_private = false)
44
- return false if name == :marshal_dump || name == :_dump
45
- rack_session.respond_to?(name, include_private) || super
46
- end
47
-
48
- def method_missing(method, *args, &block)
49
- if rack_session.respond_to?(method, true)
50
- rack_session.send(method, *args, &block)
51
- else
52
- super
53
- end
54
- end
55
-
56
- # This method is used by StimulusReflex to obtain `@by`
57
- def instance_variable_get(name)
58
- super || rack_session.instance_variable_get(name)
59
- end
60
-
61
- private
62
-
63
- def restore!
64
- socket_session.keys.each(&method(:restore_key!))
65
- end
66
-
67
- def restore_key!(key)
68
- return unless socket_session.key?(key)
69
- val = socket_session.delete(key)
70
- rack_session[key] =
71
- if val.is_a?(String)
72
- GlobalID::Locator.locate(val) || val
73
- else
74
- val
75
- end
76
- end
77
- end
78
- end
79
- end