faye-websocket 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye-websocket might be problematic. Click here for more details.
- data/README.rdoc +132 -0
- data/examples/autobahn_client.rb +45 -0
- data/examples/client.rb +30 -0
- data/examples/index.html +35 -0
- data/examples/server.rb +43 -0
- data/lib/faye/thin_extensions.rb +75 -0
- data/lib/faye/websocket.rb +126 -0
- data/lib/faye/websocket/api.rb +110 -0
- data/lib/faye/websocket/client.rb +82 -0
- data/lib/faye/websocket/draft75_parser.rb +53 -0
- data/lib/faye/websocket/draft76_parser.rb +53 -0
- data/lib/faye/websocket/protocol8_parser.rb +324 -0
- data/spec/faye/websocket/client_spec.rb +146 -0
- data/spec/faye/websocket/draft75_parser_spec.rb +41 -0
- data/spec/faye/websocket/protocol8_parser_spec.rb +145 -0
- data/spec/server.crt +15 -0
- data/spec/server.key +15 -0
- data/spec/spec_helper.rb +50 -0
- metadata +109 -0
data/README.rdoc
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
= Faye::WebSocket
|
2
|
+
|
3
|
+
This is a robust, general-purpose WebSocket implementation extracted from the
|
4
|
+
{Faye}[http://faye.jcoglan.com] project. It provides classes for easily building
|
5
|
+
WebSocket servers and clients in Ruby. It does not provide a server itself, but
|
6
|
+
rather makes it easy to handle WebSocket connections within an existing
|
7
|
+
{Rack}[http://rack.rubyforge.org/] application running under
|
8
|
+
{Thin}[http://code.macournoyer.com/thin/]. It does not provide any abstraction
|
9
|
+
other than the standard {WebSocket API}[http://dev.w3.org/html5/websockets/].
|
10
|
+
|
11
|
+
The server-side socket can process {draft-75}[http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75],
|
12
|
+
{draft-76}[http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76],
|
13
|
+
{hybi-07}[http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07]
|
14
|
+
and later versions of the protocol. It selects protocol versions automatically,
|
15
|
+
supports both +text+ and +binary+ messages, and transparently handles +ping+,
|
16
|
+
+pong+, +close+ and fragmented messages.
|
17
|
+
|
18
|
+
|
19
|
+
== Accepting WebSocket connections in Rack
|
20
|
+
|
21
|
+
You can handle WebSockets on the server side by listening for HTTP Upgrade
|
22
|
+
requests, and creating a new socket for the request. This socket object exposes
|
23
|
+
the usual WebSocket methods for receiving and sending messages. For example this
|
24
|
+
is how you'd implement an echo server:
|
25
|
+
|
26
|
+
require 'faye/websocket'
|
27
|
+
require 'rack'
|
28
|
+
require 'eventmachine'
|
29
|
+
|
30
|
+
app = lambda do |env|
|
31
|
+
if env['HTTP_UPGRADE']
|
32
|
+
ws = Faye::WebSocket.new(env)
|
33
|
+
|
34
|
+
ws.onmessage = lambda do |event|
|
35
|
+
ws.send(event.data)
|
36
|
+
end
|
37
|
+
|
38
|
+
ws.onclose = lambda do |event|
|
39
|
+
p [:close, event.code, event.reason]
|
40
|
+
ws = nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Thin async response
|
44
|
+
[-1, {}, []]
|
45
|
+
|
46
|
+
else
|
47
|
+
# Normal HTTP request
|
48
|
+
[200, {'Content-Type' => 'text/plain'}, ['Hello']]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
EM.run {
|
53
|
+
thin = Rack::Handler.get('thin')
|
54
|
+
thin.run(app, :Port => 9292)
|
55
|
+
}
|
56
|
+
|
57
|
+
|
58
|
+
== Using the WebSocket client
|
59
|
+
|
60
|
+
The client supports both the plain-text +ws+ protocol and the encrypted +wss+
|
61
|
+
protocol, and has exactly the same interface as a socket you would use in a web
|
62
|
+
browser. On the wire it identifies itself as hybi-08, though it's compatible
|
63
|
+
with servers speaking later versions of the protocol, at least up to version 17.
|
64
|
+
|
65
|
+
require 'faye/websocket'
|
66
|
+
require 'eventmachine'
|
67
|
+
|
68
|
+
EM.run {
|
69
|
+
ws = Faye::WebSocket::Client.new('ws://www.example.com/')
|
70
|
+
|
71
|
+
ws.onopen = lambda do |event|
|
72
|
+
p [:open]
|
73
|
+
ws.send('Hello, world!')
|
74
|
+
end
|
75
|
+
|
76
|
+
ws.onmessage = lambda do |event|
|
77
|
+
p [:message, event.data]
|
78
|
+
end
|
79
|
+
|
80
|
+
ws.onclose = lambda do |event|
|
81
|
+
p [:close, event.code, event.reason]
|
82
|
+
ws = nil
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
|
87
|
+
== WebSocket API
|
88
|
+
|
89
|
+
The WebSocket API consists of several event handlers and a method for sending
|
90
|
+
messages.
|
91
|
+
|
92
|
+
* <b><tt>onopen</tt></b> fires when the socket connection is established. Event
|
93
|
+
has no attributes.
|
94
|
+
* <b><tt>onerror</tt></b> fires when the connection attempt fails. Event has no
|
95
|
+
attributes.
|
96
|
+
* <b><tt>onmessage</tt></b> fires when the socket receives a message. Event has
|
97
|
+
one attribute, <b><tt>data</tt></b>, which is either a +String+ (for text
|
98
|
+
frames) or an +Array+ of byte-sized integers (for binary frames).
|
99
|
+
* <b><tt>onclose</tt></b> fires when either the client or the server closes the
|
100
|
+
connection. Event has two optional attributes, <b><tt>code</tt></b> and
|
101
|
+
<b><tt>reason</tt></b>, that expose the status code and message sent by the
|
102
|
+
peer that closed the connection.
|
103
|
+
* <b><tt>send(message)</tt></b> accepts either a +String+ or an +Array+ of
|
104
|
+
byte-sized integers and sends a text or binary message over the connection to
|
105
|
+
the other peer.
|
106
|
+
* <b><tt>close(code, reason)</tt></b> closes the connection, sending the given
|
107
|
+
status code and reason text, both of which are optional.
|
108
|
+
|
109
|
+
|
110
|
+
== License
|
111
|
+
|
112
|
+
(The MIT License)
|
113
|
+
|
114
|
+
Copyright (c) 2009-2011 James Coglan
|
115
|
+
|
116
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
117
|
+
this software and associated documentation files (the 'Software'), to deal in
|
118
|
+
the Software without restriction, including without limitation the rights to use,
|
119
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
120
|
+
Software, and to permit persons to whom the Software is furnished to do so,
|
121
|
+
subject to the following conditions:
|
122
|
+
|
123
|
+
The above copyright notice and this permission notice shall be included in all
|
124
|
+
copies or substantial portions of the Software.
|
125
|
+
|
126
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
127
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
128
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
129
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
130
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
131
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
132
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.expand_path('../../lib/faye/websocket', __FILE__)
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
EM.run {
|
6
|
+
host = 'ws://localhost:9001'
|
7
|
+
agent = "Faye (Ruby #{RUBY_VERSION})"
|
8
|
+
cases = 0
|
9
|
+
skip = [247,248,249,250,251,252,253,254,255,
|
10
|
+
256,257,258,259,260,261,262,263,264]
|
11
|
+
|
12
|
+
socket = Faye::WebSocket::Client.new("#{host}/getCaseCount")
|
13
|
+
|
14
|
+
socket.onmessage = lambda do |event|
|
15
|
+
puts "Total cases to run: #{event.data}"
|
16
|
+
cases = event.data.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
socket.onclose = lambda do |event|
|
20
|
+
run_case = lambda do |n|
|
21
|
+
if n > cases
|
22
|
+
socket = Faye::WebSocket::Client.new("#{host}/updateReports?agent=#{CGI.escape agent}")
|
23
|
+
socket.onclose = lambda { |e| EM.stop }
|
24
|
+
|
25
|
+
elsif skip.include?(n)
|
26
|
+
EM.next_tick { run_case.call(n+1) }
|
27
|
+
|
28
|
+
else
|
29
|
+
puts "Running test case ##{n} ..."
|
30
|
+
socket = Faye::WebSocket::Client.new("#{host}/runCase?case=#{n}&agent=#{CGI.escape agent}")
|
31
|
+
|
32
|
+
socket.onmessage = lambda do |event|
|
33
|
+
socket.send(event.data)
|
34
|
+
end
|
35
|
+
|
36
|
+
socket.onclose = lambda do |event|
|
37
|
+
run_case.call(n + 1)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
run_case.call(1)
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
data/examples/client.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.expand_path('../../lib/faye/websocket', __FILE__)
|
3
|
+
require 'eventmachine'
|
4
|
+
|
5
|
+
port = ARGV[0] || 7000
|
6
|
+
secure = ARGV[1] == 'ssl'
|
7
|
+
|
8
|
+
EM.run {
|
9
|
+
scheme = secure ? 'wss' : 'ws'
|
10
|
+
url = "#{scheme}://localhost:#{port}/"
|
11
|
+
socket = Faye::WebSocket::Client.new(url)
|
12
|
+
|
13
|
+
puts "Connecting to #{socket.url}"
|
14
|
+
|
15
|
+
socket.onopen = lambda do |event|
|
16
|
+
p [:open]
|
17
|
+
socket.send("Hello, WebSocket!")
|
18
|
+
end
|
19
|
+
|
20
|
+
socket.onmessage = lambda do |event|
|
21
|
+
p [:message, event.data]
|
22
|
+
# socket.close 1002, 'Going away'
|
23
|
+
end
|
24
|
+
|
25
|
+
socket.onclose = lambda do |event|
|
26
|
+
p [:close, event.code, event.reason]
|
27
|
+
EM.stop
|
28
|
+
end
|
29
|
+
}
|
30
|
+
|
data/examples/index.html
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
5
|
+
<title>WebSocket test</title>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
|
9
|
+
<h1>WebSocket test</h1>
|
10
|
+
<ul></ul>
|
11
|
+
|
12
|
+
<script type="text/javascript">
|
13
|
+
var logger = document.getElementsByTagName('ul')[0],
|
14
|
+
Socket = window.MozWebSocket || window.WebSocket,
|
15
|
+
socket = new Socket('ws://' + location.hostname + ':' + location.port + '/'),
|
16
|
+
index = 0;
|
17
|
+
|
18
|
+
socket.onopen = function() {
|
19
|
+
logger.innerHTML += '<li>OPEN</li>';
|
20
|
+
socket.send('Hello, world');
|
21
|
+
};
|
22
|
+
|
23
|
+
socket.onmessage = function(event) {
|
24
|
+
logger.innerHTML += '<li>MESSAGE: ' + event.data + '</li>';
|
25
|
+
setTimeout(function() { socket.send(++index + ' ' + event.data) }, 2000);
|
26
|
+
};
|
27
|
+
|
28
|
+
socket.onclose = function(event) {
|
29
|
+
logger.innerHTML += '<li>CLOSE: ' + event.code + ', ' + event.reason + '</li>';
|
30
|
+
};
|
31
|
+
</script>
|
32
|
+
|
33
|
+
</body>
|
34
|
+
</html>
|
35
|
+
|
data/examples/server.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.expand_path('../../lib/faye/websocket', __FILE__)
|
3
|
+
require 'rack'
|
4
|
+
require 'eventmachine'
|
5
|
+
|
6
|
+
port = ARGV[0] || 7000
|
7
|
+
secure = ARGV[1] == 'ssl'
|
8
|
+
|
9
|
+
static = Rack::File.new(File.dirname(__FILE__))
|
10
|
+
|
11
|
+
app = lambda do |env|
|
12
|
+
if env['HTTP_UPGRADE']
|
13
|
+
socket = Faye::WebSocket.new(env)
|
14
|
+
p [:open, socket.url, socket.version]
|
15
|
+
|
16
|
+
socket.onmessage = lambda do |event|
|
17
|
+
socket.send(event.data)
|
18
|
+
end
|
19
|
+
|
20
|
+
socket.onclose = lambda do |event|
|
21
|
+
p [:close, event.code, event.reason]
|
22
|
+
socket = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
[-1, {}, []]
|
26
|
+
else
|
27
|
+
static.call(env)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
EM.run {
|
32
|
+
thin = Rack::Handler.get('thin')
|
33
|
+
thin.run(app, :Port => port) do |server|
|
34
|
+
if secure
|
35
|
+
server.ssl = true
|
36
|
+
server.ssl_options = {
|
37
|
+
:private_key_file => File.expand_path('../../spec/server.key', __FILE__),
|
38
|
+
:cert_chain_file => File.expand_path('../../spec/server.crt', __FILE__)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
}
|
43
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# WebSocket extensions for Thin
|
2
|
+
# Based on code from the Cramp project
|
3
|
+
# http://github.com/lifo/cramp
|
4
|
+
|
5
|
+
# Copyright (c) 2009-2011 Pratik Naik
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
class Thin::Connection
|
27
|
+
def receive_data(data)
|
28
|
+
trace { data }
|
29
|
+
|
30
|
+
case @serving
|
31
|
+
when :websocket
|
32
|
+
callback = @request.env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK]
|
33
|
+
callback.call(data) if callback
|
34
|
+
else
|
35
|
+
if @request.parse(data)
|
36
|
+
if @request.websocket?
|
37
|
+
@request.env['em.connection'] = self
|
38
|
+
@response.persistent!
|
39
|
+
@response.websocket = true
|
40
|
+
@serving = :websocket
|
41
|
+
end
|
42
|
+
|
43
|
+
process
|
44
|
+
end
|
45
|
+
end
|
46
|
+
rescue Thin::InvalidRequest => e
|
47
|
+
log "!! Invalid request"
|
48
|
+
log_error e
|
49
|
+
close_connection
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Thin::Request
|
54
|
+
WEBSOCKET_RECEIVE_CALLBACK = 'websocket.receive_callback'.freeze
|
55
|
+
def websocket?
|
56
|
+
@env['HTTP_CONNECTION'] and
|
57
|
+
@env['HTTP_CONNECTION'].split(/\s*,\s*/).include?('Upgrade') and
|
58
|
+
['WebSocket', 'websocket'].include?(@env['HTTP_UPGRADE'])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Thin::Response
|
63
|
+
# Headers for sending Websocket upgrade
|
64
|
+
attr_accessor :websocket
|
65
|
+
|
66
|
+
def each
|
67
|
+
yield(head) unless websocket
|
68
|
+
if @body.is_a?(String)
|
69
|
+
yield @body
|
70
|
+
else
|
71
|
+
@body.each { |chunk| yield chunk }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# API and protocol references:
|
2
|
+
#
|
3
|
+
# * http://dev.w3.org/html5/websockets/
|
4
|
+
# * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-eventtarget
|
5
|
+
# * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#interface-event
|
6
|
+
# * http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
|
7
|
+
# * http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
8
|
+
# * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
|
9
|
+
|
10
|
+
require 'base64'
|
11
|
+
require 'digest/md5'
|
12
|
+
require 'digest/sha1'
|
13
|
+
require 'forwardable'
|
14
|
+
require 'net/http'
|
15
|
+
require 'uri'
|
16
|
+
|
17
|
+
require 'eventmachine'
|
18
|
+
require 'thin'
|
19
|
+
require File.dirname(__FILE__) + '/thin_extensions'
|
20
|
+
|
21
|
+
module Faye
|
22
|
+
class WebSocket
|
23
|
+
|
24
|
+
root = File.expand_path('../websocket', __FILE__)
|
25
|
+
|
26
|
+
autoload :API, root + '/api'
|
27
|
+
autoload :Client, root + '/client'
|
28
|
+
autoload :Draft75Parser, root + '/draft75_parser'
|
29
|
+
autoload :Draft76Parser, root + '/draft76_parser'
|
30
|
+
autoload :Protocol8Parser, root + '/protocol8_parser'
|
31
|
+
|
32
|
+
# http://www.w3.org/International/questions/qa-forms-utf-8.en.php
|
33
|
+
UTF8_MATCH = /^([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})*$/
|
34
|
+
|
35
|
+
def self.encode(string, validate_encoding = false)
|
36
|
+
if Array === string
|
37
|
+
return nil if validate_encoding and !valid_utf8?(string)
|
38
|
+
string = string.pack('C*')
|
39
|
+
end
|
40
|
+
return string unless string.respond_to?(:force_encoding)
|
41
|
+
string.force_encoding('UTF-8')
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.valid_utf8?(byte_array)
|
45
|
+
UTF8_MATCH =~ byte_array.pack('C*') ? true : false
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.parser(env)
|
49
|
+
if env['HTTP_SEC_WEBSOCKET_VERSION']
|
50
|
+
Protocol8Parser
|
51
|
+
elsif env['HTTP_SEC_WEBSOCKET_KEY1']
|
52
|
+
Draft76Parser
|
53
|
+
else
|
54
|
+
Draft75Parser
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
extend Forwardable
|
59
|
+
def_delegators :@parser, :version
|
60
|
+
|
61
|
+
attr_reader :env
|
62
|
+
include API
|
63
|
+
|
64
|
+
def initialize(env)
|
65
|
+
@env = env
|
66
|
+
@callback = @env['async.callback']
|
67
|
+
@stream = Stream.new(self, @env['em.connection'])
|
68
|
+
@callback.call [200, {}, @stream]
|
69
|
+
|
70
|
+
@url = determine_url
|
71
|
+
@ready_state = CONNECTING
|
72
|
+
@buffered_amount = 0
|
73
|
+
|
74
|
+
@parser = WebSocket.parser(@env).new(self)
|
75
|
+
@stream.write(@parser.handshake_response)
|
76
|
+
|
77
|
+
@ready_state = OPEN
|
78
|
+
|
79
|
+
event = Event.new('open')
|
80
|
+
event.init_event('open', false, false)
|
81
|
+
dispatch_event(event)
|
82
|
+
|
83
|
+
@env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK] = @parser.method(:parse)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def determine_url
|
89
|
+
secure = if @env.has_key?('HTTP_X_FORWARDED_PROTO')
|
90
|
+
@env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
91
|
+
else
|
92
|
+
@env['HTTP_ORIGIN'] =~ /^https:/i
|
93
|
+
end
|
94
|
+
|
95
|
+
scheme = secure ? 'wss:' : 'ws:'
|
96
|
+
"#{ scheme }//#{ @env['HTTP_HOST'] }#{ @env['REQUEST_URI'] }"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class WebSocket::Stream
|
101
|
+
include EventMachine::Deferrable
|
102
|
+
|
103
|
+
extend Forwardable
|
104
|
+
def_delegators :@connection, :close_connection, :close_connection_after_writing
|
105
|
+
|
106
|
+
def initialize(web_socket, connection)
|
107
|
+
@web_socket = web_socket
|
108
|
+
@connection = connection
|
109
|
+
end
|
110
|
+
|
111
|
+
def each(&callback)
|
112
|
+
@data_callback = callback
|
113
|
+
end
|
114
|
+
|
115
|
+
def fail
|
116
|
+
@web_socket.close(1006, '', false)
|
117
|
+
end
|
118
|
+
|
119
|
+
def write(data)
|
120
|
+
return unless @data_callback
|
121
|
+
@data_callback.call(data)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|