faye-websocket 0.4.7 → 0.5.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/CHANGELOG.md +81 -0
- data/README.md +408 -0
- data/examples/app.rb +4 -1
- data/examples/autobahn_client.rb +8 -6
- data/examples/client.rb +2 -1
- data/examples/config.ru +6 -9
- data/{spec → examples}/rainbows.conf +0 -0
- data/examples/server.rb +10 -1
- data/lib/faye/adapters/rainbows.rb +15 -16
- data/lib/faye/adapters/rainbows_client.rb +15 -16
- data/lib/faye/adapters/thin.rb +15 -16
- data/lib/faye/eventsource.rb +38 -46
- data/lib/faye/rack_stream.rb +70 -0
- data/lib/faye/websocket.rb +39 -162
- data/lib/faye/websocket/api.rb +70 -60
- data/lib/faye/websocket/api/event.rb +1 -1
- data/lib/faye/websocket/api/event_target.rb +35 -12
- data/lib/faye/websocket/client.rb +5 -38
- metadata +62 -45
- data/CHANGELOG.txt +0 -74
- data/README.rdoc +0 -366
- data/ext/faye_websocket_mask/FayeWebsocketMaskService.java +0 -61
- data/ext/faye_websocket_mask/extconf.rb +0 -5
- data/ext/faye_websocket_mask/faye_websocket_mask.c +0 -33
- data/lib/faye/websocket/draft75_parser.rb +0 -87
- data/lib/faye/websocket/draft76_parser.rb +0 -84
- data/lib/faye/websocket/hybi_parser.rb +0 -321
- data/lib/faye/websocket/hybi_parser/handshake.rb +0 -78
- data/lib/faye/websocket/hybi_parser/stream_reader.rb +0 -29
- data/lib/faye/websocket/utf8_match.rb +0 -8
- data/spec/faye/websocket/client_spec.rb +0 -162
- data/spec/faye/websocket/draft75_parser_examples.rb +0 -48
- data/spec/faye/websocket/draft75_parser_spec.rb +0 -27
- data/spec/faye/websocket/draft76_parser_spec.rb +0 -34
- data/spec/faye/websocket/hybi_parser_spec.rb +0 -149
- data/spec/server.crt +0 -15
- data/spec/server.key +0 -15
- data/spec/spec_helper.rb +0 -68
@@ -3,16 +3,13 @@ module Faye
|
|
3
3
|
|
4
4
|
class Client
|
5
5
|
include API
|
6
|
-
attr_reader :protocol, :uri
|
7
6
|
|
8
7
|
def initialize(url, protocols = nil)
|
9
|
-
@parser = HybiParser.new(self, :masking => true, :protocols => protocols)
|
10
8
|
@url = url
|
11
9
|
@uri = URI.parse(url)
|
10
|
+
@driver = ::WebSocket::Driver.client(self, :protocols => protocols)
|
12
11
|
|
13
|
-
|
14
|
-
@ready_state = CONNECTING
|
15
|
-
@buffered_amount = 0
|
12
|
+
super()
|
16
13
|
|
17
14
|
port = @uri.port || (@uri.scheme == 'wss' ? 443 : 80)
|
18
15
|
|
@@ -26,37 +23,7 @@ module Faye
|
|
26
23
|
|
27
24
|
def on_connect
|
28
25
|
@stream.start_tls if @uri.scheme == 'wss'
|
29
|
-
@
|
30
|
-
@message = []
|
31
|
-
@stream.write(@handshake.request_data)
|
32
|
-
end
|
33
|
-
|
34
|
-
def receive_data(data)
|
35
|
-
data = WebSocket.encode(data)
|
36
|
-
|
37
|
-
case @ready_state
|
38
|
-
when CONNECTING then
|
39
|
-
@message += @handshake.parse(data)
|
40
|
-
return unless @handshake.complete?
|
41
|
-
|
42
|
-
if @handshake.valid?
|
43
|
-
@protocol = @handshake.protocol || ''
|
44
|
-
@ready_state = OPEN
|
45
|
-
event = Event.new('open')
|
46
|
-
event.init_event('open', false, false)
|
47
|
-
dispatch_event(event)
|
48
|
-
|
49
|
-
receive_data(@message)
|
50
|
-
else
|
51
|
-
@ready_state = CLOSED
|
52
|
-
event = Event.new('close', :code => 1006, :reason => '')
|
53
|
-
event.init_event('close', false, false)
|
54
|
-
dispatch_event(event)
|
55
|
-
end
|
56
|
-
|
57
|
-
when OPEN, CLOSING then
|
58
|
-
@parser.parse(data)
|
59
|
-
end
|
26
|
+
@driver.start
|
60
27
|
end
|
61
28
|
|
62
29
|
module Connection
|
@@ -67,11 +34,11 @@ module Faye
|
|
67
34
|
end
|
68
35
|
|
69
36
|
def receive_data(data)
|
70
|
-
parent.__send__(:
|
37
|
+
parent.__send__(:parse, data)
|
71
38
|
end
|
72
39
|
|
73
40
|
def unbind
|
74
|
-
parent.
|
41
|
+
parent.__send__(:finalize, '', 1006)
|
75
42
|
end
|
76
43
|
|
77
44
|
def write(data)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: faye-websocket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -28,14 +28,14 @@ dependencies:
|
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 0.12.0
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: websocket-driver
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
35
35
|
- - ! '>='
|
36
36
|
- !ruby/object:Gem::Version
|
37
37
|
version: '0'
|
38
|
-
type: :
|
38
|
+
type: :runtime
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: progressbar
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
@@ -60,7 +60,23 @@ dependencies:
|
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
63
|
+
name: puma
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.0.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.0.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rack
|
64
80
|
requirement: !ruby/object:Gem::Requirement
|
65
81
|
none: false
|
66
82
|
requirements:
|
@@ -96,17 +112,17 @@ dependencies:
|
|
96
112
|
requirement: !ruby/object:Gem::Requirement
|
97
113
|
none: false
|
98
114
|
requirements:
|
99
|
-
- -
|
115
|
+
- - ~>
|
100
116
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
117
|
+
version: 4.4.0
|
102
118
|
type: :development
|
103
119
|
prerelease: false
|
104
120
|
version_requirements: !ruby/object:Gem::Requirement
|
105
121
|
none: false
|
106
122
|
requirements:
|
107
|
-
- -
|
123
|
+
- - ~>
|
108
124
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
125
|
+
version: 4.4.0
|
110
126
|
- !ruby/object:Gem::Dependency
|
111
127
|
name: thin
|
112
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,59 +139,60 @@ dependencies:
|
|
123
139
|
- - ! '>='
|
124
140
|
- !ruby/object:Gem::Version
|
125
141
|
version: 1.2.0
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: goliath
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
126
158
|
description:
|
127
159
|
email: jcoglan@gmail.com
|
128
160
|
executables: []
|
129
|
-
extensions:
|
130
|
-
- ext/faye_websocket_mask/extconf.rb
|
161
|
+
extensions: []
|
131
162
|
extra_rdoc_files:
|
132
|
-
- README.
|
163
|
+
- README.md
|
133
164
|
files:
|
134
|
-
- README.
|
135
|
-
- CHANGELOG.
|
136
|
-
- ext/faye_websocket_mask/faye_websocket_mask.c
|
137
|
-
- ext/faye_websocket_mask/FayeWebsocketMaskService.java
|
138
|
-
- ext/faye_websocket_mask/extconf.rb
|
139
|
-
- lib/faye/adapters/goliath.rb
|
140
|
-
- lib/faye/adapters/rainbows.rb
|
141
|
-
- lib/faye/adapters/rainbows_client.rb
|
142
|
-
- lib/faye/adapters/thin.rb
|
165
|
+
- README.md
|
166
|
+
- CHANGELOG.md
|
143
167
|
- lib/faye/eventsource.rb
|
144
168
|
- lib/faye/websocket/adapter.rb
|
145
|
-
- lib/faye/websocket/api/event.rb
|
146
169
|
- lib/faye/websocket/api/event_target.rb
|
147
|
-
- lib/faye/websocket/api.rb
|
170
|
+
- lib/faye/websocket/api/event.rb
|
148
171
|
- lib/faye/websocket/client.rb
|
149
|
-
- lib/faye/websocket/
|
150
|
-
- lib/faye/websocket/draft76_parser.rb
|
151
|
-
- lib/faye/websocket/hybi_parser/handshake.rb
|
152
|
-
- lib/faye/websocket/hybi_parser/stream_reader.rb
|
153
|
-
- lib/faye/websocket/hybi_parser.rb
|
154
|
-
- lib/faye/websocket/utf8_match.rb
|
172
|
+
- lib/faye/websocket/api.rb
|
155
173
|
- lib/faye/websocket.rb
|
174
|
+
- lib/faye/adapters/rainbows_client.rb
|
175
|
+
- lib/faye/adapters/goliath.rb
|
176
|
+
- lib/faye/adapters/thin.rb
|
177
|
+
- lib/faye/adapters/rainbows.rb
|
178
|
+
- lib/faye/rack_stream.rb
|
179
|
+
- examples/ws.html
|
180
|
+
- examples/config.ru
|
181
|
+
- examples/rainbows.conf
|
182
|
+
- examples/sse.html
|
156
183
|
- examples/app.rb
|
157
|
-
- examples/
|
184
|
+
- examples/server.rb
|
158
185
|
- examples/client.rb
|
159
|
-
- examples/config.ru
|
160
186
|
- examples/haproxy.conf
|
161
|
-
- examples/
|
162
|
-
- examples/sse.html
|
163
|
-
- examples/ws.html
|
164
|
-
- spec/faye/websocket/client_spec.rb
|
165
|
-
- spec/faye/websocket/draft75_parser_examples.rb
|
166
|
-
- spec/faye/websocket/draft75_parser_spec.rb
|
167
|
-
- spec/faye/websocket/draft76_parser_spec.rb
|
168
|
-
- spec/faye/websocket/hybi_parser_spec.rb
|
169
|
-
- spec/rainbows.conf
|
170
|
-
- spec/server.crt
|
171
|
-
- spec/server.key
|
172
|
-
- spec/spec_helper.rb
|
187
|
+
- examples/autobahn_client.rb
|
173
188
|
homepage: http://github.com/faye/faye-websocket-ruby
|
174
189
|
licenses: []
|
175
190
|
post_install_message:
|
176
191
|
rdoc_options:
|
177
192
|
- --main
|
178
|
-
- README.
|
193
|
+
- README.md
|
194
|
+
- --markup
|
195
|
+
- markdown
|
179
196
|
require_paths:
|
180
197
|
- lib
|
181
198
|
required_ruby_version: !ruby/object:Gem::Requirement
|
data/CHANGELOG.txt
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
=== 0.4.7 / 2013-02-14
|
2
|
-
|
3
|
-
* Emit the 'close' event if TCP is closed before CLOSE frame is acked
|
4
|
-
* Treat the 'Upgrade: websocket' header case-insensitively because of IE10
|
5
|
-
* Don't suppress headers in the Thin and Rainbows adapters unless the status is 101
|
6
|
-
|
7
|
-
|
8
|
-
=== 0.4.6 / 2012-07-09
|
9
|
-
|
10
|
-
* Add 'Connection: close' to EventSource response
|
11
|
-
|
12
|
-
|
13
|
-
=== 0.4.5 / 2012-04-06
|
14
|
-
|
15
|
-
* Add WebSocket error code 1011.
|
16
|
-
* Handle URLs with no path correctly by sending 'GET /'
|
17
|
-
|
18
|
-
|
19
|
-
=== 0.4.4 / 2012-03-16
|
20
|
-
|
21
|
-
* Fix installation on JRuby with a platform-specific gem
|
22
|
-
|
23
|
-
|
24
|
-
=== 0.4.3 / 2012-03-12
|
25
|
-
|
26
|
-
* Make extconf.rb a no-op on JRuby
|
27
|
-
|
28
|
-
|
29
|
-
=== 0.4.2 / 2012-03-09
|
30
|
-
|
31
|
-
* Port masking-function C extension to Java for JRuby
|
32
|
-
|
33
|
-
|
34
|
-
=== 0.4.1 / 2012-02-26
|
35
|
-
|
36
|
-
* Treat anything other than an Array as a string when calling send()
|
37
|
-
* Fix error loading UTF-8 validation code on Ruby 1.9 with -Ku flag
|
38
|
-
|
39
|
-
|
40
|
-
=== 0.4.0 / 2012-02-13
|
41
|
-
|
42
|
-
* Add ping() method to server-side WebSocket and EventSource
|
43
|
-
* Buffer send() calls until the draft-76 handshake is complete
|
44
|
-
* Fix HTTPS problems on Node 0.7
|
45
|
-
|
46
|
-
|
47
|
-
=== 0.3.0 / 2012-01-13
|
48
|
-
|
49
|
-
* Add support for EventSource connections
|
50
|
-
* Support the Thin, Rainbows and Goliath web servers
|
51
|
-
|
52
|
-
|
53
|
-
=== 0.2.0 / 2011-12-21
|
54
|
-
|
55
|
-
* Add support for Sec-WebSocket-Protocol negotiation
|
56
|
-
* Support hixie-76 close frames and 75/76 ignored segments
|
57
|
-
* Improve performance of HyBi parsing/framing functions
|
58
|
-
* Write masking function in C
|
59
|
-
|
60
|
-
|
61
|
-
=== 0.1.2 / 2011-12-05
|
62
|
-
|
63
|
-
* Make hixie-76 sockets work through HAProxy
|
64
|
-
|
65
|
-
|
66
|
-
=== 0.1.1 / 2011-11-30
|
67
|
-
|
68
|
-
* Fix add_event_listener() interface methods
|
69
|
-
|
70
|
-
|
71
|
-
=== 0.1.0 / 2011-11-27
|
72
|
-
|
73
|
-
* Initial release, based on WebSocket components from Faye
|
74
|
-
|
data/README.rdoc
DELETED
@@ -1,366 +0,0 @@
|
|
1
|
-
= Faye::WebSocket
|
2
|
-
|
3
|
-
* Travis CI build: {<img src="https://secure.travis-ci.org/faye/faye-websocket-ruby.png" />}[http://travis-ci.org/faye/faye-websocket-ruby]
|
4
|
-
* Autobahn tests: {server}[http://faye.jcoglan.com/autobahn/servers/], {client}[http://faye.jcoglan.com/autobahn/clients/]
|
5
|
-
|
6
|
-
This is a robust, general-purpose WebSocket implementation extracted from the
|
7
|
-
{Faye}[http://faye.jcoglan.com] project. It provides classes for easily building
|
8
|
-
WebSocket servers and clients in Ruby. It does not provide a server itself, but
|
9
|
-
rather makes it easy to handle WebSocket connections within an existing
|
10
|
-
{Rack}[http://rack.rubyforge.org/] application. It does not provide any
|
11
|
-
abstraction other than the standard
|
12
|
-
{WebSocket API}[http://dev.w3.org/html5/websockets/].
|
13
|
-
|
14
|
-
It also provides an abstraction for handling {EventSource}[http://dev.w3.org/html5/eventsource/]
|
15
|
-
connections, which are one-way connections that allow the server to push data to
|
16
|
-
the client. They are based on streaming HTTP responses and can be easier to
|
17
|
-
access via proxies than WebSockets.
|
18
|
-
|
19
|
-
Currently, the following web servers are supported, and can be accessed directly
|
20
|
-
or via HAProxy:
|
21
|
-
|
22
|
-
* {Thin}[http://code.macournoyer.com/thin/]
|
23
|
-
* {Rainbows}[http://rainbows.rubyforge.org/] using EventMachine
|
24
|
-
* {Goliath}[http://postrank-labs.github.com/goliath/]
|
25
|
-
|
26
|
-
The server-side socket can process {draft-75}[http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75],
|
27
|
-
{draft-76}[http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76],
|
28
|
-
{hybi-07}[http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07]
|
29
|
-
and later versions of the protocol. It selects protocol versions automatically,
|
30
|
-
supports both +text+ and +binary+ messages, and transparently handles +ping+,
|
31
|
-
+pong+, +close+ and fragmented messages.
|
32
|
-
|
33
|
-
|
34
|
-
== Handling WebSocket connections in Rack
|
35
|
-
|
36
|
-
You can handle WebSockets on the server side by listening for requests using the
|
37
|
-
<tt>Faye::WebSocket.websocket?</tt> method, and creating a new socket for the
|
38
|
-
request. This socket object exposes the usual WebSocket methods for receiving
|
39
|
-
and sending messages. For example this is how you'd implement an echo server:
|
40
|
-
|
41
|
-
# app.rb
|
42
|
-
require 'faye/websocket'
|
43
|
-
|
44
|
-
App = lambda do |env|
|
45
|
-
if Faye::WebSocket.websocket?(env)
|
46
|
-
ws = Faye::WebSocket.new(env)
|
47
|
-
|
48
|
-
ws.onmessage = lambda do |event|
|
49
|
-
ws.send(event.data)
|
50
|
-
end
|
51
|
-
|
52
|
-
ws.onclose = lambda do |event|
|
53
|
-
p [:close, event.code, event.reason]
|
54
|
-
ws = nil
|
55
|
-
end
|
56
|
-
|
57
|
-
# Return async Rack response
|
58
|
-
ws.rack_response
|
59
|
-
|
60
|
-
else
|
61
|
-
# Normal HTTP request
|
62
|
-
[200, {'Content-Type' => 'text/plain'}, ['Hello']]
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
This is a standard Rack app, so it can be run using a <tt>config.ru</tt> file.
|
67
|
-
However, so that incoming requests can be properly prepared to process WebSocket
|
68
|
-
connections, you need to tell <tt>Faye::WebSocket</tt> which adapter to load;
|
69
|
-
this can be either +thin+, +rainbows+ or +goliath+. If one of these servers is
|
70
|
-
already loaded before <tt>faye/websocket</tt> is loaded, it will load
|
71
|
-
appropriate adapters automatically.
|
72
|
-
|
73
|
-
# config.ru
|
74
|
-
require './app'
|
75
|
-
Faye::WebSocket.load_adapter('thin')
|
76
|
-
run App
|
77
|
-
|
78
|
-
Note that under certain circumstances (notably a draft-76 client connecting
|
79
|
-
through an HTTP proxy), the WebSocket handshake will not be complete after you
|
80
|
-
call `Faye::WebSocket.new` because the server will not have received the entire
|
81
|
-
handshake from the client yet. In this case, calls to `ws.send` will buffer the
|
82
|
-
message in memory until the handshake is complete, at which point any buffered
|
83
|
-
messages will be sent to the client.
|
84
|
-
|
85
|
-
If you need to detect when the WebSocket handshake is complete, you can use the
|
86
|
-
`onopen` event.
|
87
|
-
|
88
|
-
If the connection's protocol version supports it, you can call <tt>ws.ping()</tt>
|
89
|
-
to send a ping message and wait for the client's response. This method takes a
|
90
|
-
message string, and an optional callback that fires when a matching pong message
|
91
|
-
is received. It returns +true+ iff a ping message was sent. If the client does
|
92
|
-
not support ping/pong, this method sends no data and returns +false+.
|
93
|
-
|
94
|
-
ws.ping 'Mic check, one, two' do
|
95
|
-
# fires when pong is received
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
== Using the WebSocket client
|
100
|
-
|
101
|
-
The client supports both the plain-text +ws+ protocol and the encrypted +wss+
|
102
|
-
protocol, and has exactly the same interface as a socket you would use in a web
|
103
|
-
browser. On the wire it identifies itself as hybi-13.
|
104
|
-
|
105
|
-
require 'faye/websocket'
|
106
|
-
require 'eventmachine'
|
107
|
-
|
108
|
-
EM.run {
|
109
|
-
ws = Faye::WebSocket::Client.new('ws://www.example.com/')
|
110
|
-
|
111
|
-
ws.onopen = lambda do |event|
|
112
|
-
p [:open]
|
113
|
-
ws.send('Hello, world!')
|
114
|
-
end
|
115
|
-
|
116
|
-
ws.onmessage = lambda do |event|
|
117
|
-
p [:message, event.data]
|
118
|
-
end
|
119
|
-
|
120
|
-
ws.onclose = lambda do |event|
|
121
|
-
p [:close, event.code, event.reason]
|
122
|
-
ws = nil
|
123
|
-
end
|
124
|
-
}
|
125
|
-
|
126
|
-
|
127
|
-
== Subprotocol negotiation
|
128
|
-
|
129
|
-
The WebSocket protocol allows peers to select and identify the application
|
130
|
-
protocol to use over the connection. On the client side, you can set which
|
131
|
-
protocols the client accepts by passing a list of protocol names when you
|
132
|
-
construct the socket:
|
133
|
-
|
134
|
-
ws = Faye::WebSocket::Client.new('ws://www.example.com/', ['irc', 'amqp'])
|
135
|
-
|
136
|
-
On the server side, you can likewise pass in the list of protocols the server
|
137
|
-
supports after the other constructor arguments:
|
138
|
-
|
139
|
-
ws = Faye::WebSocket.new(env, ['irc', 'amqp'])
|
140
|
-
|
141
|
-
If the client and server agree on a protocol, both the client- and server-side
|
142
|
-
socket objects expose the selected protocol through the <tt>ws.protocol</tt>
|
143
|
-
property. If they cannot agree on a protocol to use, the client closes the
|
144
|
-
connection.
|
145
|
-
|
146
|
-
|
147
|
-
== WebSocket API
|
148
|
-
|
149
|
-
The WebSocket API consists of several event handlers and a method for sending
|
150
|
-
messages.
|
151
|
-
|
152
|
-
* <b><tt>onopen</tt></b> fires when the socket connection is established. Event
|
153
|
-
has no attributes.
|
154
|
-
* <b><tt>onerror</tt></b> fires when the connection attempt fails. Event has no
|
155
|
-
attributes.
|
156
|
-
* <b><tt>onmessage</tt></b> fires when the socket receives a message. Event has
|
157
|
-
one attribute, <b><tt>data</tt></b>, which is either a +String+ (for text
|
158
|
-
frames) or an +Array+ of byte-sized integers (for binary frames).
|
159
|
-
* <b><tt>onclose</tt></b> fires when either the client or the server closes the
|
160
|
-
connection. Event has two optional attributes, <b><tt>code</tt></b> and
|
161
|
-
<b><tt>reason</tt></b>, that expose the status code and message sent by the
|
162
|
-
peer that closed the connection.
|
163
|
-
* <b><tt>send(message)</tt></b> accepts either a +String+ or an +Array+ of
|
164
|
-
byte-sized integers and sends a text or binary message over the connection to
|
165
|
-
the other peer.
|
166
|
-
* <b><tt>close(code, reason)</tt></b> closes the connection, sending the given
|
167
|
-
status code and reason text, both of which are optional.
|
168
|
-
* <b><tt>protocol</tt></b> is a string (which may be empty) identifying the
|
169
|
-
subprotocol the socket is using.
|
170
|
-
|
171
|
-
|
172
|
-
== Handling EventSource connections in Rack
|
173
|
-
|
174
|
-
EventSource connections provide a very similar interface, although because they
|
175
|
-
only allow the server to send data to the client, there is no +onmessage+ API.
|
176
|
-
EventSource allows the server to push text messages to the client, where each
|
177
|
-
message has an optional event-type and ID.
|
178
|
-
|
179
|
-
# app.rb
|
180
|
-
require 'faye/websocket'
|
181
|
-
|
182
|
-
App = lambda do |env|
|
183
|
-
if Faye::EventSource.eventsource?(env)
|
184
|
-
es = Faye::EventSource.new(env)
|
185
|
-
p [:open, es.url, es.last_event_id]
|
186
|
-
|
187
|
-
# Periodically send messages
|
188
|
-
loop = EM.add_periodic_timer(1) { es.send('Hello') }
|
189
|
-
|
190
|
-
es.onclose = lambda do |event|
|
191
|
-
EM.cancel_timer(loop)
|
192
|
-
es = nil
|
193
|
-
end
|
194
|
-
|
195
|
-
# Return async Rack response
|
196
|
-
es.rack_response
|
197
|
-
|
198
|
-
else
|
199
|
-
# Normal HTTP request
|
200
|
-
[200, {'Content-Type' => 'text/plain'}, ['Hello']]
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
The +send+ method takes two optional parameters, <tt>:event</tt> and
|
205
|
-
<tt>:id</tt>. The default event-type is <tt>'message'</tt> with no ID. For
|
206
|
-
example, to send a +notification+ event with ID +99+:
|
207
|
-
|
208
|
-
es.send('Breaking News!', :event => 'notification', :id => '99')
|
209
|
-
|
210
|
-
The +EventSource+ object exposes the following properties:
|
211
|
-
|
212
|
-
* <b><tt>url</tt></b> is a string containing the URL the client used to create
|
213
|
-
the EventSource.
|
214
|
-
* <b><tt>last_event_id</tt></b> is a string containing the last event ID
|
215
|
-
received by the client. You can use this when the client reconnects after a
|
216
|
-
dropped connection to determine which messages need resending.
|
217
|
-
|
218
|
-
When you initialize an EventSource with <tt>Faye::EventSource.new</tt>, you can
|
219
|
-
pass configuration options after the +env+ parameter. Available options are:
|
220
|
-
|
221
|
-
* <b><tt>:retry</tt></b> is a number that tells the client how long (in seconds)
|
222
|
-
it should wait after a dropped connection before attempting to reconnect.
|
223
|
-
* <b><tt>:ping</tt></b> is a number that tells the server how often (in seconds)
|
224
|
-
to send 'ping' packets to the client to keep the connection open, to defeat
|
225
|
-
timeouts set by proxies. The client will ignore these messages.
|
226
|
-
|
227
|
-
For example, this creates a connection that pings every 15 seconds and is
|
228
|
-
retryable every 10 seconds if the connection is broken:
|
229
|
-
|
230
|
-
es = Faye::EventSource.new(es, :ping => 15, :retry => 10)
|
231
|
-
|
232
|
-
You can send a ping message at any time by calling <tt>es.ping()</tt>. Unlike
|
233
|
-
WebSocket the client does not send a response to this; it is merely to send some
|
234
|
-
data over the wire to keep the connection alive.
|
235
|
-
|
236
|
-
|
237
|
-
== Running your socket application
|
238
|
-
|
239
|
-
To use this library you must be using an EventMachine-based server; currently
|
240
|
-
Thin, Rainbows and Goliath are supported.
|
241
|
-
|
242
|
-
|
243
|
-
=== Running the app with Thin
|
244
|
-
|
245
|
-
Thin can be started via the command line if you've set up a <tt>config.ru</tt>
|
246
|
-
file for your application:
|
247
|
-
|
248
|
-
thin start -R config.ru -p 9292
|
249
|
-
|
250
|
-
Or, you can use +rackup+. In development mode, this adds middlewares that don't
|
251
|
-
work with async apps, so you must start it in production mode:
|
252
|
-
|
253
|
-
rackup config.ru -s thin -E production -p 9292
|
254
|
-
|
255
|
-
It can also be started using the <tt>Rack::Handler</tt> interface common to many
|
256
|
-
Ruby servers. It must be run using EventMachine, and you can configure Thin
|
257
|
-
further in a block passed to +run+:
|
258
|
-
|
259
|
-
require 'eventmachine'
|
260
|
-
require 'rack'
|
261
|
-
require 'thin'
|
262
|
-
require './app'
|
263
|
-
|
264
|
-
EM.run {
|
265
|
-
thin = Rack::Handler.get('thin')
|
266
|
-
|
267
|
-
thin.run(App, :Port => 9292) do |server|
|
268
|
-
# You can set options on the server here, for example to set up SSL:
|
269
|
-
server.ssl_options = {
|
270
|
-
:private_key_file => 'path/to/ssl.key',
|
271
|
-
:cert_chain_file => 'path/to/ssl.crt'
|
272
|
-
}
|
273
|
-
server.ssl = true
|
274
|
-
end
|
275
|
-
}
|
276
|
-
|
277
|
-
|
278
|
-
=== Running the app with Rainbows
|
279
|
-
|
280
|
-
<tt>Faye::WebSocket</tt> can only be run using EventMachine. To begin with,
|
281
|
-
you'll need a Rainbows config file that tells it to use EventMachine, along with
|
282
|
-
whatever Rainbows/Unicorn configuration you require.
|
283
|
-
|
284
|
-
# rainbows.conf
|
285
|
-
Rainbows! do
|
286
|
-
use :EventMachine
|
287
|
-
end
|
288
|
-
|
289
|
-
You can then run your <tt>config.ru</tt> file from the command line. Again,
|
290
|
-
<tt>Rack::Lint</tt> will complain unless you put the application in production
|
291
|
-
mode.
|
292
|
-
|
293
|
-
rainbows config.ru -c path/to/rainbows.conf -E production -p 9292
|
294
|
-
|
295
|
-
Rainbows also has a Ruby API for starting a server:
|
296
|
-
|
297
|
-
require 'rainbows'
|
298
|
-
require './app'
|
299
|
-
|
300
|
-
rackup = Unicorn::Configurator::RACKUP
|
301
|
-
rackup[:port] = 9292
|
302
|
-
rackup[:set_listener] = true
|
303
|
-
options = rackup[:options]
|
304
|
-
options[:config_file] = 'path/to/rainbows.conf'
|
305
|
-
|
306
|
-
server = Rainbows::HttpServer.new(App, options)
|
307
|
-
|
308
|
-
# This is non-blocking; use server.start.join to block
|
309
|
-
server.start
|
310
|
-
|
311
|
-
|
312
|
-
=== Running the app with Goliath
|
313
|
-
|
314
|
-
Goliath can be made to run arbitrary Rack apps by delegating to them from a
|
315
|
-
<tt>Goliath::API</tt> instance. A simple server looks like this:
|
316
|
-
|
317
|
-
require 'goliath'
|
318
|
-
require './app'
|
319
|
-
|
320
|
-
class EchoServer < Goliath::API
|
321
|
-
def response(env)
|
322
|
-
App.call(env)
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
<tt>Faye::WebSocket</tt> can also be used inline within a Goliath app:
|
327
|
-
|
328
|
-
require 'goliath'
|
329
|
-
require 'faye/websocket'
|
330
|
-
|
331
|
-
class EchoServer < Goliath::API
|
332
|
-
def response(env)
|
333
|
-
ws = Faye::WebSocket.new(env)
|
334
|
-
|
335
|
-
ws.onmessage = lambda do |event|
|
336
|
-
ws.send(event.data)
|
337
|
-
end
|
338
|
-
|
339
|
-
ws.rack_response
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
|
344
|
-
== License
|
345
|
-
|
346
|
-
(The MIT License)
|
347
|
-
|
348
|
-
Copyright (c) 2009-2013 James Coglan
|
349
|
-
|
350
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
351
|
-
this software and associated documentation files (the 'Software'), to deal in
|
352
|
-
the Software without restriction, including without limitation the rights to use,
|
353
|
-
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
354
|
-
Software, and to permit persons to whom the Software is furnished to do so,
|
355
|
-
subject to the following conditions:
|
356
|
-
|
357
|
-
The above copyright notice and this permission notice shall be included in all
|
358
|
-
copies or substantial portions of the Software.
|
359
|
-
|
360
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
361
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
362
|
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
363
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
364
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
365
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
366
|
-
|