Class: EventMachine::WebSocket::Connection

Inherits:
Connection
  • Object
show all
Includes:
Debugger
Defined in:
lib/em-websocket/connection.rb

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Connection

Returns a new instance of Connection.



37
38
39
40
41
42
43
44
45
# File 'lib/em-websocket/connection.rb', line 37

def initialize(options)
  @options = options
  @debug = options[:debug] || false
  @secure = options[:secure] || false
  @tls_options = options[:tls_options] || {}
  @data = ''

  debug [:initialize]
end

Instance Method Details

#close_websocket(code = nil, body = nil) ⇒ Object

Use this method to close the websocket connection cleanly This sends a close frame and waits for acknowlegement before closing the connection



50
51
52
53
54
55
56
57
58
59
# File 'lib/em-websocket/connection.rb', line 50

def close_websocket(code = nil, body = nil)
  if code && !(4000..4999).include?(code)
    raise "Application code may only use codes in the range 4000-4999"
  end

  # If code not defined then set to 1000 (normal closure)
  code ||= 1000

  close_websocket_private(code, body)
end

#dispatch(data) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/em-websocket/connection.rb', line 101

def dispatch(data)
  if data.match(/\A<policy-file-request\s*\/>/)
    send_flash_cross_domain_file
    return false
  else
    debug [:inbound_headers, data]
    @data << data
    @handler = HandlerFactory.build(self, @data, @secure, @debug)
    unless @handler
      # The whole header has not been received yet.
      return false
    end
    @data = nil
    @handler.run
    return true
  end
end

#onclose(&blk) ⇒ Object



10
# File 'lib/em-websocket/connection.rb', line 10

def onclose(&blk);    @onclose = blk;   end

#onerror(&blk) ⇒ Object



11
# File 'lib/em-websocket/connection.rb', line 11

def onerror(&blk);    @onerror = blk;   end

#onmessage(&blk) ⇒ Object



12
# File 'lib/em-websocket/connection.rb', line 12

def onmessage(&blk);  @onmessage = blk; end

#onopen(&blk) ⇒ Object

define WebSocket callbacks



9
# File 'lib/em-websocket/connection.rb', line 9

def onopen(&blk);     @onopen = blk;    end

#onping(&blk) ⇒ Object



13
# File 'lib/em-websocket/connection.rb', line 13

def onping(&blk);     @onping = blk;    end

#onpong(&blk) ⇒ Object



14
# File 'lib/em-websocket/connection.rb', line 14

def onpong(&blk);     @onpong = blk;    end

#ping(body = '') ⇒ Object

Send a ping to the client. The client must respond with a pong.

In the case that the client is running a WebSocket draft < 01, false is returned since ping & pong are not supported



156
157
158
159
160
161
162
# File 'lib/em-websocket/connection.rb', line 156

def ping(body = '')
  if @handler
    @handler.pingable? ? @handler.send_frame(:ping, body) && true : false
  else
    raise WebSocketError, "Cannot ping before onopen callback"
  end
end

#pingable?Boolean

Test whether the connection is pingable (i.e. the WebSocket draft in use is >= 01)

Returns:

  • (Boolean)


180
181
182
183
184
185
186
# File 'lib/em-websocket/connection.rb', line 180

def pingable?
  if @handler
    @handler.pingable?
  else
    raise WebSocketError, "Cannot test whether pingable before onopen callback"
  end
end

#pong(body = '') ⇒ Object

Send an unsolicited pong message, as allowed by the protocol. The client is not expected to respond to this message.

em-websocket automatically takes care of sending pong replies to incoming ping messages, as the protocol demands.



170
171
172
173
174
175
176
# File 'lib/em-websocket/connection.rb', line 170

def pong(body = '')
  if @handler
    @handler.pingable? ? @handler.send_frame(:pong, body) && true : false
  else
    raise WebSocketError, "Cannot ping before onopen callback"
  end
end

