Class: PusherClient::Socket

Inherits:
Object
  • Object
show all
Defined in:
lib/pusher-client/socket.rb

Constant Summary collapse

CLIENT_ID =
'pusher-ruby-client'
PROTOCOL =
'6'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app_key, options = {}) ⇒ Socket

Returns a new instance of Socket.

Raises:

  • (ArgumentError)


13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/pusher-client/socket.rb', line 13

def initialize(app_key, options={})
  raise ArgumentError, "Missing app_key" if app_key.to_s.empty?

  @path = "#{options[:ws_path]}/app/#{app_key}?client=#{CLIENT_ID}&version=#{PusherClient::VERSION}&protocol=#{PROTOCOL}"
  @key = app_key.to_s
  @secret = options[:secret]
  @socket_id = nil
  @channels = Channels.new
  @global_channel = Channel.new('pusher_global_channel')
  @global_channel.global = true
  @connected = false
  @encrypted = options[:encrypted] || false
  @logger = options[:logger] || PusherClient.logger
  @private_auth_method = options[:private_auth_method]
  @cert_file = options[:cert_file]
  @ws_host = options[:ws_host] || HOST
  @ws_port = options[:ws_port] || WS_PORT
  @wss_port = options[:wss_port] || WSS_PORT
  @ssl_verify = options.fetch(:ssl_verify, true)

  if @encrypted
    @url = "wss://#{@ws_host}:#{@wss_port}#{@path}"
  else
    @url = "ws://#{@ws_host}:#{@ws_port}#{@path}"
  end

  bind('pusher:connection_established') do |data|
    socket = parser(data)
    @connected = true
    @socket_id = socket['socket_id']
    subscribe_all
  end

  bind('pusher:connection_disconnected') do |data|
    @connected = false
    @channels.channels.each { |c| c.disconnect }
  end

  bind('pusher:error') do |data|
    logger.fatal("Pusher : error : #{data.inspect}")
  end

  # Keep this in case we're using a websocket protocol that doesn't
  # implement ping/pong
  bind('pusher:ping') do
    send_event('pusher:pong', nil)
  end
end

Instance Attribute Details

#channelsObject (readonly)

Returns the value of attribute channels.



11
12
13
# File 'lib/pusher-client/socket.rb', line 11

def channels
  @channels
end

#connectedObject (readonly)

Returns the value of attribute connected.



11
12
13
# File 'lib/pusher-client/socket.rb', line 11

def connected
  @connected
end

#global_channelObject (readonly)

Returns the value of attribute global_channel.



11
12
13
# File 'lib/pusher-client/socket.rb', line 11

def global_channel
  @global_channel
end

#pathObject (readonly)

Returns the value of attribute path.



11
12
13
# File 'lib/pusher-client/socket.rb', line 11

def path
  @path
end

#socket_idObject (readonly)

Returns the value of attribute socket_id.



11
12
13
# File 'lib/pusher-client/socket.rb', line 11

def socket_id
  @socket_id
end

Instance Method Details

#[](channel_name) ⇒ Object



123
124
125
# File 'lib/pusher-client/socket.rb', line 123

def [](channel_name)
  @channels[channel_name] || NullChannel.new(channel_name)
end

#authorize(channel, callback) ⇒ Object

auth for private and presence



132
133
134
135
136
137
138
139
140
# File 'lib/pusher-client/socket.rb', line 132

def authorize(channel, callback)
  if is_private_channel(channel.name)
    auth_data = get_private_auth(channel)
  elsif is_presence_channel(channel.name)
    auth_data = get_presence_auth(channel)
  end
  # could both be nil if didn't require auth
  callback.call(channel, auth_data, channel.user_data)
end

#authorize_callback(channel, auth_data, channel_data) ⇒ Object



142
143
144
145
146
147
148
149
# File 'lib/pusher-client/socket.rb', line 142

def authorize_callback(channel, auth_data, channel_data)
  send_event('pusher:subscribe', {
    'channel' => channel.name,
    'auth' => auth_data,
    'channel_data' => channel_data
  })
  channel.acknowledge_subscription(nil)
end

#bind(event_name, &callback) ⇒ Object



118
119
120
121
# File 'lib/pusher-client/socket.rb', line 118

def bind(event_name, &callback)
  @global_channel.bind(event_name, &callback)
  return self
end

#connect(async = false) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/pusher-client/socket.rb', line 62

def connect(async = false)
  return if @connection
  logger.debug("Pusher : connecting : #{@url}")

  if async
    @connection_thread = Thread.new do
      begin
        connect_internal
      rescue => ex
        send_local_event "pusher:error", ex
      end
    end
  else
    connect_internal
  end
  self
end

#disconnectObject



80
81
82
83
84
85
86
87
88
89
90
# File 'lib/pusher-client/socket.rb', line 80

def disconnect
  return unless @connection
  logger.debug("Pusher : disconnecting")
  @connected = false
  @connection.close
  @connection = nil
  if @connection_thread
    @connection_thread.kill
    @connection_thread = nil
  end
end

#get_presence_auth(channel) ⇒ Object



169
170
171
172
173
# File 'lib/pusher-client/socket.rb', line 169

def get_presence_auth(channel)
  string_to_sign = @socket_id + ':' + channel.name + ':' + channel.user_data
  signature = hmac(@secret, string_to_sign)
  return "#{@key}:#{signature}"
end

#get_private_auth(channel) ⇒ Object



159
160
161
162
163
164
165
166
167
# File 'lib/pusher-client/socket.rb', line 159

def get_private_auth(channel)
  if @private_auth_method.nil?
    string_to_sign = @socket_id + ':' + channel.name
    signature = hmac(@secret, string_to_sign)
    return "#{@key}:#{signature}"
  else
    return @private_auth_method.call(@socket_id, channel)
  end
end

#is_presence_channel(channel_name) ⇒ Object



155
156
157
# File 'lib/pusher-client/socket.rb', line 155

def is_presence_channel(channel_name)
  channel_name.match(/^presence-/)
end

#is_private_channel(channel_name) ⇒ Object



151
152
153
# File 'lib/pusher-client/socket.rb', line 151

def is_private_channel(channel_name)
  channel_name.match(/^private-/)
end

#send_channel_event(channel, event_name, data) ⇒ Object



185
186
187
188
189
# File 'lib/pusher-client/socket.rb', line 185

def send_channel_event(channel, event_name, data)
  payload = {'channel' => channel, 'event' => event_name, 'data' => data}.to_json
  @connection.send(payload)
  logger.debug("Pusher : sending channel event : #{payload}")
end

#send_event(event_name, data) ⇒ Object



179
180
181
182
183
# File 'lib/pusher-client/socket.rb', line 179

def send_event(event_name, data)
  payload = {'event' => event_name, 'data' => data}.to_json
  @connection.send(payload)
  logger.debug("Pusher : sending event : #{payload}")
end

#subscribe(channel_name, user_data = nil) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/pusher-client/socket.rb', line 92

def subscribe(channel_name, user_data = nil)
  if user_data.is_a? Hash
    user_data = user_data.to_json
  elsif user_data
    user_data = {:user_id => user_data}.to_json
  elsif is_presence_channel(channel_name)
    raise ArgumentError, "user_data is required for presence channels"
  end

  channel = @channels.add(channel_name, user_data)
  if @connected
    authorize(channel, method(:authorize_callback))
  end
  return channel
end

#subscribe_allObject Also known as: subscribeAll



127
128
129
# File 'lib/pusher-client/socket.rb', line 127

def subscribe_all
  @channels.channels.clone.each { |k,v| subscribe(v.name, v.user_data) }
end

#unsubscribe(channel_name) ⇒ Object



108
109
110
111
112
113
114
115
116
# File 'lib/pusher-client/socket.rb', line 108

def unsubscribe(channel_name)
  channel = @channels.remove channel_name
  if channel && @connected
    send_event('pusher:unsubscribe', {
      'channel' => channel_name
    })
  end
  return channel
end