class Pusher::Client
Constants
- DEFAULT_CLUSTER
- DEFAULT_CONNECT_TIMEOUT
CONFIGURATION ##
- DEFAULT_KEEP_ALIVE_TIMEOUT
- DEFAULT_RECEIVE_TIMEOUT
- DEFAULT_SEND_TIMEOUT
Attributes
Public Class Methods
Loads the configuration from an url in the environment
# File lib/pusher/client.rb, line 19 def self.from_env(key = 'PUSHER_URL') url = ENV[key] || raise(ConfigurationError, key) from_url(url) end
Loads the configuration from a url
# File lib/pusher/client.rb, line 25 def self.from_url(url) client = new client.url = url client end
# File lib/pusher/client.rb, line 31 def initialize(options = {}) @scheme = "https" @port = options[:port] || 443 if options.key?(:encrypted) warn "[DEPRECATION] `encrypted` is deprecated and will be removed in the next major version. Use `use_tls` instead." end if options[:use_tls] == false || options[:encrypted] == false @scheme = "http" @port = options[:port] || 80 end @app_id = options[:app_id] @key = options[:key] @secret = options[:secret] @host = options[:host] @host ||= "api-#{options[:cluster]}.pusher.com" unless options[:cluster].nil? || options[:cluster].empty? @host ||= "api-#{DEFAULT_CLUSTER}.pusher.com" @encryption_master_key = Base64.strict_decode64(options[:encryption_master_key_base64]) if options[:encryption_master_key_base64] @http_proxy = options[:http_proxy] # Default timeouts @connect_timeout = DEFAULT_CONNECT_TIMEOUT @send_timeout = DEFAULT_SEND_TIMEOUT @receive_timeout = DEFAULT_RECEIVE_TIMEOUT @keep_alive_timeout = DEFAULT_KEEP_ALIVE_TIMEOUT end
Public Instance Methods
Generate the expected response for an authentication endpoint. See pusher.com/docs/authenticating_users for details.
@example Private channels
render :json => Pusher.authenticate('private-my_channel', params[:socket_id])
@example Presence channels
render :json => Pusher.authenticate('presence-my_channel', params[:socket_id], { :user_id => current_user.id, # => required :user_info => { # => optional - for example :name => current_user.name, :email => current_user.email } })
@param socket_id [String] @param custom_data [Hash] used for example by private channels
@return [Hash]
@raise [Pusher::Error] if channel_name or socket_id are invalid
@private Custom data is sent to server as JSON-encoded string
# File lib/pusher/client.rb, line 347 def authenticate(channel_name, socket_id, custom_data = nil) channel_instance = channel(channel_name) r = channel_instance.authenticate(socket_id, custom_data) if channel_name.match(/^private-encrypted-/) r[:shared_secret] = Base64.strict_encode64( channel_instance.shared_secret(encryption_master_key) ) end r end
@private Returns the authentication token for the client
# File lib/pusher/client.rb, line 64 def authentication_token raise ConfigurationError, :key unless @key raise ConfigurationError, :secret unless @secret Pusher::Signature::Token.new(@key, @secret) end
Return a convenience channel object by name that delegates operations on a channel. No API request is made.
@example
Pusher['my-channel']
@return [Channel] @raise [Pusher::Error] if the channel name is invalid.
Channel names should be less than 200 characters, and should not contain anything other than letters, numbers, or the characters "_\-=@,.;"
# File lib/pusher/client.rb, line 221 def channel(channel_name) Channel.new(nil, channel_name, self) end
Request
info for a specific channel
@param channel_name [String] Channel
name (max 200 characters) @param params [Hash] Hash of parameters for the API - see REST API docs
@return [Hash] See Pusher
API docs
@raise [Pusher::Error] Unsuccessful response - see the error message @raise [Pusher::HTTPError] Error
raised inside http client. The original error is wrapped in error.original_error
# File lib/pusher/client.rb, line 254 def channel_info(channel_name, params = {}) get("/channels/#{channel_name}", params) end
Request
info for users of a presence channel
@param channel_name [String] Channel
name (max 200 characters) @param params [Hash] Hash of parameters for the API - see REST API docs
@return [Hash] See Pusher
API docs
@raise [Pusher::Error] Unsuccessful response - see the error message @raise [Pusher::HTTPError] Error
raised inside http client. The original error is wrapped in error.original_error
# File lib/pusher/client.rb, line 270 def channel_users(channel_name, params = {}) get("/channels/#{channel_name}/users", params) end
Request
a list of occupied channels from the API
GET /apps//channels
@param params [Hash] Hash of parameters for the API - see REST API docs
@return [Hash] See Pusher
API docs
@raise [Pusher::Error] Unsuccessful response - see the error message @raise [Pusher::HTTPError] Error
raised inside http client. The original error is wrapped in error.original_error
# File lib/pusher/client.rb, line 238 def channels(params = {}) get('/channels', params) end
# File lib/pusher/client.rb, line 125 def cluster=(cluster) cluster = DEFAULT_CLUSTER if cluster.nil? || cluster.empty? @host = "api-#{cluster}.pusher.com" end
@private Construct an em-http-request http client
# File lib/pusher/client.rb, line 373 def em_http_client(uri) begin unless defined?(EventMachine) && EventMachine.reactor_running? raise Error, "In order to use async calling you must be running inside an eventmachine loop" end require 'em-http' unless defined?(EventMachine::HttpRequest) connection_opts = { connect_timeout: @connect_timeout, inactivity_timeout: @receive_timeout, } if defined?(@proxy) proxy_opts = { host: @proxy[:host], port: @proxy[:port] } if @proxy[:user] proxy_opts[:authorization] = [@proxy[:user], @proxy[:password]] end connection_opts[:proxy] = proxy_opts end EventMachine::HttpRequest.new(uri, connection_opts) end end
Configure whether Pusher
API calls should be made over SSL (default false)
@example
Pusher.encrypted = true
# File lib/pusher/client.rb, line 115 def encrypted=(boolean) @scheme = boolean ? 'https' : 'http' # Configure port if it hasn't already been configured @port = boolean ? 443 : 80 end
# File lib/pusher/client.rb, line 121 def encrypted? @scheme == 'https' end
Set an encryption_master_key
to use with private-encrypted channels from a base64 encoded string.
# File lib/pusher/client.rb, line 139 def encryption_master_key_base64=(s) @encryption_master_key = s ? Base64.strict_decode64(s) : nil end
GET arbitrary REST API resource using a synchronous http client. All request signing is handled automatically.
@example
begin Pusher.get('/channels', filter_by_prefix: 'private-') rescue Pusher::Error => e # Handle error end
@param path [String] Path excluding /apps/APP_ID @param params [Hash] API params (see pusher.com/docs/rest_api)
@return [Hash] See Pusher
API docs
@raise [Pusher::Error] Unsuccessful response - see the error message @raise [Pusher::HTTPError] Error
raised inside http client. The original error is wrapped in error.original_error
# File lib/pusher/client.rb, line 167 def get(path, params = {}) resource(path).get(params) end
GET arbitrary REST API resource using an asynchronous http client. All request signing is handled automatically.
When the eventmachine reactor is running, the em-http-request gem is used; otherwise an async request is made using httpclient. See README for details and examples.
@param path [String] Path excluding /apps/APP_ID @param params [Hash] API params (see pusher.com/docs/rest_api)
@return Either an EM::DefaultDeferrable or a HTTPClient::Connection
# File lib/pusher/client.rb, line 183 def get_async(path, params = {}) resource(path).get_async(params) end
# File lib/pusher/client.rb, line 97 def http_proxy=(http_proxy) @http_proxy = http_proxy uri = URI.parse(http_proxy) @proxy = { scheme: uri.scheme, host: uri.host, port: uri.port, user: uri.user, password: uri.password } end
POST arbitrary REST API resource using a synchronous http client. Works identially to get method, but posts params as JSON in post body.
# File lib/pusher/client.rb, line 189 def post(path, params = {}) resource(path).post(params) end
POST arbitrary REST API resource using an asynchronous http client. Works identially to get_async
method, but posts params as JSON in post body.
# File lib/pusher/client.rb, line 196 def post_async(path, params = {}) resource(path).post_async(params) end
INTERACT WITH THE API ##
# File lib/pusher/client.rb, line 145 def resource(path) Resource.new(self, path) end
@private Construct a net/http http client
# File lib/pusher/client.rb, line 359 def sync_http_client require 'httpclient' @client ||= begin HTTPClient.new(@http_proxy).tap do |c| c.connect_timeout = @connect_timeout c.send_timeout = @send_timeout c.receive_timeout = @receive_timeout c.keep_alive_timeout = @keep_alive_timeout end end end
Convenience method to set all timeouts to the same value (in seconds). For more control, use the individual writers.
# File lib/pusher/client.rb, line 133 def timeout=(value) @connect_timeout, @send_timeout, @receive_timeout = value, value, value end
Trigger an event on one or more channels
POST /apps//events
@param channels [String or Array] 1-10 channel names @param event_name [String] @param data [Object] Event data to be triggered in javascript.
Objects other than strings will be converted to JSON
@param params [Hash] Additional parameters to send to api, e.g socket_id
@return [Hash] See Pusher
API docs
@raise [Pusher::Error] Unsuccessful response - see the error message @raise [Pusher::HTTPError] Error
raised inside http client. The original error is wrapped in error.original_error
# File lib/pusher/client.rb, line 289 def trigger(channels, event_name, data, params = {}) post('/events', trigger_params(channels, event_name, data, params)) end
Trigger an event on one or more channels asynchronously. For parameters see trigger
# File lib/pusher/client.rb, line 311 def trigger_async(channels, event_name, data, params = {}) post_async('/events', trigger_params(channels, event_name, data, params)) end
Trigger multiple events at the same time
POST /apps//batch_events
@param events [Array] List of events to publish
@return [Hash] See Pusher
API docs
@raise [Pusher::Error] Unsuccessful response - see the error message @raise [Pusher::HTTPError] Error
raised inside http client. The original error is wrapped in error.original_error
# File lib/pusher/client.rb, line 304 def trigger_batch(*events) post('/batch_events', trigger_batch_params(events.flatten)) end
Trigger multiple events asynchronously. For parameters see trigger_batch
# File lib/pusher/client.rb, line 318 def trigger_batch_async(*events) post_async('/batch_events', trigger_batch_params(events.flatten)) end
@private Builds a url for this app, optionally appending a path
# File lib/pusher/client.rb, line 71 def url(path = nil) raise ConfigurationError, :app_id unless @app_id URI::Generic.build({ scheme: @scheme, host: @host, port: @port, path: "/apps/#{@app_id}#{path}" }) end
Configure Pusher
connection by providing a url rather than specifying scheme, key, secret, and app_id
separately.
@example
Pusher.url = http://KEY:SECRET@api.pusherapp.com/apps/APP_ID
# File lib/pusher/client.rb, line 87 def url=(url) uri = URI.parse(url) @scheme = uri.scheme @app_id = uri.path.split('/').last @key = uri.user @secret = uri.password @host = uri.host @port = uri.port end
Convenience method for creating a new WebHook
instance for validating and extracting info from a received WebHook
@param request [Rack::Request] Either a Rack::Request or a Hash containing :key, :signature, :body, and optionally :content_type.
# File lib/pusher/client.rb, line 207 def webhook(request) WebHook.new(request, self) end
Private Instance Methods
# File lib/pusher/client.rb, line 462 def configured? host && scheme && key && secret && app_id end
JSON-encode the data if it's not a string
# File lib/pusher/client.rb, line 435 def encode_data(data) return data if data.is_a? String MultiJson.encode(data) end
Encrypts a message with a key derived from the master key and channel name
# File lib/pusher/client.rb, line 442 def encrypt(channel_name, encoded_data) raise ConfigurationError, :encryption_master_key unless @encryption_master_key # Only now load rbnacl, so that people that aren't using it don't need to # install libsodium require_rbnacl secret_box = RbNaCl::SecretBox.new( channel(channel_name).shared_secret(@encryption_master_key) ) nonce = RbNaCl::Random.random_bytes(secret_box.nonce_bytes) ciphertext = secret_box.encrypt(nonce, encoded_data) MultiJson.encode({ "nonce" => Base64::strict_encode64(nonce), "ciphertext" => Base64::strict_encode64(ciphertext), }) end
# File lib/pusher/client.rb, line 466 def require_rbnacl require 'rbnacl' rescue LoadError => e $stderr.puts "You don't have rbnacl installed in your application. Please add it to your Gemfile and run bundle install" raise e end
# File lib/pusher/client.rb, line 420 def trigger_batch_params(events) { batch: events.map do |event| event.dup.tap do |e| e[:data] = if e[:channel].match(/^private-encrypted-/) then encrypt(e[:channel], encode_data(e[:data])) else encode_data(e[:data]) end end end } end
# File lib/pusher/client.rb, line 402 def trigger_params(channels, event_name, data, params) channels = Array(channels).map(&:to_s) raise Pusher::Error, "Too many channels (#{channels.length}), max 10" if channels.length > 10 encoded_data = if channels.any?{ |c| c.match(/^private-encrypted-/) } then raise Pusher::Error, "Cannot trigger to multiple channels if any are encrypted" if channels.length > 1 encrypt(channels[0], encode_data(data)) else encode_data(data) end params.merge({ name: event_name, channels: channels, data: encoded_data, }) end