#post_initObject



61
62
63
# File 'lib/em-websocket/connection.rb', line 61

def post_init
  start_tls(@tls_options) if @secure
end

#receive_data(data) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/em-websocket/connection.rb', line 65

def receive_data(data)
  debug [:receive_data, data]

  if @handler
    @handler.receive_data(data)
  else
    dispatch(data)
  end
rescue HandshakeError => e
  debug [:error, e]
  trigger_on_error(e)
  # Errors during the handshake require the connection to be aborted
  abort
rescue WebSocketError => e
  debug [:error, e]
  trigger_on_error(e)
  close_websocket_private(1002) # 1002 indicates a protocol error
rescue => e
  debug [:error, e]
  # These are application errors - raise unless onerror defined
  trigger_on_error(e) || raise(e)
  # There is no code defined for application errors, so use 3000
  # (which is reserved for frameworks)
  close_websocket_private(3000)
end

#requestObject



188
189
190
# File 'lib/em-websocket/connection.rb', line 188

def request
  @handler ? @handler.request : {}
end

#send(data) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/em-websocket/connection.rb', line 130

def send(data)
  # If we're using Ruby 1.9, be pedantic about encodings
  if data.respond_to?(:force_encoding)
    # Also accept ascii only data in other encodings for convenience
    unless (data.encoding == Encoding.find("UTF-8") && data.valid_encoding?) || data.ascii_only?
      raise WebSocketError, "Data sent to WebSocket must be valid UTF-8 but was #{data.encoding} (valid: #{data.valid_encoding?})"
    end
    # This labels the encoding as binary so that it can be combined with
    # the BINARY framing
    data.force_encoding("BINARY")
  else
    # TODO: Check that data is valid UTF-8
  end

  if @handler
    @handler.send_text_frame(data)
  else
    raise WebSocketError, "Cannot send data before onopen callback"
  end
end

#send_flash_cross_domain_fileObject



119
120
121
122
123
124
125
126
127
128
# File 'lib/em-websocket/connection.rb', line 119

def send_flash_cross_domain_file
  file =  '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
  debug [:cross_domain, file]
  send_data file

  # handle the cross-domain request transparently
  # no need to notify the user about this connection
  @onclose = nil
  close_connection_after_writing
end

#stateObject



192
193
194
# File 'lib/em-websocket/connection.rb', line 192

def state
  @handler ? @handler.state : :handshake
end

#trigger_on_closeObject



22
23
24
# File 'lib/em-websocket/connection.rb', line 22

def trigger_on_close
  @onclose.call if @onclose
end

#trigger_on_error(reason) ⇒ Object



31
32
33
34
35
# File 'lib/em-websocket/connection.rb', line 31

def trigger_on_error(reason)
  return false unless @onerror
  @onerror.call(reason)
  true
end

#trigger_on_message(msg) ⇒ Object



16
17
18
# File 'lib/em-websocket/connection.rb', line 16

def trigger_on_message(msg)
  @onmessage.call(msg) if @onmessage
end

#trigger_on_openObject



19
20
21
# File 'lib/em-websocket/connection.rb', line 19

def trigger_on_open
  @onopen.call if @onopen
end

#trigger_on_ping(data) ⇒ Object



25
26
27
# File 'lib/em-websocket/connection.rb', line 25

def trigger_on_ping(data)
  @onping.call(data) if @onping
end

#trigger_on_pong(data) ⇒ Object



28
29
30
# File 'lib/em-websocket/connection.rb', line 28

def trigger_on_pong(data)
  @onpong.call(data) if @onpong
end

#unbindObject



91
92
93
94
95
96
97
98
99
# File 'lib/em-websocket/connection.rb', line 91

def unbind
  debug [:unbind, :connection]

  @handler.unbind if @handler
rescue => e
  debug [:error, e]
  # These are application errors - raise unless onerror defined
  trigger_on_error(e) || raise(e)
end