Merge branch 'master' into subscription-rejection
This commit is contained in:
commit
cb4ef80f20
|
@ -8,11 +8,15 @@ rvm:
|
||||||
|
|
||||||
gemfile:
|
gemfile:
|
||||||
- Gemfile
|
- Gemfile
|
||||||
- gemfiles/rails_4-2-stable.gemfile
|
- gemfiles/Gemfile.rails-4-2
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
allow_failures: ruby-head
|
allow_failures:
|
||||||
|
- ruby-head
|
||||||
|
|
||||||
|
services:
|
||||||
|
- redis-server
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/rack/rack.git
|
remote: git://github.com/rack/rack.git
|
||||||
revision: 6216a3f8a3560639ee1ddadc1e0d6bf9e5f31830
|
revision: 35599cfc2751e0ee611c0ff799924b8e7fe0c0b4
|
||||||
specs:
|
specs:
|
||||||
rack (2.0.0.alpha)
|
rack (2.0.0.alpha)
|
||||||
json
|
json
|
||||||
|
@ -13,7 +13,7 @@ GIT
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
remote: git://github.com/rails/rails.git
|
remote: git://github.com/rails/rails.git
|
||||||
revision: 960de47f0eef79d234eb3cfc47fabb470fef1529
|
revision: f94e328cf801fd5c8055b06c4ee5439273146833
|
||||||
specs:
|
specs:
|
||||||
actionpack (5.0.0.alpha)
|
actionpack (5.0.0.alpha)
|
||||||
actionview (= 5.0.0.alpha)
|
actionview (= 5.0.0.alpha)
|
||||||
|
@ -59,7 +59,7 @@ GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
builder (3.2.2)
|
builder (3.2.2)
|
||||||
celluloid (0.16.1)
|
celluloid (0.16.0)
|
||||||
timers (~> 4.0.0)
|
timers (~> 4.0.0)
|
||||||
coffee-rails (4.1.0)
|
coffee-rails (4.1.0)
|
||||||
coffee-script (>= 2.2.0)
|
coffee-script (>= 2.2.0)
|
||||||
|
@ -92,7 +92,7 @@ GEM
|
||||||
metaclass (~> 0.0.1)
|
metaclass (~> 0.0.1)
|
||||||
nokogiri (1.6.6.2)
|
nokogiri (1.6.6.2)
|
||||||
mini_portile (~> 0.6.0)
|
mini_portile (~> 0.6.0)
|
||||||
puma (2.12.2)
|
puma (2.14.0)
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails-deprecated_sanitizer (1.0.3)
|
rails-deprecated_sanitizer (1.0.3)
|
||||||
|
|
|
@ -63,6 +63,8 @@ module ApplicationCable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
Here `identified_by` is a connection identifier that can be used to find the specific connection again or later.
|
||||||
|
Note that anything marked as an identifier will automatically create a delegate by the same name on any channel instances created off the connection.
|
||||||
|
|
||||||
Then you should define your `ApplicationCable::Channel` class in Ruby. This is the place where you put
|
Then you should define your `ApplicationCable::Channel` class in Ruby. This is the place where you put
|
||||||
shared logic between your channels.
|
shared logic between your channels.
|
||||||
|
@ -317,7 +319,7 @@ application. The recommended basic setup is as follows:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# cable/config.ru
|
# cable/config.ru
|
||||||
require ::File.expand_path('../../config/environment', __FILE__)
|
require ::File.expand_path('../../config/environment', __FILE__)
|
||||||
Rails.application.eager_load!
|
Rails.application.eager_load!
|
||||||
|
|
||||||
require 'action_cable/process/logging'
|
require 'action_cable/process/logging'
|
||||||
|
@ -328,7 +330,7 @@ run ActionCable.server
|
||||||
Then you start the server using a binstub in bin/cable ala:
|
Then you start the server using a binstub in bin/cable ala:
|
||||||
```
|
```
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
bundle exec puma -p 28080 cable/config.ru
|
bundle exec puma -p 28080 cable/config.ru
|
||||||
```
|
```
|
||||||
|
|
||||||
The above will start a cable server on port 28080. Remember to point your client-side setup against that using something like:
|
The above will start a cable server on port 28080. Remember to point your client-side setup against that using something like:
|
||||||
|
|
|
@ -76,10 +76,7 @@ module ActionCable
|
||||||
SUBSCRIPTION_CONFIRMATION_INTERNAL_MESSAGE = 'confirm_subscription'.freeze
|
SUBSCRIPTION_CONFIRMATION_INTERNAL_MESSAGE = 'confirm_subscription'.freeze
|
||||||
SUBSCRIPTION_REJECTION_INTERNAL_MESSAGE = 'reject_subscription'.freeze
|
SUBSCRIPTION_REJECTION_INTERNAL_MESSAGE = 'reject_subscription'.freeze
|
||||||
|
|
||||||
on_subscribe :subscribed
|
attr_reader :params, :connection, ::identifier
|
||||||
on_unsubscribe :unsubscribed
|
|
||||||
|
|
||||||
attr_reader :params, :connection, :identifier
|
|
||||||
delegate :logger, to: :connection
|
delegate :logger, to: :connection
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
@ -147,7 +144,9 @@ module ActionCable
|
||||||
# Called by the cable connection when its cut so the channel has a chance to cleanup with callbacks.
|
# Called by the cable connection when its cut so the channel has a chance to cleanup with callbacks.
|
||||||
# This method is not intended to be called directly by the user. Instead, overwrite the #unsubscribed callback.
|
# This method is not intended to be called directly by the user. Instead, overwrite the #unsubscribed callback.
|
||||||
def unsubscribe_from_channel
|
def unsubscribe_from_channel
|
||||||
run_unsubscribe_callbacks
|
run_callbacks :unsubscribe do
|
||||||
|
unsubscribed
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -202,7 +201,9 @@ module ActionCable
|
||||||
|
|
||||||
|
|
||||||
def subscribe_to_channel
|
def subscribe_to_channel
|
||||||
run_subscribe_callbacks
|
run_callbacks :subscribe do
|
||||||
|
subscribed
|
||||||
|
end
|
||||||
|
|
||||||
if subscription_rejected?
|
if subscription_rejected?
|
||||||
reject_subscription
|
reject_subscription
|
||||||
|
@ -238,19 +239,10 @@ module ActionCable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_subscribe_callbacks
|
|
||||||
self.class.on_subscribe_callbacks.each { |callback| send(callback) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_unsubscribe_callbacks
|
|
||||||
self.class.on_unsubscribe_callbacks.each { |callback| send(callback) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def transmit_subscription_confirmation
|
def transmit_subscription_confirmation
|
||||||
unless subscription_confirmation_sent?
|
unless subscription_confirmation_sent?
|
||||||
logger.info "#{self.class.name} is transmitting the subscription confirmation"
|
logger.info "#{self.class.name} is transmitting the subscription confirmation"
|
||||||
connection.transmit ActiveSupport::JSON.encode(identifier: @identifier, type: SUBSCRIPTION_CONFIRMATION_INTERNAL_MESSAGE)
|
connection.transmit ActiveSupport::JSON.encode(identifier: @identifier, type: SUBSCRIPTION_CONFIRMATION_INTERNAL_MESSAGE)
|
||||||
|
|
||||||
@subscription_confirmation_sent = true
|
@subscription_confirmation_sent = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -264,7 +256,6 @@ module ActionCable
|
||||||
logger.info "#{self.class.name} is transmitting the subscription rejection"
|
logger.info "#{self.class.name} is transmitting the subscription rejection"
|
||||||
connection.transmit ActiveSupport::JSON.encode(identifier: @identifier, type: SUBSCRIPTION_REJECTION_INTERNAL_MESSAGE)
|
connection.transmit ActiveSupport::JSON.encode(identifier: @identifier, type: SUBSCRIPTION_REJECTION_INTERNAL_MESSAGE)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,28 +1,35 @@
|
||||||
|
require 'active_support/callbacks'
|
||||||
|
|
||||||
module ActionCable
|
module ActionCable
|
||||||
module Channel
|
module Channel
|
||||||
module Callbacks
|
module Callbacks
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
include ActiveSupport::Callbacks
|
||||||
|
|
||||||
included do
|
included do
|
||||||
class_attribute :on_subscribe_callbacks, :on_unsubscribe_callbacks, instance_reader: false
|
define_callbacks :subscribe
|
||||||
|
define_callbacks :unsubscribe
|
||||||
self.on_subscribe_callbacks = []
|
|
||||||
self.on_unsubscribe_callbacks = []
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
class_methods do
|
||||||
# Name methods that should be called when the channel is subscribed to.
|
def before_subscribe(*methods, &block)
|
||||||
# (These methods should be private, so they're not callable by the user).
|
set_callback(:subscribe, :before, *methods, &block)
|
||||||
def on_subscribe(*methods)
|
|
||||||
self.on_subscribe_callbacks += methods
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Name methods that should be called when the channel is unsubscribed from.
|
def after_subscribe(*methods, &block)
|
||||||
# (These methods should be private, so they're not callable by the user).
|
set_callback(:subscribe, :after, *methods, &block)
|
||||||
def on_unsubscribe(*methods)
|
|
||||||
self.on_unsubscribe_callbacks += methods
|
|
||||||
end
|
end
|
||||||
|
alias_method :on_subscribe, :after_subscribe
|
||||||
|
|
||||||
|
def before_unsubscribe(*methods, &block)
|
||||||
|
set_callback(:unsubscribe, :before, *methods, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_unsubscribe(*methods, &block)
|
||||||
|
set_callback(:unsubscribe, :after, *methods, &block)
|
||||||
|
end
|
||||||
|
alias_method :on_unsubscribe, :after_unsubscribe
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,8 +7,8 @@ module ActionCable
|
||||||
class_attribute :periodic_timers, instance_reader: false
|
class_attribute :periodic_timers, instance_reader: false
|
||||||
self.periodic_timers = []
|
self.periodic_timers = []
|
||||||
|
|
||||||
on_subscribe :start_periodic_timers
|
after_subscribe :start_periodic_timers
|
||||||
on_unsubscribe :stop_periodic_timers
|
after_unsubscribe :stop_periodic_timers
|
||||||
end
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
@ -38,4 +38,4 @@ module ActionCable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,7 +56,7 @@ module ActionCable
|
||||||
def initialize(server, env)
|
def initialize(server, env)
|
||||||
@server, @env = server, env
|
@server, @env = server, env
|
||||||
|
|
||||||
@logger = new_tagged_logger
|
@logger = new_tagged_logger || server.logger
|
||||||
|
|
||||||
@websocket = ActionCable::Connection::WebSocket.new(env)
|
@websocket = ActionCable::Connection::WebSocket.new(env)
|
||||||
@subscriptions = ActionCable::Connection::Subscriptions.new(self)
|
@subscriptions = ActionCable::Connection::Subscriptions.new(self)
|
||||||
|
@ -151,7 +151,6 @@ module ActionCable
|
||||||
server.add_connection(self)
|
server.add_connection(self)
|
||||||
rescue ActionCable::Connection::Authorization::UnauthorizedError
|
rescue ActionCable::Connection::Authorization::UnauthorizedError
|
||||||
respond_to_invalid_request
|
respond_to_invalid_request
|
||||||
close
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_message(message)
|
def on_message(message)
|
||||||
|
@ -186,6 +185,8 @@ module ActionCable
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond_to_invalid_request
|
def respond_to_invalid_request
|
||||||
|
close if websocket.alive?
|
||||||
|
|
||||||
logger.info finished_request_message
|
logger.info finished_request_message
|
||||||
[ 404, { 'Content-Type' => 'text/plain' }, [ 'Page not found' ] ]
|
[ 404, { 'Content-Type' => 'text/plain' }, [ 'Page not found' ] ]
|
||||||
end
|
end
|
||||||
|
@ -193,8 +194,10 @@ module ActionCable
|
||||||
|
|
||||||
# Tags are declared in the server but computed in the connection. This allows us per-connection tailored tags.
|
# Tags are declared in the server but computed in the connection. This allows us per-connection tailored tags.
|
||||||
def new_tagged_logger
|
def new_tagged_logger
|
||||||
TaggedLoggerProxy.new server.logger,
|
if server.logger.respond_to?(:tagged)
|
||||||
tags: server.config.log_tags.map { |tag| tag.respond_to?(:call) ? tag.call(request) : tag.to_s.camelize }
|
TaggedLoggerProxy.new server.logger,
|
||||||
|
tags: server.config.log_tags.map { |tag| tag.respond_to?(:call) ? tag.call(request) : tag.to_s.camelize }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def started_request_message
|
def started_request_message
|
||||||
|
|
|
@ -20,8 +20,8 @@ class ActionCable::Channel::BaseTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
class ChatChannel < BasicChannel
|
class ChatChannel < BasicChannel
|
||||||
attr_reader :room, :last_action
|
attr_reader :room, :last_action
|
||||||
on_subscribe :toggle_subscribed
|
after_subscribe :toggle_subscribed
|
||||||
on_unsubscribe :toggle_subscribed
|
after_unsubscribe :toggle_subscribed
|
||||||
|
|
||||||
def initialize(*)
|
def initialize(*)
|
||||||
@subscribed = false
|
@subscribed = false
|
||||||
|
|
|
@ -8,17 +8,25 @@ class ActionCable::Connection::AuthorizationTest < ActionCable::TestCase
|
||||||
def connect
|
def connect
|
||||||
reject_unauthorized_connection
|
reject_unauthorized_connection
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def send_async(method, *args)
|
||||||
|
# Bypass Celluloid
|
||||||
|
send method, *args
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "unauthorized connection" do
|
test "unauthorized connection" do
|
||||||
run_in_eventmachine do
|
run_in_eventmachine do
|
||||||
server = TestServer.new
|
server = TestServer.new
|
||||||
env = Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket'
|
server.config.allowed_request_origins = %w( http://rubyonrails.com )
|
||||||
|
|
||||||
|
env = Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket',
|
||||||
|
'HTTP_ORIGIN' => 'http://rubyonrails.com'
|
||||||
|
|
||||||
connection = Connection.new(server, env)
|
connection = Connection.new(server, env)
|
||||||
connection.websocket.expects(:close)
|
connection.websocket.expects(:close)
|
||||||
|
|
||||||
connection.process
|
connection.process
|
||||||
connection.send :on_open
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue