sonixlabs-em-websocket 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG.rdoc +80 -0
  3. data/Gemfile +3 -0
  4. data/README.md +98 -0
  5. data/Rakefile +11 -0
  6. data/em-websocket.gemspec +27 -0
  7. data/examples/echo.rb +8 -0
  8. data/examples/flash_policy_file_server.rb +21 -0
  9. data/examples/js/FABridge.js +604 -0
  10. data/examples/js/WebSocketMain.swf +0 -0
  11. data/examples/js/swfobject.js +4 -0
  12. data/examples/js/web_socket.js +312 -0
  13. data/examples/multicast.rb +47 -0
  14. data/examples/test.html +30 -0
  15. data/lib/em-websocket/client_connection.rb +19 -0
  16. data/lib/em-websocket/close03.rb +11 -0
  17. data/lib/em-websocket/close05.rb +11 -0
  18. data/lib/em-websocket/close06.rb +16 -0
  19. data/lib/em-websocket/close75.rb +10 -0
  20. data/lib/em-websocket/connection.rb +184 -0
  21. data/lib/em-websocket/debugger.rb +17 -0
  22. data/lib/em-websocket/framing03.rb +167 -0
  23. data/lib/em-websocket/framing04.rb +15 -0
  24. data/lib/em-websocket/framing05.rb +168 -0
  25. data/lib/em-websocket/framing07.rb +180 -0
  26. data/lib/em-websocket/framing76.rb +114 -0
  27. data/lib/em-websocket/handler.rb +56 -0
  28. data/lib/em-websocket/handler03.rb +10 -0
  29. data/lib/em-websocket/handler05.rb +10 -0
  30. data/lib/em-websocket/handler06.rb +10 -0
  31. data/lib/em-websocket/handler07.rb +10 -0
  32. data/lib/em-websocket/handler08.rb +10 -0
  33. data/lib/em-websocket/handler13.rb +10 -0
  34. data/lib/em-websocket/handler75.rb +9 -0
  35. data/lib/em-websocket/handler76.rb +12 -0
  36. data/lib/em-websocket/handler_factory.rb +107 -0
  37. data/lib/em-websocket/handshake04.rb +75 -0
  38. data/lib/em-websocket/handshake75.rb +21 -0
  39. data/lib/em-websocket/handshake76.rb +71 -0
  40. data/lib/em-websocket/masking04.rb +63 -0
  41. data/lib/em-websocket/message_processor_03.rb +38 -0
  42. data/lib/em-websocket/message_processor_06.rb +52 -0
  43. data/lib/em-websocket/version.rb +5 -0
  44. data/lib/em-websocket/websocket.rb +45 -0
  45. data/lib/em-websocket.rb +23 -0
  46. data/lib/sonixlabs-em-websocket.rb +1 -0
  47. data/spec/helper.rb +146 -0
  48. data/spec/integration/client_examples.rb +48 -0
  49. data/spec/integration/common_spec.rb +118 -0
  50. data/spec/integration/draft03_spec.rb +270 -0
  51. data/spec/integration/draft05_spec.rb +48 -0
  52. data/spec/integration/draft06_spec.rb +88 -0
  53. data/spec/integration/draft13_spec.rb +75 -0
  54. data/spec/integration/draft75_spec.rb +117 -0
  55. data/spec/integration/draft76_spec.rb +230 -0
  56. data/spec/integration/shared_examples.rb +91 -0
  57. data/spec/unit/framing_spec.rb +325 -0
  58. data/spec/unit/handler_spec.rb +147 -0
  59. data/spec/unit/masking_spec.rb +27 -0
  60. data/spec/unit/message_processor_spec.rb +36 -0
  61. metadata +198 -0
