1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #23976 from danielrhodes/enhancement/ac-ping-to-message-type

ActionCable: Add a "welcome" and "ping" message type
This commit is contained in:
Matthew Draper 2016-03-02 19:16:28 +10:30
commit fc1b32f8d1
7 changed files with 24 additions and 19 deletions

View file

@ -73,8 +73,11 @@ class ActionCable.Connection
events: events:
message: (event) -> message: (event) ->
{identifier, message, type} = JSON.parse(event.data) {identifier, message, type} = JSON.parse(event.data)
switch type switch type
when message_types.welcome
@consumer.connectionMonitor.connected()
when message_types.ping
@consumer.connectionMonitor.ping()
when message_types.confirmation when message_types.confirmation
@consumer.subscriptions.notify(identifier, "connected") @consumer.subscriptions.notify(identifier, "connected")
when message_types.rejection when message_types.rejection

View file

@ -7,10 +7,7 @@ class ActionCable.ConnectionMonitor
@staleThreshold: 6 # Server::Connections::BEAT_INTERVAL * 2 (missed two pings) @staleThreshold: 6 # Server::Connections::BEAT_INTERVAL * 2 (missed two pings)
identifier: ActionCable.INTERNAL.identifiers.ping
constructor: (@consumer) -> constructor: (@consumer) ->
@consumer.subscriptions.add(this)
@start() @start()
connected: -> connected: ->
@ -22,11 +19,12 @@ class ActionCable.ConnectionMonitor
disconnected: -> disconnected: ->
@disconnectedAt = now() @disconnectedAt = now()
received: -> ping: ->
@pingedAt = now() @pingedAt = now()
reset: -> reset: ->
@reconnectAttempts = 0 @reconnectAttempts = 0
@consumer.connection.isOpen()
start: -> start: ->
@reset() @reset()

View file

@ -58,7 +58,5 @@ class ActionCable.Subscriptions
sendCommand: (subscription, command) -> sendCommand: (subscription, command) ->
{identifier} = subscription {identifier} = subscription
if identifier is ActionCable.INTERNAL.identifiers.ping @consumer.send({command, identifier})
@consumer.connection.isOpen()
else
@consumer.send({command, identifier})

View file

