anycable-rails 1.4.0 → 1.4.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- metadata +10 -58
- data/lib/action_cable/subscription_adapter/any_cable.rb +0 -40
- data/lib/action_cable/subscription_adapter/anycable.rb +0 -10
- data/lib/anycable/rails/action_cable_ext/channel.rb +0 -51
- data/lib/anycable/rails/action_cable_ext/connection.rb +0 -90
- data/lib/anycable/rails/action_cable_ext/remote_connections.rb +0 -13
- data/lib/anycable/rails/channel_state.rb +0 -108
- data/lib/anycable/rails/compatibility/rubocop/config/default.yml +0 -14
- data/lib/anycable/rails/compatibility/rubocop/cops/anycable/instance_vars.rb +0 -50
- data/lib/anycable/rails/compatibility/rubocop/cops/anycable/periodical_timers.rb +0 -29
- data/lib/anycable/rails/compatibility/rubocop/cops/anycable/stream_from.rb +0 -100
- data/lib/anycable/rails/compatibility/rubocop.rb +0 -27
- data/lib/anycable/rails/compatibility.rb +0 -63
- data/lib/anycable/rails/config.rb +0 -19
- data/lib/anycable/rails/connection.rb +0 -211
- data/lib/anycable/rails/connection_factory.rb +0 -44
- data/lib/anycable/rails/connections/persistent_session.rb +0 -40
- data/lib/anycable/rails/connections/serializable_identification.rb +0 -46
- data/lib/anycable/rails/connections/session_proxy.rb +0 -81
- data/lib/anycable/rails/middlewares/executor.rb +0 -31
- data/lib/anycable/rails/middlewares/log_tagging.rb +0 -21
- data/lib/anycable/rails/rack.rb +0 -56
- data/lib/anycable/rails/railtie.rb +0 -92
- data/lib/anycable/rails/version.rb +0 -7
- data/lib/anycable/rails.rb +0 -76
- data/lib/anycable-rails.rb +0 -3
- data/lib/generators/anycable/download/USAGE +0 -14
- data/lib/generators/anycable/download/download_generator.rb +0 -85
- data/lib/generators/anycable/setup/USAGE +0 -2
- data/lib/generators/anycable/setup/setup_generator.rb +0 -300
- data/lib/generators/anycable/setup/templates/Procfile.dev.tt +0 -6
- data/lib/generators/anycable/setup/templates/config/anycable.yml.tt +0 -48
- data/lib/generators/anycable/setup/templates/config/cable.yml.tt +0 -11
- data/lib/generators/anycable/setup/templates/config/initializers/anycable.rb.tt +0 -9
- data/lib/generators/anycable/with_os_helpers.rb +0 -55
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AnyCable
|
4
|
-
class CompatibilityError < StandardError; end
|
5
|
-
|
6
|
-
module Compatibility # :nodoc:
|
7
|
-
IGNORE_INSTANCE_VARS = %i[
|
8
|
-
@active_periodic_timers
|
9
|
-
@_streams
|
10
|
-
@parameter_filter
|
11
|
-
]
|
12
|
-
|
13
|
-
ActionCable::Channel::Base.prepend(Module.new do
|
14
|
-
def stream_from(broadcasting, callback = nil, coder: nil)
|
15
|
-
if coder.present? && coder != ActiveSupport::JSON
|
16
|
-
raise AnyCable::CompatibilityError, "Custom coders are not supported by AnyCable"
|
17
|
-
end
|
18
|
-
|
19
|
-
if callback.present? || block_given?
|
20
|
-
raise AnyCable::CompatibilityError,
|
21
|
-
"Custom stream callbacks are not supported by AnyCable"
|
22
|
-
end
|
23
|
-
|
24
|
-
super
|
25
|
-
end
|
26
|
-
|
27
|
-
# Do not prepend `subscribe_to_channel` 'cause we make it no-op
|
28
|
-
# when AnyCable is running (see anycable/rails/actioncable/channel.rb)
|
29
|
-
%w[run_callbacks perform_action].each do |mid|
|
30
|
-
module_eval <<~CODE, __FILE__, __LINE__ + 1
|
31
|
-
def #{mid}(*)
|
32
|
-
__anycable_check_ivars__ { super }
|
33
|
-
end
|
34
|
-
CODE
|
35
|
-
end
|
36
|
-
|
37
|
-
def __anycable_check_ivars__
|
38
|
-
was_ivars = instance_variables
|
39
|
-
res = yield
|
40
|
-
diff = instance_variables - was_ivars - IGNORE_INSTANCE_VARS
|
41
|
-
|
42
|
-
if self.class.respond_to?(:channel_state_attributes)
|
43
|
-
diff.delete(:@__istate__)
|
44
|
-
diff.delete_if { |ivar| self.class.channel_state_attributes.include?(:"#{ivar.to_s.sub(/^@/, "")}") }
|
45
|
-
end
|
46
|
-
|
47
|
-
unless diff.empty?
|
48
|
-
raise AnyCable::CompatibilityError,
|
49
|
-
"Channel instance variables are not supported by AnyCable, " \
|
50
|
-
"but were set: #{diff.join(", ")}"
|
51
|
-
end
|
52
|
-
|
53
|
-
res
|
54
|
-
end
|
55
|
-
end)
|
56
|
-
|
57
|
-
ActionCable::Channel::Base.singleton_class.prepend(Module.new do
|
58
|
-
def periodically(*)
|
59
|
-
raise AnyCable::CompatibilityError, "Periodical timers are not supported by AnyCable"
|
60
|
-
end
|
61
|
-
end)
|
62
|
-
end
|
63
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "anycable/config"
|
4
|
-
# Make sure Rails extensions for Anyway Config are loaded
|
5
|
-
# See https://github.com/anycable/anycable-rails/issues/63
|
6
|
-
require "anyway/rails"
|
7
|
-
|
8
|
-
# Extend AnyCable configuration with:
|
9
|
-
# - `access_logs_disabled` (defaults to true) — whether to print Started/Finished logs
|
10
|
-
# - `persistent_session_enabled` (defaults to false) — whether to store session changes in the connection state
|
11
|
-
# - `embedded` (defaults to false) — whether to run RPC server inside a Rails server process
|
12
|
-
# - `http_rpc_mount_path` (default to nil) — path to mount HTTP RPC server
|
13
|
-
AnyCable::Config.attr_config(
|
14
|
-
access_logs_disabled: true,
|
15
|
-
persistent_session_enabled: false,
|
16
|
-
embedded: false,
|
17
|
-
http_rpc_mount_path: nil
|
18
|
-
)
|
19
|
-
AnyCable::Config.ignore_options :access_logs_disabled, :persistent_session_enabled
|
@@ -1,211 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "action_cable"
|
4
|
-
|
5
|
-
module AnyCable
|
6
|
-
module Rails
|
7
|
-
# Enhance Action Cable connection
|
8
|
-
using(Module.new do
|
9
|
-
refine ActionCable::Connection::Base do
|
10
|
-
attr_writer :env, :websocket, :logger, :coder,
|
11
|
-
:subscriptions, :serialized_ids, :cached_ids, :server,
|
12
|
-
:anycable_socket
|
13
|
-
|
14
|
-
# Using public :send_welcome_message causes stack level too deep 🤷🏻♂️
|
15
|
-
def send_welcome_message
|
16
|
-
transmit({
|
17
|
-
type: ActionCable::INTERNAL[:message_types][:welcome],
|
18
|
-
sid: env["anycable.sid"]
|
19
|
-
}.compact)
|
20
|
-
end
|
21
|
-
|
22
|
-
def public_request
|
23
|
-
request
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
refine ActionCable::Channel::Base do
|
28
|
-
def rejected?
|
29
|
-
subscription_rejected?
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
refine ActionCable::Connection::Subscriptions do
|
34
|
-
# Find or add a subscription to the list
|
35
|
-
def fetch(identifier)
|
36
|
-
add("identifier" => identifier) unless subscriptions[identifier]
|
37
|
-
|
38
|
-
unless subscriptions[identifier]
|
39
|
-
raise "Channel not found: #{ActiveSupport::JSON.decode(identifier).fetch("channel")}"
|
40
|
-
end
|
41
|
-
|
42
|
-
subscriptions[identifier]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end)
|
46
|
-
|
47
|
-
class Connection
|
48
|
-
# We store logger tags in the connection state to be able
|
49
|
-
# to re-use them in the subsequent calls
|
50
|
-
LOG_TAGS_IDENTIFIER = "__ltags__"
|
51
|
-
|
52
|
-
delegate :identifiers_json, to: :conn
|
53
|
-
|
54
|
-
attr_reader :socket, :logger
|
55
|
-
|
56
|
-
def initialize(connection_class, socket, identifiers: nil, subscriptions: nil)
|
57
|
-
@socket = socket
|
58
|
-
|
59
|
-
logger_tags = fetch_logger_tags_from_state
|
60
|
-
@logger = ActionCable::Connection::TaggedLoggerProxy.new(AnyCable.logger, tags: logger_tags)
|
61
|
-
|
62
|
-
# Instead of calling #initialize,
|
63
|
-
# we allocate an instance and setup all the required components manually
|
64
|
-
@conn = connection_class.allocate
|
65
|
-
# Required to access config (for access origin checks)
|
66
|
-
conn.server = ActionCable.server
|
67
|
-
conn.logger = logger
|
68
|
-
conn.anycable_socket = conn.websocket = socket
|
69
|
-
conn.env = socket.env
|
70
|
-
conn.coder = ActiveSupport::JSON
|
71
|
-
conn.subscriptions = ActionCable::Connection::Subscriptions.new(conn)
|
72
|
-
conn.serialized_ids = {}
|
73
|
-
conn.serialized_ids = ActiveSupport::JSON.decode(identifiers) if identifiers
|
74
|
-
conn.cached_ids = {}
|
75
|
-
conn.anycable_request_builder = self
|
76
|
-
|
77
|
-
return unless subscriptions
|
78
|
-
|
79
|
-
# Pre-initialize channels (for disconnect)
|
80
|
-
subscriptions.each do |id|
|
81
|
-
channel = conn.subscriptions.fetch(id)
|
82
|
-
next unless socket.istate[id]
|
83
|
-
|
84
|
-
channel.__istate__ = ActiveSupport::JSON.decode(socket.istate[id])
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def handle_open
|
89
|
-
logger.info started_request_message if access_logs?
|
90
|
-
|
91
|
-
verify_origin! || return
|
92
|
-
|
93
|
-
conn.connect if conn.respond_to?(:connect)
|
94
|
-
|
95
|
-
socket.cstate.write(LOG_TAGS_IDENTIFIER, logger.tags.to_json) unless logger.tags.empty?
|
96
|
-
|
97
|
-
conn.send_welcome_message
|
98
|
-
rescue ::ActionCable::Connection::Authorization::UnauthorizedError
|
99
|
-
reject_request(
|
100
|
-
ActionCable::INTERNAL[:disconnect_reasons]&.[](:unauthorized) || "unauthorized"
|
101
|
-
)
|
102
|
-
end
|
103
|
-
|
104
|
-
def handle_close
|
105
|
-
logger.info finished_request_message if access_logs?
|
106
|
-
|
107
|
-
conn.subscriptions.unsubscribe_from_all
|
108
|
-
conn.disconnect if conn.respond_to?(:disconnect)
|
109
|
-
true
|
110
|
-
end
|
111
|
-
|
112
|
-
def handle_channel_command(identifier, command, data)
|
113
|
-
conn.run_callbacks :command do
|
114
|
-
# We cannot use subscriptions#execute_command here,
|
115
|
-
# since we MUST return true of false, depending on the status
|
116
|
-
# of execution
|
117
|
-
channel = conn.subscriptions.fetch(identifier)
|
118
|
-
case command
|
119
|
-
when "subscribe"
|
120
|
-
channel.handle_subscribe
|
121
|
-
!channel.rejected?
|
122
|
-
when "unsubscribe"
|
123
|
-
conn.subscriptions.remove_subscription(channel)
|
124
|
-
true
|
125
|
-
when "message"
|
126
|
-
channel.perform_action ActiveSupport::JSON.decode(data)
|
127
|
-
true
|
128
|
-
else
|
129
|
-
false
|
130
|
-
end
|
131
|
-
end
|
132
|
-
# Support rescue_from
|
133
|
-
# https://github.com/rails/rails/commit/d2571e560c62116f60429c933d0c41a0e249b58b
|
134
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
135
|
-
rescue_with_handler(e) || raise
|
136
|
-
false
|
137
|
-
end
|
138
|
-
|
139
|
-
def build_rack_request(env)
|
140
|
-
environment = ::Rails.application.env_config.merge(env) if defined?(::Rails.application) && ::Rails.application
|
141
|
-
AnyCable::Rails::Rack.app.call(environment) if environment
|
142
|
-
|
143
|
-
ActionDispatch::Request.new(environment || env)
|
144
|
-
end
|
145
|
-
|
146
|
-
def action_cable_connection
|
147
|
-
conn
|
148
|
-
end
|
149
|
-
|
150
|
-
private
|
151
|
-
|
152
|
-
attr_reader :conn
|
153
|
-
|
154
|
-
def reject_request(reason, reconnect = false)
|
155
|
-
logger.info finished_request_message("Rejected") if access_logs?
|
156
|
-
conn.close(
|
157
|
-
reason: reason,
|
158
|
-
reconnect: reconnect
|
159
|
-
)
|
160
|
-
end
|
161
|
-
|
162
|
-
def fetch_logger_tags_from_state
|
163
|
-
socket.cstate.read(LOG_TAGS_IDENTIFIER).yield_self do |raw_tags|
|
164
|
-
next [] unless raw_tags
|
165
|
-
ActiveSupport::JSON.decode(raw_tags)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def started_request_message
|
170
|
-
format(
|
171
|
-
'Started "%s"%s for %s at %s',
|
172
|
-
request.filtered_path, " [AnyCable]", request.ip, Time.now.to_s
|
173
|
-
)
|
174
|
-
end
|
175
|
-
|
176
|
-
def finished_request_message(reason = "Closed")
|
177
|
-
format(
|
178
|
-
'Finished "%s"%s for %s at %s (%s)',
|
179
|
-
request.filtered_path, " [AnyCable]", request.ip, Time.now.to_s, reason
|
180
|
-
)
|
181
|
-
end
|
182
|
-
|
183
|
-
def verify_origin!
|
184
|
-
return true unless socket.env.key?("HTTP_ORIGIN")
|
185
|
-
|
186
|
-
return true if conn.send(:allow_request_origin?)
|
187
|
-
|
188
|
-
reject_request(
|
189
|
-
ActionCable::INTERNAL[:disconnect_reasons]&.[](:invalid_request) || "invalid_request"
|
190
|
-
)
|
191
|
-
false
|
192
|
-
end
|
193
|
-
|
194
|
-
def access_logs?
|
195
|
-
AnyCable.config.access_logs_disabled == false
|
196
|
-
end
|
197
|
-
|
198
|
-
def request
|
199
|
-
conn.public_request
|
200
|
-
end
|
201
|
-
|
202
|
-
def request_loaded?
|
203
|
-
conn.instance_variable_defined?(:@request)
|
204
|
-
end
|
205
|
-
|
206
|
-
def rescue_with_handler(e)
|
207
|
-
conn.rescue_with_handler(e) if conn.respond_to?(:rescue_with_handler)
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "anycable/rails/connection"
|
4
|
-
|
5
|
-
module AnyCable
|
6
|
-
module Rails
|
7
|
-
class ConnectionFactory
|
8
|
-
def initialize(&block)
|
9
|
-
@mappings = []
|
10
|
-
@use_router = false
|
11
|
-
instance_eval(&block) if block
|
12
|
-
end
|
13
|
-
|
14
|
-
def call(socket, **options)
|
15
|
-
connection_class = use_router? ? resolve_connection_class(socket.env) :
|
16
|
-
ActionCable.server.config.connection_class.call
|
17
|
-
|
18
|
-
AnyCable::Rails::Connection.new(connection_class, socket, **options)
|
19
|
-
end
|
20
|
-
|
21
|
-
def map(route, &block)
|
22
|
-
raise ArgumentError, "Block is required" unless block
|
23
|
-
|
24
|
-
@use_router = true
|
25
|
-
mappings << [route, block]
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
attr_reader :mappings, :use_router
|
31
|
-
alias_method :use_router?, :use_router
|
32
|
-
|
33
|
-
def resolve_connection_class(env)
|
34
|
-
path = env["PATH_INFO"]
|
35
|
-
|
36
|
-
mappings.each do |(prefix, resolver)|
|
37
|
-
return resolver.call if path.starts_with?(prefix)
|
38
|
-
end
|
39
|
-
|
40
|
-
raise "No connection class found matching #{path}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "anycable/rails/connections/session_proxy"
|
4
|
-
|
5
|
-
module AnyCable
|
6
|
-
module Rails
|
7
|
-
module Connections
|
8
|
-
module PersistentSession
|
9
|
-
def handle_open
|
10
|
-
super.tap { commit_session! }
|
11
|
-
end
|
12
|
-
|
13
|
-
def handle_channel_command(*)
|
14
|
-
super.tap { commit_session! }
|
15
|
-
end
|
16
|
-
|
17
|
-
def build_rack_request(env)
|
18
|
-
return super unless socket.session
|
19
|
-
|
20
|
-
super.tap do |req|
|
21
|
-
req.env[::Rack::RACK_SESSION] =
|
22
|
-
SessionProxy.new(req.env[::Rack::RACK_SESSION], socket.session)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def commit_session!
|
29
|
-
return unless request_loaded? && request.session.respond_to?(:loaded?) && request.session.loaded?
|
30
|
-
|
31
|
-
socket.session = request.session.to_json
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
AnyCable::Rails::Connection.prepend(
|
39
|
-
AnyCable::Rails::Connections::PersistentSession
|
40
|
-
)
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AnyCable
|
4
|
-
module Rails
|
5
|
-
module Connections
|
6
|
-
module SerializableIdentification
|
7
|
-
extend ActiveSupport::Concern
|
8
|
-
|
9
|
-
class_methods do
|
10
|
-
def identified_by(*identifiers)
|
11
|
-
super
|
12
|
-
Array(identifiers).each do |identifier|
|
13
|
-
define_method(identifier) do
|
14
|
-
instance_variable_get(:"@#{identifier}") || fetch_identifier(identifier)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# Generate identifiers info.
|
21
|
-
# Converts GlobalID compatible vars to corresponding global IDs params.
|
22
|
-
def identifiers_hash
|
23
|
-
identifiers.each_with_object({}) do |id, acc|
|
24
|
-
obj = instance_variable_get("@#{id}")
|
25
|
-
next unless obj
|
26
|
-
|
27
|
-
acc[id] = AnyCable::Rails.serialize(obj)
|
28
|
-
end.compact
|
29
|
-
end
|
30
|
-
|
31
|
-
def identifiers_json
|
32
|
-
identifiers_hash.to_json
|
33
|
-
end
|
34
|
-
|
35
|
-
# Fetch identifier and deserialize if neccessary
|
36
|
-
def fetch_identifier(name)
|
37
|
-
return unless @cached_ids
|
38
|
-
|
39
|
-
@cached_ids[name] ||= @cached_ids.fetch(name) do
|
40
|
-
AnyCable::Rails.deserialize(@serialized_ids[name.to_s])
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,81 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AnyCable
|
4
|
-
module Rails
|
5
|
-
module Connections
|
6
|
-
# Wrap `request.session` to lazily load values provided
|
7
|
-
# in the RPC call (set by the previous calls)
|
8
|
-
class SessionProxy
|
9
|
-
attr_reader :rack_session, :socket_session
|
10
|
-
|
11
|
-
def initialize(rack_session, socket_session)
|
12
|
-
@rack_session = rack_session
|
13
|
-
@socket_session = JSON.parse(socket_session).with_indifferent_access
|
14
|
-
end
|
15
|
-
|
16
|
-
%i[has_key? [] []= fetch delete dig].each do |mid|
|
17
|
-
class_eval <<~CODE, __FILE__, __LINE__ + 1
|
18
|
-
def #{mid}(*args, **kwargs, &block)
|
19
|
-
restore_key! args.first
|
20
|
-
rack_session.#{mid}(*args, **kwargs, &block)
|
21
|
-
end
|
22
|
-
CODE
|
23
|
-
end
|
24
|
-
|
25
|
-
alias_method :include?, :has_key?
|
26
|
-
alias_method :key?, :has_key?
|
27
|
-
|
28
|
-
%i[update merge! to_hash].each do |mid|
|
29
|
-
class_eval <<~CODE, __FILE__, __LINE__ + 1
|
30
|
-
def #{mid}(*args, **kwargs, &block)
|
31
|
-
restore!
|
32
|
-
rack_session.#{mid}(*args, **kwargs, &block)
|
33
|
-
end
|
34
|
-
CODE
|
35
|
-
end
|
36
|
-
|
37
|
-
alias_method :to_h, :to_hash
|
38
|
-
|
39
|
-
def keys
|
40
|
-
rack_session.keys + socket_session.keys
|
41
|
-
end
|
42
|
-
|
43
|
-
# Delegate both publuc and private methods to rack_session
|
44
|
-
def respond_to_missing?(name, include_private = false)
|
45
|
-
return false if name == :marshal_dump || name == :_dump
|
46
|
-
rack_session.respond_to?(name, include_private) || super
|
47
|
-
end
|
48
|
-
|
49
|
-
def method_missing(method, *args, &block)
|
50
|
-
if rack_session.respond_to?(method, true)
|
51
|
-
rack_session.send(method, *args, &block)
|
52
|
-
else
|
53
|
-
super
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# This method is used by StimulusReflex to obtain `@by`
|
58
|
-
def instance_variable_get(name)
|
59
|
-
super || rack_session.instance_variable_get(name)
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
def restore!
|
65
|
-
socket_session.keys.each(&method(:restore_key!))
|
66
|
-
end
|
67
|
-
|
68
|
-
def restore_key!(key)
|
69
|
-
return unless socket_session.key?(key)
|
70
|
-
val = socket_session.delete(key)
|
71
|
-
rack_session[key] =
|
72
|
-
if val.is_a?(String)
|
73
|
-
GlobalID::Locator.locate(val) || val
|
74
|
-
else
|
75
|
-
val
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AnyCable
|
4
|
-
module Rails
|
5
|
-
module Middlewares
|
6
|
-
# Executor runs Rails executor for each call
|
7
|
-
# See https://guides.rubyonrails.org/v5.2.0/threading_and_code_execution.html#framework-behavior
|
8
|
-
class Executor < AnyCable::Middleware
|
9
|
-
attr_reader :executor
|
10
|
-
|
11
|
-
def initialize(executor)
|
12
|
-
@executor = executor
|
13
|
-
end
|
14
|
-
|
15
|
-
def call(method, message, metadata)
|
16
|
-
if ::Rails.respond_to?(:error)
|
17
|
-
executor.wrap do
|
18
|
-
sid = metadata["sid"]
|
19
|
-
|
20
|
-
::Rails.error.record(context: {method: method, payload: message.to_h, sid: sid}) do
|
21
|
-
yield
|
22
|
-
end
|
23
|
-
end
|
24
|
-
else
|
25
|
-
executor.wrap { yield }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AnyCable
|
4
|
-
module Rails
|
5
|
-
module Middlewares
|
6
|
-
# Middleware to add `sid` (session ID) tag to logs.
|
7
|
-
#
|
8
|
-
# Session ID could be provided through gRPC metadata `sid` key.
|
9
|
-
#
|
10
|
-
# See https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md
|
11
|
-
class LogTagging < AnyCable::Middleware
|
12
|
-
def call(_method, _request, metadata)
|
13
|
-
sid = metadata["sid"]
|
14
|
-
return yield unless sid
|
15
|
-
|
16
|
-
AnyCable.logger.tagged("AnyCable sid=#{sid}") { yield }
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/lib/anycable/rails/rack.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "rails/configuration"
|
4
|
-
require "action_dispatch/middleware/stack"
|
5
|
-
|
6
|
-
module AnyCable
|
7
|
-
module Rails
|
8
|
-
# Rack middleware stack to modify the HTTP request object.
|
9
|
-
#
|
10
|
-
# AnyCable Websocket server does not use Rack middleware processing mechanism (which Rails uses
|
11
|
-
# when Action Cable is mounted into the main app).
|
12
|
-
#
|
13
|
-
# Some middlewares could enhance request env with useful information.
|
14
|
-
#
|
15
|
-
# For instance, consider the Rails session middleware: it's responsible for restoring the
|
16
|
-
# session data from cookies.
|
17
|
-
#
|
18
|
-
# AnyCable adds session middelware by default to its own stack.
|
19
|
-
#
|
20
|
-
# You can also use any Rack/Rails middleware you want. For example, to enable Devise/Warden
|
21
|
-
# you can add the following code to an initializer or any other configuration file:
|
22
|
-
#
|
23
|
-
# AnyCable::Rails::Rack.middleware.use Warden::Manager do |config|
|
24
|
-
# Devise.warden_config = config
|
25
|
-
# end
|
26
|
-
module Rack
|
27
|
-
def self.app_build_lock
|
28
|
-
@app_build_lock
|
29
|
-
end
|
30
|
-
|
31
|
-
@app_build_lock = Mutex.new
|
32
|
-
|
33
|
-
def self.middleware
|
34
|
-
@middleware ||= ::Rails::Configuration::MiddlewareStackProxy.new
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.default_middleware_stack
|
38
|
-
config = ::Rails.application.config
|
39
|
-
|
40
|
-
ActionDispatch::MiddlewareStack.new do |middleware|
|
41
|
-
middleware.use(config.session_store, config.session_options) if config.session_store
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.app
|
46
|
-
@rack_app || app_build_lock.synchronize do
|
47
|
-
@rack_app ||= default_middleware_stack.yield_self do |stack|
|
48
|
-
middleware.merge_into(stack)
|
49
|
-
end.yield_self do |stack|
|
50
|
-
stack.build { [-1, {}, []] }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|