@@ -0,0 +1,312 @@
1
+ // Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
2
+ // Lincense: New BSD Lincense
3
+ // Reference: http://dev.w3.org/html5/websockets/
4
+ // Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol
5
+
6
+ (function() {
7
+
8
+ if (window.WebSocket) return;
9
+
10
+ var console = window.console;
11
+ if (!console) console = {log: function(){ }, error: function(){ }};
12
+
13
+ function hasFlash() {
14
+ if ('navigator' in window && 'plugins' in navigator && navigator.plugins['Shockwave Flash']) {
15
+ return !!navigator.plugins['Shockwave Flash'].description;
16
+ }
17
+ if ('ActiveXObject' in window) {
18
+ try {
19
+ return !!new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
20
+ } catch (e) {}
21
+ }
22
+ return false;
23
+ }
24
+
25
+ if (!hasFlash()) {
26
+ console.error("Flash Player is not installed.");
27
+ return;
28
+ }
29
+
30
+ WebSocket = function(url, protocol, proxyHost, proxyPort, headers) {
31
+ var self = this;
32
+ self.readyState = WebSocket.CONNECTING;
33
+ self.bufferedAmount = 0;
34
+ WebSocket.__addTask(function() {
35
+ self.__flash =
36
+ WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null);
37
+
38
+ self.__flash.addEventListener("open", function(fe) {
39
+ try {
40
+ if (self.onopen) self.onopen();
41
+ } catch (e) {
42
+ console.error(e.toString());
43
+ }
44
+ });
45
+
46
+ self.__flash.addEventListener("close", function(fe) {
47
+ try {
48
+ if (self.onclose) self.onclose();
49
+ } catch (e) {
50
+ console.error(e.toString());
51
+ }
52
+ });
53
+
54
+ self.__flash.addEventListener("message", function(fe) {
55
+ var data = decodeURIComponent(fe.getData());
56
+ try {
57
+ if (self.onmessage) {
58
+ var e;
59
+ if (window.MessageEvent) {
60
+ e = document.createEvent("MessageEvent");
61
+ e.initMessageEvent("message", false, false, data, null, null, window);
62
+ } else { // IE
63
+ e = {data: data};
64
+ }
65
+ self.onmessage(e);
66
+ }
67
+ } catch (e) {
68
+ console.error(e.toString());
69
+ }
70
+ });
71
+
72
+ self.__flash.addEventListener("stateChange", function(fe) {
73
+ try {
74
+ self.readyState = fe.getReadyState();
75
+ self.bufferedAmount = fe.getBufferedAmount();
76
+ } catch (e) {
77
+ console.error(e.toString());
78
+ }
79
+ });
80
+
81
+ //console.log("[WebSocket] Flash object is ready");
82
+ });
83
+ }
84
+
85
+ WebSocket.prototype.send = function(data) {
86
+ if (!this.__flash || this.readyState == WebSocket.CONNECTING) {
87
+ throw "INVALID_STATE_ERR: Web Socket connection has not been established";
88
+ }
89
+ var result = this.__flash.send(data);
90
+ if (result < 0) { // success
91
+ return true;
92
+ } else {
93
+ this.bufferedAmount = result;
94
+ return false;
95
+ }
96
+ };
97
+
98
+ WebSocket.prototype.close = function() {
99
+ if (!this.__flash) return;
100
+ if (this.readyState != WebSocket.OPEN) return;
101
+ this.__flash.close();
102
+ // Sets/calls them manually here because Flash WebSocketConnection.close cannot fire events
103
+ // which causes weird error:
104
+ // > You are trying to call recursively into the Flash Player which is not allowed.
105
+ this.readyState = WebSocket.CLOSED;
106
+ if (this.onclose) this.onclose();
107
+ };
108
+
109
+ /**
110
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
111
+ *
112
+ * @param {string} type
113
+ * @param {function} listener
114
+ * @param {boolean} useCapture !NB Not implemented yet
115
+ * @return void
116
+ */
117
+ WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
118
+ if (!('__events' in this)) {
119
+ this.__events = {};
120
+ }
121
+ if (!(type in this.__events)) {
122
+ this.__events[type] = [];
123
+ if ('function' == typeof this['on' + type]) {
124
+ this.__events[type].defaultHandler = this['on' + type];
125
+ this['on' + type] = WebSocket_FireEvent(this, type);
126
+ }
127
+ }
128
+ this.__events[type].push(listener);
129
+ };
130
+
131
+ /**
132
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
133
+ *
134
+ * @param {string} type
135
+ * @param {function} listener
136
+ * @param {boolean} useCapture NB! Not implemented yet
137
+ * @return void
138
+ */
139
+ WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
140
+ if (!('__events' in this)) {
141
+ this.__events = {};
142
+ }
143
+ if (!(type in this.__events)) return;
144
+ for (var i = this.__events.length; i > -1; --i) {
145
+ if (listener === this.__events[type][i]) {
146
+ this.__events[type].splice(i, 1);
147
+ break;
148
+ }
149
+ }
150
+ };
151
+
152
+ /**
153
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
154
+ *
155
+ * @param {WebSocketEvent} event
156
+ * @return void
157
+ */
158
+ WebSocket.prototype.dispatchEvent = function(event) {
159
+ if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
160
+ if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR';
161
+
162
+ for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) {
163
+ this.__events[event.type][i](event);
164
+ if (event.cancelBubble) break;
165
+ }
166
+
167
+ if (false !== event.returnValue &&
168
+ 'function' == typeof this.__events[event.type].defaultHandler)
169
+ {
170
+ this.__events[event.type].defaultHandler(event);
171
+ }
172
+ };
173
+
174
+ /**
175
+ *
176
+ * @param {object} object
177
+ * @param {string} type
178
+ */
179
+ function WebSocket_FireEvent(object, type) {
180
+ return function(data) {
181
+ var event = new WebSocketEvent();
182
+ event.initEvent(type, true, true);
183
+ event.target = event.currentTarget = object;
184
+ for (var key in data) {
185
+ event[key] = data[key];
186
+ }
187
+ object.dispatchEvent(event, arguments);
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Basic implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface">DOM 2 EventInterface</a>}
193
+ *
194
+ * @class
195
+ * @constructor
196
+ */
197
+ function WebSocketEvent(){}
198
+
199
+ /**
200
+ *
201
+ * @type boolean
202
+ */
203
+ WebSocketEvent.prototype.cancelable = true;
204
+
205
+ /**
206
+ *
207
+ * @type boolean
208
+ */
209
+ WebSocketEvent.prototype.cancelBubble = false;
210
+
211
+ /**
212
+ *
213
+ * @return void
214
+ */
215
+ WebSocketEvent.prototype.preventDefault = function() {
216
+ if (this.cancelable) {
217
+ this.returnValue = false;
218
+ }
219
+ };
220
+
221
+ /**
222
+ *
223
+ * @return void
224
+ */
225
+ WebSocketEvent.prototype.stopPropagation = function() {
226
+ this.cancelBubble = true;
227
+ };
228
+
229
+ /**
230
+ *
231
+ * @param {string} eventTypeArg
232
+ * @param {boolean} canBubbleArg
233
+ * @param {boolean} cancelableArg
234
+ * @return void
235
+ */
236
+ WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) {
237
+ this.type = eventTypeArg;
238
+ this.cancelable = cancelableArg;
239
+ this.timeStamp = new Date();
240
+ };
241
+
242
+
243
+ WebSocket.CONNECTING = 0;
244
+ WebSocket.OPEN = 1;
245
+ WebSocket.CLOSED = 2;
246
+
247
+ WebSocket.__tasks = [];
248
+
249
+ WebSocket.__initialize = function() {
250
+ if (!WebSocket.__swfLocation) {
251
+ //console.error("[WebSocket] set WebSocket.__swfLocation to location of WebSocketMain.swf");
252
+ //return;
253
+ WebSocket.__swfLocation = "js/WebSocketMain.swf";
254
+ }
255
+ var container = document.createElement("div");
256
+ container.id = "webSocketContainer";
257
+ // Puts the Flash out of the window. Note that we cannot use display: none or visibility: hidden
258
+ // here because it prevents Flash from loading at least in IE.
259
+ container.style.position = "absolute";
260
+ container.style.left = "-100px";
261
+ container.style.top = "-100px";
262
+ var holder = document.createElement("div");
263
+ holder.id = "webSocketFlash";
264
+ container.appendChild(holder);
265
+ document.body.appendChild(container);
266
+ swfobject.embedSWF(
267
+ WebSocket.__swfLocation, "webSocketFlash", "8", "8", "9.0.0",
268
+ null, {bridgeName: "webSocket"}, null, null,
269
+ function(e) {
270
+ if (!e.success) console.error("[WebSocket] swfobject.embedSWF failed");
271
+ }
272
+ );
273
+ FABridge.addInitializationCallback("webSocket", function() {
274
+ try {
275
+ //console.log("[WebSocket] FABridge initializad");
276
+ WebSocket.__flash = FABridge.webSocket.root();
277
+ WebSocket.__flash.setCallerUrl(location.href);
278
+ for (var i = 0; i < WebSocket.__tasks.length; ++i) {
279
+ WebSocket.__tasks[i]();
280
+ }
281
+ WebSocket.__tasks = [];
282
+ } catch (e) {
283
+ console.error("[WebSocket] " + e.toString());
284
+ }
285
+ });
286
+ };
287
+
288
+ WebSocket.__addTask = function(task) {
289
+ if (WebSocket.__flash) {
290
+ task();
291
+ } else {
292
+ WebSocket.__tasks.push(task);
293
+ }
294
+ }
295
+
296
+ // called from Flash
297
+ function webSocketLog(message) {
298
+ console.log(decodeURIComponent(message));
299
+ }
300
+
301
+ // called from Flash
302
+ function webSocketError(message) {
303
+ console.error(decodeURIComponent(message));
304
+ }
305
+
306
+ if (window.addEventListener) {
307
+ window.addEventListener("load", WebSocket.__initialize, false);
308
+ } else {
309
+ window.attachEvent("onload", WebSocket.__initialize);
310
+ }
311
+
312
+ })();
@@ -0,0 +1,47 @@
1
+ require 'em-websocket'
2
+ # requires the twitter-stream gem
3
+ require 'twitter/json_stream'
4
+ require 'json'
5
+
6
+ #
7
+ # broadcast all ruby related tweets to all connected users!
8
+ #
9
+
10
+ username = ARGV.shift
11
+ password = ARGV.shift
12
+ raise "need username and password" if !username or !password
13
+
14
+ EventMachine.run {
15
+ @channel = EM::Channel.new
16
+
17
+ @twitter = Twitter::JSONStream.connect(
18
+ :path => '/1/statuses/filter.json?track=ruby',
19
+ :auth => "#{username}:#{password}",
20
+ :ssl => true
21
+ )
22
+
23
+ @twitter.each_item do |status|
24
+ status = JSON.parse(status)
25
+ @channel.push "#{status['user']['screen_name']}: #{status['text']}"
26
+ end
27
+
28
+
29
+ EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do |ws|
30
+
31
+ ws.onopen {
32
+ sid = @channel.subscribe { |msg| ws.send msg }
33
+ @channel.push "#{sid} connected!"
34
+
35
+ ws.onmessage { |msg|
36
+ @channel.push "<#{sid}>: #{msg}"
37
+ }
38
+
39
+ ws.onclose {
40
+ @channel.unsubscribe(sid)
41
+ }
42
+ }
43
+
44
+ end
45
+
46
+ puts "Server started"
47
+ }
@@ -0,0 +1,30 @@
1
+ <html>
2
+ <head>
3
+ <script src='js/swfobject.js'></script>
4
+ <script src='js/FABridge.js'></script>
5
+ <script src='js/web_socket.js'></script>
6
+ <script>
7
+ function init() {
8
+ function debug(string) {
9
+ var element = document.getElementById("debug");
10
+ var p = document.createElement("p");
11
+ p.appendChild(document.createTextNode(string));
12
+ element.appendChild(p);
13
+ }
14
+
15
+ var Socket = "MozWebSocket" in window ? MozWebSocket : WebSocket;
16
+ var ws = new Socket("ws://localhost:8080/");
17
+ ws.onmessage = function(evt) { debug("Message: " + evt.data); };
18
+ ws.onclose = function() { debug("socket closed"); };
19
+ ws.onopen = function() {
20
+ debug("connected...");
21
+ ws.send("hello server");
22
+ ws.send("hello again");
23
+ };
24
+ };
25
+ </script>
26
+ </head>
27
+ <body onload="init();">
28
+ <div id="debug"></div>
29
+ </body>
30
+ </html>
@@ -0,0 +1,19 @@
1
+ require 'addressable/uri'
2
+
3
+ module EventMachine
4
+ module WebSocket
5
+ class ClientConnection < EventMachine::WebSocket::Connection
6
+
7
+ def initialize(options)
8
+ super
9
+ @handler = Handler08.new( self, options, options[:debug] )
10
+ @handler.run_client
11
+ end
12
+
13
+ def dispatch(data)
14
+ # server's handshake response
15
+ @handler.client_handle_server_handshake_response(data)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ module Close03
4
+ def close_websocket(code, body)
5
+ # TODO: Ideally send body data and check that it matches in ack
6
+ send_frame(:close, '')
7
+ @state = :closing
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ module Close05
4
+ def close_websocket(code, body)
5
+ # TODO: Ideally send body data and check that it matches in ack
6
+ send_frame(:close, "\x53")
7
+ @state = :closing
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ module Close06
4
+ def close_websocket(code, body)
5
+ if code
6
+ close_data = [code].pack('n')
7
+ close_data << body if body
8
+ send_frame(:close, close_data)
9
+ else
10
+ send_frame(:close, '')
11
+ end
12
+ @state = :closing
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ module Close75
4
+ def close_websocket(code, body)
5
+ @state = :closed
6
+ @connection.close_connection_after_writing
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,184 @@
1
+ require 'addressable/uri'
2
+
3
+ module EventMachine
4
+ module WebSocket
5
+ class Connection < EventMachine::Connection
6
+ include Debugger
7
+
8
+ # define WebSocket callbacks
9
+ def onopen(&blk); @onopen = blk; end
10
+ def onclose(&blk); @onclose = blk; end
11
+ def onerror(&blk); @onerror = blk; end
12
+ def onmessage(&blk); @onmessage = blk; end
13
+
14
+ def trigger_on_message(msg, type=:text)
15
+ if @onmessage
16
+ if @onmessage.arity == 2
17
+ @onmessage.call msg, type
18
+ else
19
+ @onmessage.call msg
20
+ end
21
+ end
22
+ end
23
+ def trigger_on_open
24
+ @onopen.call if @onopen
25
+ end
26
+ def trigger_on_close
27
+ @onclose.call if @onclose
28
+ end
29
+ def trigger_on_error(reason)
30
+ return false unless @onerror
31
+ @onerror.call(reason)
32
+ true
33
+ end
34
+
35
+ def initialize(options)
36
+ @options = options
37
+ @debug = options[:debug] || false
38
+ @secure = options[:secure] || false
39
+ @tls_options = options[:tls_options] || {}
40
+ @data = ''
41
+
42
+ debug [:initialize]
43
+ end
44
+
45
+ # Use this method to close the websocket connection cleanly
46
+ # This sends a close frame and waits for acknowlegement before closing
47
+ # the connection
48
+ def close_websocket(code = nil, body = nil)
49
+ if code && !(4000..4999).include?(code)
50
+ raise "Application code may only use codes in the range 4000-4999"
51
+ end
52
+
53
+ # If code not defined then set to 1000 (normal closure)
54
+ code ||= 1000
55
+
56
+ close_websocket_private(code, body)
57
+ end
58
+
59
+ def post_init
60
+ start_tls(@tls_options) if @secure
61
+ end
62
+
63
+ def receive_data(data)
64
+ debug [:receive_data, data]
65
+
66
+ if @handler and state != :handshake #TODO a different way to catch client sent handshake, is waiting state
67
+ @handler.receive_data(data)
68
+ else
69
+ dispatch(data)
70
+ end
71
+ rescue HandshakeError => e
72
+ debug [:error, e]
73
+ trigger_on_error(e)
74
+ # Errors during the handshake require the connection to be aborted
75
+ abort
76
+ rescue WebSocketError => e
77
+ debug [:error, e]
78
+ trigger_on_error(e)
79
+ close_websocket_private(1002) # 1002 indicates a protocol error
80
+ rescue => e
81
+ debug [:error, e]
82
+ # These are application errors - raise unless onerror defined
83
+ trigger_on_error(e) || raise(e)
84
+ # There is no code defined for application errors, so use 3000
85
+ # (which is reserved for frameworks)
86
+ close_websocket_private(3000)
87
+ end
88
+
89
+ def unbind
90
+ debug [:unbind, :connection]
91
+
92
+ @handler.unbind if @handler
93
+ rescue => e
94
+ debug [:error, e]
95
+ # These are application errors - raise unless onerror defined
96
+ trigger_on_error(e) || raise(e)
97
+ end
98
+
99
+ def dispatch(data)
100
+ if data.match(/\A<policy-file-request\s*\/>/)
101
+ send_flash_cross_domain_file
102
+ return false
103
+ else
104
+ debug [:inbound_headers, data]
105
+ @data << data
106
+ @handler = HandlerFactory.build(self, @data, @secure, @debug)
107
+ unless @handler
108
+ # The whole header has not been received yet.
109
+ return false
110
+ end
111
+ @data = nil
112
+ @handler.run_server
113
+ return true
114
+ end
115
+ end
116
+
117
+ def send_flash_cross_domain_file
118
+ file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
119
+ debug [:cross_domain, file]
120
+ send_data file
121
+
122
+ # handle the cross-domain request transparently
123
+ # no need to notify the user about this connection
124
+ @onclose = nil
125
+ close_connection_after_writing
126
+ end
127
+
128
+ def send(data, type=:text)
129
+ if type == :text
130
+ # If we're using Ruby 1.9, be pedantic about encodings
131
+ if data.respond_to?(:force_encoding)
132
+ # Also accept ascii only data in other encodings for convenience
133
+ unless (data.encoding == Encoding.find("UTF-8") && data.valid_encoding?) || data.ascii_only?
134
+ raise WebSocketError, "Data sent to WebSocket must be valid UTF-8 but was #{data.encoding} (valid: #{data.valid_encoding?})"
135
+ end
136
+ # This labels the encoding as binary so that it can be combined with
137
+ # the BINARY framing
138
+ data.force_encoding("BINARY")
139
+ else
140
+ # TODO: Check that data is valid UTF-8
141
+ end
142
+
143
+ if @handler
144
+ @handler.send_text_frame(data)
145
+ else
146
+ raise WebSocketError, "Cannot send data before onopen callback"
147
+ end
148
+ else
149
+ if @handler
150
+ @handler.send_frame(type, data)
151
+ else
152
+ raise WebSocketError, "Cannot send data before onopen callback"
153
+ end
154
+ end
155
+ end
156
+
157
+ def request
158
+ @handler ? @handler.request : {}
159
+ end
160
+
161
+ def state
162
+ @handler ? @handler.state : :handshake
163
+ end
164
+
165
+ private
166
+
167
+ # As definited in draft 06 7.2.2, some failures require that the server
168
+ # abort the websocket connection rather than close cleanly
169
+ def abort
170
+ close_connection
171
+ end
172
+
173
+ def close_websocket_private(code, body = nil)
174
+ if @handler
175
+ debug [:closing, code]
176
+ @handler.close_websocket(code, body)
177
+ else
178
+ # The handshake hasn't completed - should be safe to terminate
179
+ abort
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,17 @@
1
+ module EventMachine
2
+ module WebSocket
3
+ module Debugger
4
+
5
+ private
6
+
7
+ def debug(*data)
8
+ if @debug
9
+ require 'pp'
10
+ pp data
11
+ puts
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end