@ -29,10 +29,9 @@ module ActionCable
extend ActiveSupport::Autoload extend ActiveSupport::Autoload
INTERNAL = { INTERNAL = {
identifiers: {
ping: '_ping'.freeze
},
message_types: { message_types: {
welcome: 'welcome'.freeze,
ping: 'ping'.freeze,
confirmation: 'confirm_subscription'.freeze, confirmation: 'confirm_subscription'.freeze,
rejection: 'reject_subscription'.freeze rejection: 'reject_subscription'.freeze
} }

View file

@ -115,7 +115,7 @@ module ActionCable
end end
def beat def beat
transmit ActiveSupport::JSON.encode(identifier: ActionCable::INTERNAL[:identifiers][:ping], message: Time.now.to_i) transmit ActiveSupport::JSON.encode(type: ActionCable::INTERNAL[:message_types][:ping], message: Time.now.to_i)
end end
def on_open # :nodoc: def on_open # :nodoc:
@ -155,7 +155,7 @@ module ActionCable
def handle_open def handle_open
connect if respond_to?(:connect) connect if respond_to?(:connect)
subscribe_to_internal_channel subscribe_to_internal_channel
confirm_connection_monitor_subscription send_welcome_message
message_buffer.process! message_buffer.process!
server.add_connection(self) server.add_connection(self)
@ -174,11 +174,11 @@ module ActionCable
disconnect if respond_to?(:disconnect) disconnect if respond_to?(:disconnect)
end end
def confirm_connection_monitor_subscription def send_welcome_message
# Send confirmation message to the internal connection monitor channel. # Send welcome message to the internal connection monitor channel.
# This ensures the connection monitor state is reset after a successful # This ensures the connection monitor state is reset after a successful
# websocket connection. # websocket connection.
transmit ActiveSupport::JSON.encode(identifier: ActionCable::INTERNAL[:identifiers][:ping], type: ActionCable::INTERNAL[:message_types][:confirmation]) transmit ActiveSupport::JSON.encode(type: ActionCable::INTERNAL[:message_types][:welcome])
end end
def allow_request_origin? def allow_request_origin?

View file

@ -75,7 +75,7 @@ class ClientTest < ActionCable::TestCase
@ws.on(:message) do |event| @ws.on(:message) do |event|
hash = JSON.parse(event.data) hash = JSON.parse(event.data)
if hash['identifier'] == '_ping' if hash['type'] == 'ping'
@pings += 1 @pings += 1
else else
@messages << hash @messages << hash
@ -146,6 +146,7 @@ class ClientTest < ActionCable::TestCase
def test_single_client def test_single_client
with_puma_server do |port| with_puma_server do |port|
c = faye_client(port) c = faye_client(port)
assert_equal({"type" => "welcome"}, c.read_message) # pop the first welcome message off the stack
c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel') c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel')
assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message) assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message)
c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello') c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello')
@ -162,6 +163,7 @@ class ClientTest < ActionCable::TestCase
barrier_2 = Concurrent::CyclicBarrier.new(clients.size) barrier_2 = Concurrent::CyclicBarrier.new(clients.size)
clients.map {|c| Concurrent::Future.execute { clients.map {|c| Concurrent::Future.execute {
assert_equal({"type" => "welcome"}, c.read_message) # pop the first welcome message off the stack
c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel') c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel')
assert_equal({"identifier"=>'{"channel":"EchoChannel"}', "type"=>"confirm_subscription"}, c.read_message) assert_equal({"identifier"=>'{"channel":"EchoChannel"}', "type"=>"confirm_subscription"}, c.read_message)
c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello') c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello')
@ -181,6 +183,7 @@ class ClientTest < ActionCable::TestCase
clients = 100.times.map { faye_client(port) } clients = 100.times.map { faye_client(port) }
clients.map {|c| Concurrent::Future.execute { clients.map {|c| Concurrent::Future.execute {
assert_equal({"type" => "welcome"}, c.read_message) # pop the first welcome message off the stack
c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel') c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel')
assert_equal({"identifier"=>'{"channel":"EchoChannel"}', "type"=>"confirm_subscription"}, c.read_message) assert_equal({"identifier"=>'{"channel":"EchoChannel"}', "type"=>"confirm_subscription"}, c.read_message)
c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello') c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello')
@ -194,12 +197,14 @@ class ClientTest < ActionCable::TestCase
def test_disappearing_client def test_disappearing_client
with_puma_server do |port| with_puma_server do |port|
c = faye_client(port) c = faye_client(port)
assert_equal({"type" => "welcome"}, c.read_message) # pop the first welcome message off the stack
c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel') c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel')
assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message) assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message)
c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'delay', message: 'hello') c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'delay', message: 'hello')
c.close # disappear before write c.close # disappear before write
c = faye_client(port) c = faye_client(port)
assert_equal({"type" => "welcome"}, c.read_message) # pop the first welcome message off the stack
c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel') c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel')
assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message) assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message)
c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello') c.send_message command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello')
@ -214,6 +219,7 @@ class ClientTest < ActionCable::TestCase
identifier = JSON.dump(channel: 'EchoChannel') identifier = JSON.dump(channel: 'EchoChannel')
c = faye_client(port) c = faye_client(port)
assert_equal({"type" => "welcome"}, c.read_message)
c.send_message command: 'subscribe', identifier: identifier c.send_message command: 'subscribe', identifier: identifier
assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message) assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message)
assert_equal(1, app.connections.count) assert_equal(1, app.connections.count)
@ -232,6 +238,7 @@ class ClientTest < ActionCable::TestCase
def test_server_restart def test_server_restart
with_puma_server do |port| with_puma_server do |port|
c = faye_client(port) c = faye_client(port)
assert_equal({"type" => "welcome"}, c.read_message)
c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel') c.send_message command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel')
assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message) assert_equal({"identifier"=>"{\"channel\":\"EchoChannel\"}", "type"=>"confirm_subscription"}, c.read_message)

View file

@ -56,7 +56,7 @@ class ActionCable::Connection::BaseTest < ActionCable::TestCase
run_in_eventmachine do run_in_eventmachine do
connection = open_connection connection = open_connection
connection.websocket.expects(:transmit).with({ identifier: "_ping", type: "confirm_subscription" }.to_json) connection.websocket.expects(:transmit).with({ type: "welcome" }.to_json)
connection.message_buffer.expects(:process!) connection.message_buffer.expects(:process!)
connection.process connection.process