aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/action_cable.rb33
-rw-r--r--lib/action_cable/channel.rb16
-rw-r--r--lib/action_cable/channel/base.rb8
-rw-r--r--lib/action_cable/connection.rb20
-rw-r--r--lib/action_cable/connection/base.rb34
-rw-r--r--lib/action_cable/connection/identification.rb12
-rw-r--r--lib/action_cable/connection/internal_channel.rb4
-rw-r--r--lib/action_cable/connection/message_buffer.rb4
-rw-r--r--lib/action_cable/connection/subscriptions.rb2
-rw-r--r--lib/action_cable/connection/web_socket.rb2
-rw-r--r--lib/action_cable/engine.rb18
-rw-r--r--lib/action_cable/server.rb20
-rw-r--r--lib/action_cable/server/base.rb4
-rw-r--r--lib/action_cable/server/broadcasting.rb6
-rw-r--r--lib/action_cable/server/configuration.rb2
-rw-r--r--lib/action_cable/server/connections.rb6
-rw-r--r--lib/action_cable/server/worker.rb5
17 files changed, 120 insertions, 76 deletions
diff --git a/lib/action_cable.rb b/lib/action_cable.rb
index d2c5251634..89ffa1fda7 100644
--- a/lib/action_cable.rb
+++ b/lib/action_cable.rb
@@ -1,32 +1,21 @@
-require 'eventmachine'
-EventMachine.epoll if EventMachine.epoll?
-EventMachine.kqueue if EventMachine.kqueue?
-
-require 'set'
-
require 'active_support'
-require 'active_support/json'
-require 'active_support/concern'
-require 'active_support/core_ext/hash/indifferent_access'
-require 'active_support/core_ext/module/delegation'
-require 'active_support/callbacks'
-
-require 'faye/websocket'
-require 'celluloid'
-require 'em-hiredis'
-require 'redis'
-
-require 'action_cable/engine' if defined?(Rails)
+require 'active_support/rails'
require 'action_cable/version'
module ActionCable
- autoload :Server, 'action_cable/server'
- autoload :Connection, 'action_cable/connection'
- autoload :Channel, 'action_cable/channel'
- autoload :RemoteConnections, 'action_cable/remote_connections'
+ extend ActiveSupport::Autoload
# Singleton instance of the server
module_function def server
@server ||= ActionCable::Server::Base.new
end
+
+ eager_autoload do
+ autoload :Server
+ autoload :Connection
+ autoload :Channel
+ autoload :RemoteConnections
+ end
end
+
+require 'action_cable/engine' if defined?(Rails)
diff --git a/lib/action_cable/channel.rb b/lib/action_cable/channel.rb
index 3b973ba0a7..7ae262ce5f 100644
--- a/lib/action_cable/channel.rb
+++ b/lib/action_cable/channel.rb
@@ -1,10 +1,14 @@
module ActionCable
module Channel
- autoload :Base, 'action_cable/channel/base'
- autoload :Broadcasting, 'action_cable/channel/broadcasting'
- autoload :Callbacks, 'action_cable/channel/callbacks'
- autoload :Naming, 'action_cable/channel/naming'
- autoload :PeriodicTimers, 'action_cable/channel/periodic_timers'
- autoload :Streams, 'action_cable/channel/streams'
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :Base
+ autoload :Broadcasting
+ autoload :Callbacks
+ autoload :Naming
+ autoload :PeriodicTimers
+ autoload :Streams
+ end
end
end
diff --git a/lib/action_cable/channel/base.rb b/lib/action_cable/channel/base.rb
index 2f1b4a187d..df87064195 100644
--- a/lib/action_cable/channel/base.rb
+++ b/lib/action_cable/channel/base.rb
@@ -1,6 +1,8 @@
+require 'set'
+
module ActionCable
module Channel
- # The channel provides the basic structure of grouping behavior into logical units when communicating over the websocket connection.
+ # The channel provides the basic structure of grouping behavior into logical units when communicating over the WebSocket connection.
# You can think of a channel like a form of controller, but one that's capable of pushing content to the subscriber in addition to simply
# responding to the subscriber's direct requests.
#
@@ -139,7 +141,6 @@ module ActionCable
# This method is not intended to be called directly by the user. Instead, overwrite the #unsubscribed callback.
def unsubscribe_from_channel
run_unsubscribe_callbacks
- logger.info "#{self.class.name} unsubscribed"
end
@@ -160,7 +161,7 @@ module ActionCable
# the proper channel identifier marked as the recipient.
def transmit(data, via: nil)
logger.info "#{self.class.name} transmitting #{data.inspect}".tap { |m| m << " (via #{via})" if via }
- connection.transmit({ identifier: @identifier, message: data }.to_json)
+ connection.transmit ActiveSupport::JSON.encode(identifier: @identifier, message: data)
end
@@ -175,7 +176,6 @@ module ActionCable
def subscribe_to_channel
- logger.info "#{self.class.name} subscribing"
run_subscribe_callbacks
end
diff --git a/lib/action_cable/connection.rb b/lib/action_cable/connection.rb
index 3d6ed6a6e8..b672e00682 100644
--- a/lib/action_cable/connection.rb
+++ b/lib/action_cable/connection.rb
@@ -1,12 +1,16 @@
module ActionCable
module Connection
- autoload :Authorization, 'action_cable/connection/authorization'
- autoload :Base, 'action_cable/connection/base'
- autoload :Identification, 'action_cable/connection/identification'
- autoload :InternalChannel, 'action_cable/connection/internal_channel'
- autoload :MessageBuffer, 'action_cable/connection/message_buffer'
- autoload :WebSocket, 'action_cable/connection/web_socket'
- autoload :Subscriptions, 'action_cable/connection/subscriptions'
- autoload :TaggedLoggerProxy, 'action_cable/connection/tagged_logger_proxy'
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :Authorization
+ autoload :Base
+ autoload :Identification
+ autoload :InternalChannel
+ autoload :MessageBuffer
+ autoload :WebSocket
+ autoload :Subscriptions
+ autoload :TaggedLoggerProxy
+ end
end
end
diff --git a/lib/action_cable/connection/base.rb b/lib/action_cable/connection/base.rb
index 2f2fa1fdec..9f74226f98 100644
--- a/lib/action_cable/connection/base.rb
+++ b/lib/action_cable/connection/base.rb
@@ -1,8 +1,8 @@
-require 'action_dispatch/http/request'
+require 'action_dispatch'
module ActionCable
module Connection
- # For every websocket the cable server is accepting, a Connection object will be instantiated. This instance becomes the parent
+ # For every WebSocket the cable server is accepting, a Connection object will be instantiated. This instance becomes the parent
# of all 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 cable consumer. The Connection itself does not deal with any specific application logic beyond
# authentication and authorization.
@@ -37,8 +37,8 @@ module ActionCable
# established for that current_user (and potentially disconnect them if the user was removed from an account). You can declare as many
# identification indexes as you like. Declaring an identification means that a 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.
+ # 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 name of the current user to easily distinguish their messages in the log.
#
@@ -65,7 +65,7 @@ module ActionCable
@started_at = Time.now
end
- # Called by the server when a new websocket connection is established. This configures the callbacks intended for overwriting by the user.
+ # Called by the server when a new WebSocket connection is established. This configures the callbacks intended for overwriting by the user.
# This method should not be called directly. Rely on the #connect (and #disconnect) callback instead.
def process
logger.info started_request_message
@@ -87,19 +87,18 @@ module ActionCable
if websocket.alive?
subscriptions.execute_command ActiveSupport::JSON.decode(data_in_json)
else
- logger.error "Received data without a live websocket (#{data.inspect})"
+ logger.error "Received data without a live WebSocket (#{data_in_json.inspect})"
end
end
- # Send raw data straight back down the websocket. This is not intended to be called directly. Use the #transmit available on the
+ # Send raw data straight back down the WebSocket. This is not intended to be called directly. Use the #transmit available on the
# Channel instead, as that'll automatically address the correct subscriber and wrap the message in JSON.
def transmit(data)
websocket.transmit data
end
- # Close the websocket connection.
+ # Close the WebSocket connection.
def close
- logger.error "Closing connection"
websocket.close
end
@@ -120,12 +119,12 @@ module ActionCable
end
def beat
- transmit({ identifier: '_ping', message: Time.now.to_i }.to_json)
+ transmit ActiveSupport::JSON.encode(identifier: '_ping', message: Time.now.to_i)
end
protected
- # The request that initiated the websocket connection is available here. This gives access to the environment, cookies, etc.
+ # The request that initiated the WebSocket connection is available here. This gives access to the environment, cookies, etc.
def request
@request ||= begin
environment = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
@@ -133,7 +132,7 @@ module ActionCable
end
end
- # The cookies of the request that initiated the websocket connection. Useful for performing authorization checks.
+ # The cookies of the request that initiated the WebSocket connection. Useful for performing authorization checks.
def cookies
request.cookie_jar
end
@@ -144,13 +143,12 @@ module ActionCable
attr_reader :subscriptions, :message_buffer
def on_open
- server.add_connection(self)
-
connect if respond_to?(:connect)
subscribe_to_internal_channel
beat
message_buffer.process!
+ server.add_connection(self)
rescue ActionCable::Connection::Authorization::UnauthorizedError
respond_to_invalid_request
close
@@ -203,17 +201,17 @@ module ActionCable
'Started %s "%s"%s for %s at %s' % [
request.request_method,
request.filtered_path,
- websocket.possible? ? ' [Websocket]' : '',
+ websocket.possible? ? ' [WebSocket]' : '',
request.ip,
- Time.now.to_default_s ]
+ Time.now.to_s ]
end
def finished_request_message
'Finished "%s"%s for %s at %s' % [
request.filtered_path,
- websocket.possible? ? ' [Websocket]' : '',
+ websocket.possible? ? ' [WebSocket]' : '',
request.ip,
- Time.now.to_default_s ]
+ Time.now.to_s ]
end
end
end
diff --git a/lib/action_cable/connection/identification.rb b/lib/action_cable/connection/identification.rb
index 4e9beac058..431493aa70 100644
--- a/lib/action_cable/connection/identification.rb
+++ b/lib/action_cable/connection/identification.rb
@@ -1,3 +1,5 @@
+require 'set'
+
module ActionCable
module Connection
module Identification
@@ -22,7 +24,7 @@ module ActionCable
# Return a single connection identifier that combines the value of all the registered identifiers into a single gid.
def connection_identifier
- if @connection_identifier.blank?
+ unless defined? @connection_identifier
@connection_identifier = connection_gid identifiers.map { |id| instance_variable_get("@#{id}") }.compact
end
@@ -31,7 +33,13 @@ module ActionCable
private
def connection_gid(ids)
- ids.map { |o| (o.try(:to_global_id) || o).to_s }.sort.join(":")
+ ids.map do |o|
+ if o.respond_to? :to_global_id
+ o.to_global_id
+ else
+ o.to_s
+ end
+ end.sort.join(":")
end
end
end
diff --git a/lib/action_cable/connection/internal_channel.rb b/lib/action_cable/connection/internal_channel.rb
index b00e21824c..c065a24ab7 100644
--- a/lib/action_cable/connection/internal_channel.rb
+++ b/lib/action_cable/connection/internal_channel.rb
@@ -15,14 +15,14 @@ module ActionCable
@_internal_redis_subscriptions ||= []
@_internal_redis_subscriptions << [ internal_redis_channel, callback ]
- pubsub.subscribe(internal_redis_channel, &callback)
+ EM.next_tick { pubsub.subscribe(internal_redis_channel, &callback) }
logger.info "Registered connection (#{connection_identifier})"
end
end
def unsubscribe_from_internal_channel
if @_internal_redis_subscriptions.present?
- @_internal_redis_subscriptions.each { |channel, callback| pubsub.unsubscribe_proc(channel, callback) }
+ @_internal_redis_subscriptions.each { |channel, callback| EM.next_tick { pubsub.unsubscribe_proc(channel, callback) } }
end
end
diff --git a/lib/action_cable/connection/message_buffer.rb b/lib/action_cable/connection/message_buffer.rb
index d5a8e9eba9..25cff75b41 100644
--- a/lib/action_cable/connection/message_buffer.rb
+++ b/lib/action_cable/connection/message_buffer.rb
@@ -1,6 +1,6 @@
module ActionCable
module Connection
- # Allows us to buffer messages received from the websocket before the Connection has been fully initialized and is ready to receive them.
+ # Allows us to buffer messages received from the WebSocket before the Connection has been fully initialized and is ready to receive them.
# Entirely internal operation and should not be used directly by the user.
class MessageBuffer
def initialize(connection)
@@ -50,4 +50,4 @@ module ActionCable
end
end
end
-end \ No newline at end of file
+end
diff --git a/lib/action_cable/connection/subscriptions.rb b/lib/action_cable/connection/subscriptions.rb
index 69e3f60706..229be2a316 100644
--- a/lib/action_cable/connection/subscriptions.rb
+++ b/lib/action_cable/connection/subscriptions.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/indifferent_access'
+
module ActionCable
module Connection
# Collection class for all the channel subscriptions established on a given connection. Responsible for routing incoming commands that arrive on
diff --git a/lib/action_cable/connection/web_socket.rb b/lib/action_cable/connection/web_socket.rb
index 135a28cfe4..169b683b8c 100644
--- a/lib/action_cable/connection/web_socket.rb
+++ b/lib/action_cable/connection/web_socket.rb
@@ -1,3 +1,5 @@
+require 'faye/websocket'
+
module ActionCable
module Connection
# Decorate the Faye::WebSocket with helpers we need.
diff --git a/lib/action_cable/engine.rb b/lib/action_cable/engine.rb
index 6c943c7971..613a9b99f2 100644
--- a/lib/action_cable/engine.rb
+++ b/lib/action_cable/engine.rb
@@ -1,4 +1,22 @@
+require 'rails/engine'
+require 'active_support/ordered_options'
+
module ActionCable
class Engine < ::Rails::Engine
+ config.action_cable = ActiveSupport::OrderedOptions.new
+
+ initializer "action_cable.logger" do
+ ActiveSupport.on_load(:action_cable) { self.logger ||= ::Rails.logger }
+ end
+
+ initializer "action_cable.set_configs" do |app|
+ options = app.config.action_cable
+
+ options.allowed_request_origins ||= "http://localhost:3000" if ::Rails.env.development?
+
+ ActiveSupport.on_load(:action_cable) do
+ options.each { |k,v| send("#{k}=", v) }
+ end
+ end
end
end
diff --git a/lib/action_cable/server.rb b/lib/action_cable/server.rb
index 2278509341..a2a89d5f1e 100644
--- a/lib/action_cable/server.rb
+++ b/lib/action_cable/server.rb
@@ -1,11 +1,19 @@
+require 'eventmachine'
+EventMachine.epoll if EventMachine.epoll?
+EventMachine.kqueue if EventMachine.kqueue?
+
module ActionCable
module Server
- autoload :Base, 'action_cable/server/base'
- autoload :Broadcasting, 'action_cable/server/broadcasting'
- autoload :Connections, 'action_cable/server/connections'
- autoload :Configuration, 'action_cable/server/configuration'
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :Base
+ autoload :Broadcasting
+ autoload :Connections
+ autoload :Configuration
- autoload :Worker, 'action_cable/server/worker'
- autoload :ActiveRecordConnectionManagement, 'action_cable/server/worker/active_record_connection_management'
+ autoload :Worker
+ autoload :ActiveRecordConnectionManagement, 'action_cable/server/worker/active_record_connection_management'
+ end
end
end
diff --git a/lib/action_cable/server/base.rb b/lib/action_cable/server/base.rb
index 9315a48f20..f1585dc776 100644
--- a/lib/action_cable/server/base.rb
+++ b/lib/action_cable/server/base.rb
@@ -1,3 +1,5 @@
+require 'em-hiredis'
+
module ActionCable
module Server
# A singleton ActionCable::Server instance is available via ActionCable.server. It's used by the rack process that starts the cable server, but
@@ -66,5 +68,7 @@ module ActionCable
config.connection_class.identifiers
end
end
+
+ ActiveSupport.run_load_hooks(:action_cable, Base.config)
end
end
diff --git a/lib/action_cable/server/broadcasting.rb b/lib/action_cable/server/broadcasting.rb
index 037b98951e..6e0fbae387 100644
--- a/lib/action_cable/server/broadcasting.rb
+++ b/lib/action_cable/server/broadcasting.rb
@@ -1,3 +1,5 @@
+require 'redis'
+
module ActionCable
module Server
# Broadcasting is how other parts of your application can send messages to the channel subscribers. As explained in Channel, most of the time, these
@@ -44,9 +46,9 @@ module ActionCable
def broadcast(message)
server.logger.info "[ActionCable] Broadcasting to #{broadcasting}: #{message}"
- server.broadcasting_redis.publish broadcasting, message.to_json
+ server.broadcasting_redis.publish broadcasting, ActiveSupport::JSON.encode(message)
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/lib/action_cable/server/configuration.rb b/lib/action_cable/server/configuration.rb
index 315782ec3e..b22de273b8 100644
--- a/lib/action_cable/server/configuration.rb
+++ b/lib/action_cable/server/configuration.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/indifferent_access'
+
module ActionCable
module Server
# An instance of this configuration object is available via ActionCable.server.config, which allows you to tweak the configuration points
diff --git a/lib/action_cable/server/connections.rb b/lib/action_cable/server/connections.rb
index b3d1632cf7..47dcea8c20 100644
--- a/lib/action_cable/server/connections.rb
+++ b/lib/action_cable/server/connections.rb
@@ -18,13 +18,13 @@ module ActionCable
connections.delete connection
end
- # Websocket connection implementations differ on when they'll mark a connection as stale. We basically never want a connection to go stale, as you
+ # WebSocket connection implementations differ on when they'll mark a connection as stale. We basically never want a connection to go stale, as you
# then can't rely on being able to receive and send to it. So there's a 3 second heartbeat running on all connections. If the beat fails, we automatically
# disconnect.
def setup_heartbeat_timer
EM.next_tick do
@heartbeat_timer ||= EventMachine.add_periodic_timer(BEAT_INTERVAL) do
- EM.next_tick { connections.map &:beat }
+ EM.next_tick { connections.map(&:beat) }
end
end
end
@@ -34,4 +34,4 @@ module ActionCable
end
end
end
-end \ No newline at end of file
+end
diff --git a/lib/action_cable/server/worker.rb b/lib/action_cable/server/worker.rb
index 91496775b8..e063b2a2e1 100644
--- a/lib/action_cable/server/worker.rb
+++ b/lib/action_cable/server/worker.rb
@@ -1,3 +1,6 @@
+require 'celluloid'
+require 'active_support/callbacks'
+
module ActionCable
module Server
# Worker used by Server.send_async to do connection work in threads. Only for internal use.
@@ -36,4 +39,4 @@ module ActionCable
end
end
end
-end \ No newline at end of file
+end