Class: AnyCable::Rails::Connection

Inherits:
Object
  • Object
show all
Includes:
AnyCable::Rails::Connections::PersistentSession
Defined in:
lib/anycable/rails/connection.rb,
lib/anycable/rails/next/connection.rb

Defined Under Namespace

Classes: Subscriptions

Constant Summary collapse

LOG_TAGS_IDENTIFIER =

We store logger tags in the connection state to be able to re-use them in the subsequent calls

"__ltags__"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection_class, socket, identifiers: nil, subscriptions: nil, server: ::ActionCable.server) ⇒ Connection

Returns a new instance of Connection.



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/anycable/rails/connection.rb', line 110

def initialize(connection_class, socket, identifiers: nil, subscriptions: nil)
  @socket = socket

  logger_tags = fetch_logger_tags_from_state
  @logger = ActionCable::Connection::TaggedLoggerProxy.new(AnyCable.logger, tags: logger_tags)

  # Instead of calling #initialize,
  # we allocate an instance and setup all the required components manually
  @conn = connection_class.allocate
  # Required to access config (for access origin checks)
  conn.server = ActionCable.server
  conn.logger = logger
  conn.anycable_socket = conn.websocket = socket
  conn.env = socket.env
  conn.coder = ActiveSupport::JSON
  conn.subscriptions = ActionCable::Connection::Subscriptions.new(conn)
  conn.serialized_ids = {}
  conn.serialized_ids = ActiveSupport::JSON.decode(identifiers) if identifiers
  conn.cached_ids = {}
  conn.anycable_request_builder = self

  # Pre-initialize channels (for disconnect)
  conn.subscriptions.restore(subscriptions, socket.istate) if subscriptions
end

Instance Attribute Details

#loggerObject (readonly)

Action Cable socket interface [BEGIN]



161
162
163
# File 'lib/anycable/rails/next/connection.rb', line 161

def logger
  @logger
end

#protocolObject (readonly)

Action Cable socket interface [BEGIN]



161
162
163
# File 'lib/anycable/rails/next/connection.rb', line 161

def protocol
  @protocol
end

#serverObject (readonly)

Returns the value of attribute server.



112
113
114
# File 'lib/anycable/rails/next/connection.rb', line 112

def server
  @server
end

#socketObject (readonly)

Returns the value of attribute socket.



108
109
110
# File 'lib/anycable/rails/connection.rb', line 108

def socket
  @socket
end

Instance Method Details

#action_cable_connectionObject



175
176
177
# File 'lib/anycable/rails/connection.rb', line 175

def action_cable_connection
  conn
end

#build_rack_request(env) ⇒ Object



168
169
170
171
172
173
# File 'lib/anycable/rails/connection.rb', line 168

def build_rack_request(env)
  environment = ::Rails.application.env_config.merge(env) if defined?(::Rails.application) && ::Rails.application
  AnyCable::Rails::Rack.app.call(environment) if environment

  ActionDispatch::Request.new(environment || env)
end

#closeObject



179
180
181
182
183
# File 'lib/anycable/rails/next/connection.rb', line 179

def close(...)
  return if socket.closed?
  logger.info finished_request_message if access_logs?
  socket.close(...)
end

#handle_channel_command(identifier, command, data) ⇒ Object



159
160
161
162
163
164
165
166
# File 'lib/anycable/rails/connection.rb', line 159

def handle_channel_command(identifier, command, data)
  conn.run_callbacks :command do
    conn.subscriptions.execute_rpc_command({"command" => command, "identifier" => identifier, "data" => data})
  end
rescue Exception => e # rubocop:disable Lint/RescueException
  rescue_with_handler(e) || raise
  false
end

#handle_closeObject



151
152
153
154
155
156
157
# File 'lib/anycable/rails/connection.rb', line 151

def handle_close
  logger.info finished_request_message if access_logs?

  conn.subscriptions.unsubscribe_from_all
  conn.disconnect if conn.respond_to?(:disconnect)
  true
end

#handle_openObject

AnyCable RPC interface [BEGIN] ==



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/anycable/rails/next/connection.rb', line 136

def handle_open
  logger.info started_request_message if access_logs?

  verify_origin! || return

  conn.connect if conn.respond_to?(:connect)

  socket.cstate.write(LOG_TAGS_IDENTIFIER, logger.tags.to_json) unless logger.tags.empty?

  conn.send_welcome_message
rescue ::ActionCable::Connection::Authorization::UnauthorizedError
  reject_request(
    ActionCable::INTERNAL[:disconnect_reasons]&.[](:unauthorized) || "unauthorized"
  )
end

#perform_work(receiver, method_name, *args) ⇒ Object

Raises:

  • (ArgumentError)


185
186
187
# File 'lib/anycable/rails/next/connection.rb', line 185

def perform_work(receiver, method_name, *args)
  raise ArgumentError, "Performing work is not supported within AnyCable"
end

#requestObject



227
228
229
# File 'lib/anycable/rails/connection.rb', line 227

def request
  conn.public_request
end

#transmit(data) ⇒ Object



175
176
177
# File 'lib/anycable/rails/next/connection.rb', line 175

def transmit(data)
  socket.transmit ActiveSupport::JSON.encode(data)
end