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:
commit
fc1b32f8d1
7 changed files with 24 additions and 19 deletions
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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})
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue