Class: ActionCable::Connection::Base

Inherits:
Object
  • Object
show all
Includes:
Authorization, Callbacks, Identification, InternalChannel, ActiveSupport::Rescuable
Defined in:
lib/action_cable/connection/base.rb

Overview

# Action Cable Connection Base

For every WebSocket connection the Action Cable server accepts, a Connection object will be instantiated. This instance becomes the parent of all of the channel subscriptions that are created from there on. Incoming messages are then routed to these channel subscriptions based on an identifier sent by the Action Cable consumer. The Connection itself does not deal with any specific application logic beyond authentication and authorization.

Here’s a basic example:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
      logger.add_tags current_user.name
    end

    def disconnect
      # Any cleanup work needed when the cable connection is cut.
    end

    private
      def find_verified_user
        User.find_by_identity(cookies.encrypted[:identity_id]) ||
          reject_unauthorized_connection
      end
  end
end

First, we declare that this connection can be identified by its current_user. This allows us to later be able to find all connections established for that current_user (and potentially disconnect them). You can declare as many identification indexes as you like. Declaring an identification means that an attr_accessor is automatically set for that key.

Second, we rely on the fact that the WebSocket connection is established with the cookies from the domain being sent along. This makes it easy to use signed cookies that were set when logging in via a web interface to authorize the WebSocket connection.

Finally, we add a tag to the connection-specific logger with the name of the current user to easily distinguish their messages in the log.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Authorization

#reject_unauthorized_connection

Methods included from Identification

#connection_identifier

Constructor Details

#initialize(server, socket) ⇒ Base

Returns a new instance of Base.



74
75
76
77
78
79
80
81
82
83
84
# File 'lib/action_cable/connection/base.rb', line 74

def initialize(server, socket)
  @server = server
  @socket = socket

  @logger = socket.logger
  @subscriptions  = Subscriptions.new(self)

  @_internal_subscriptions = nil

  @started_at = Time.now
end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



68
69
70
# File 'lib/action_cable/connection/base.rb', line 68

def logger
  @logger
end

#subscriptionsObject (readonly)

Returns the value of attribute subscriptions.



68
69
70
# File 'lib/action_cable/connection/base.rb', line 68

def subscriptions
  @subscriptions
end

Class Method Details

.__restore_visibility__Object



187
188
189
190
191
# File 'lib/action_cable/connection/base.rb', line 187

def __restore_visibility__
  %i[handle_open handle_close handle_channel_command transmit close].each do |method|
    instance_method(method).owner.send(:public, method)
  end
end

Instance Method Details

#beatObject



152
153
154
# File 'lib/action_cable/connection/base.rb', line 152

def beat
  transmit type: ActionCable::INTERNAL[:message_types][:ping], message: Time.now.to_i
end

#close(reason: nil, reconnect: true) ⇒ Object

Close the connection.



131
132
133
134
135
136
137
138
# File 'lib/action_cable/connection/base.rb', line 131

def close(reason: nil, reconnect: true)
  transmit(
    type: ActionCable::INTERNAL[:message_types][:disconnect],
    reason: reason,
    reconnect: reconnect
  )
  socket.close
end

#connectObject

This method is called every time an Action Cable client establishes an underlying connection. Override it in your class to define authentication logic and populate connection identifiers.



89
90
# File 'lib/action_cable/connection/base.rb', line 89

def connect
end

#disconnectObject

This method is called every time an Action Cable client disconnects. Override it in your class to cleanup the relevant application state (e.g., presence, online counts, etc.)



94
95
# File 'lib/action_cable/connection/base.rb', line 94

def disconnect
end

#handle_channel_command(payload) ⇒ Object Also known as: handle_incoming



112
113
114
115
116
117
118
# File 'lib/action_cable/connection/base.rb', line 112

def handle_channel_command(payload)
  run_callbacks :command do
    subscriptions.execute_command payload
  end
rescue Exception => e
  rescue_with_handler(e) || raise
end

#handle_closeObject



105
106
107
108
109
110
# File 'lib/action_cable/connection/base.rb', line 105

def handle_close
  subscriptions.unsubscribe_from_all
  unsubscribe_from_internal_channel

  disconnect
end

#handle_openObject



97
98
99
100
101
102
103
# File 'lib/action_cable/connection/base.rb', line 97

def handle_open
  connect
  subscribe_to_internal_channel
  send_welcome_message
rescue ActionCable::Connection::Authorization::UnauthorizedError
  close(reason: ActionCable::INTERNAL[:disconnect_reasons][:unauthorized], reconnect: false)
end

#inspectObject

:nodoc:



159
160
161
# File 'lib/action_cable/connection/base.rb', line 159

def inspect # :nodoc:
  "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
end

#raw_transmit(data) ⇒ Object

:nodoc:



126
127
128
# File 'lib/action_cable/connection/base.rb', line 126

def raw_transmit(data) # :nodoc:
  socket.raw_transmit(data)
end

#statisticsObject

Return a basic hash of statistics for the connection keyed with ‘identifier`, `started_at`, `subscriptions`, and `request_id`. This can be returned by a health check against the connection.



143
144
145
146
147
148
149
150
# File 'lib/action_cable/connection/base.rb', line 143

def statistics
  {
    identifier: connection_identifier,
    started_at: @started_at,
    subscriptions: subscriptions.identifiers,
    request_id: env["action_dispatch.request_id"]
  }
end

#transmit(data) ⇒ Object

:nodoc:



122
123
124
# File 'lib/action_cable/connection/base.rb', line 122

def transmit(data) # :nodoc:
  socket.transmit(data)
end