_bushido-faye-websocket 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG.txt +56 -0
  2. data/README.rdoc +366 -0
  3. data/examples/app.rb +50 -0
  4. data/examples/autobahn_client.rb +44 -0
  5. data/examples/client.rb +30 -0
  6. data/examples/config.ru +17 -0
  7. data/examples/haproxy.conf +21 -0
  8. data/examples/server.rb +44 -0
  9. data/examples/sse.html +39 -0
  10. data/examples/ws.html +44 -0
  11. data/ext/faye_websocket_mask/FayeWebsocketMaskService.java +61 -0
  12. data/ext/faye_websocket_mask/extconf.rb +5 -0
  13. data/ext/faye_websocket_mask/faye_websocket_mask.c +33 -0
  14. data/lib/faye/adapters/goliath.rb +47 -0
  15. data/lib/faye/adapters/rainbows.rb +32 -0
  16. data/lib/faye/adapters/rainbows_client.rb +70 -0
  17. data/lib/faye/adapters/thin.rb +62 -0
  18. data/lib/faye/eventsource.rb +124 -0
  19. data/lib/faye/websocket.rb +216 -0
  20. data/lib/faye/websocket/adapter.rb +21 -0
  21. data/lib/faye/websocket/api.rb +96 -0
  22. data/lib/faye/websocket/api/event.rb +33 -0
  23. data/lib/faye/websocket/api/event_target.rb +34 -0
  24. data/lib/faye/websocket/client.rb +84 -0
  25. data/lib/faye/websocket/draft75_parser.rb +87 -0
  26. data/lib/faye/websocket/draft76_parser.rb +84 -0
  27. data/lib/faye/websocket/hybi_parser.rb +320 -0
  28. data/lib/faye/websocket/hybi_parser/handshake.rb +78 -0
  29. data/lib/faye/websocket/hybi_parser/stream_reader.rb +29 -0
  30. data/lib/faye/websocket/utf8_match.rb +8 -0
  31. data/spec/faye/websocket/client_spec.rb +179 -0
  32. data/spec/faye/websocket/draft75_parser_examples.rb +48 -0
  33. data/spec/faye/websocket/draft75_parser_spec.rb +27 -0
  34. data/spec/faye/websocket/draft76_parser_spec.rb +34 -0
  35. data/spec/faye/websocket/hybi_parser_spec.rb +156 -0
  36. data/spec/rainbows.conf +3 -0
  37. data/spec/server.crt +15 -0
  38. data/spec/server.key +15 -0
  39. data/spec/spec_helper.rb +68 -0
  40. metadata +158 -0
@@ -0,0 +1,56 @@
1
+ === 0.4.4 / 2012-03-16
2
+
3
+ * Fix installation on JRuby with a platform-specific gem
4
+
5
+
6
+ === 0.4.3 / 2012-03-12
7
+
8
+ * Make extconf.rb a no-op on JRuby
9
+
10
+
11
+ === 0.4.2 / 2012-03-09
12
+
13
+ * Port masking-function C extension to Java for JRuby
14
+
15
+
16
+ === 0.4.1 / 2012-02-26
17
+
18
+ * Treat anything other than an Array as a string when calling send()
19
+ * Fix error loading UTF-8 validation code on Ruby 1.9 with -Ku flag
20
+
21
+
22
+ === 0.4.0 / 2012-02-13
23
+
24
+ * Add ping() method to server-side WebSocket and EventSource
25
+ * Buffer send() calls until the draft-76 handshake is complete
26
+ * Fix HTTPS problems on Node 0.7
27
+
28
+
29
+ === 0.3.0 / 2012-01-13
30
+
31
+ * Add support for EventSource connections
32
+ * Support the Thin, Rainbows and Goliath web servers
33
+
34
+
35
+ === 0.2.0 / 2011-12-21
36
+
37
+ * Add support for Sec-WebSocket-Protocol negotiation
38
+ * Support hixie-76 close frames and 75/76 ignored segments
39
+ * Improve performance of HyBi parsing/framing functions
40
+ * Write masking function in C
41
+
42
+
43
+ === 0.1.2 / 2011-12-05
44
+
45
+ * Make hixie-76 sockets work through HAProxy
46
+
47
+
48
+ === 0.1.1 / 2011-11-30
49
+
50
+ * Fix add_event_listener() interface methods
51
+
52
+
53
+ === 0.1.0 / 2011-11-27
54
+
55
+ * Initial release, based on WebSocket components from Faye
56
+
@@ -0,0 +1,366 @@
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-2012 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
+
@@ -0,0 +1,50 @@
1
+ require File.expand_path('../../lib/faye/websocket', __FILE__)
2
+ require 'rack'
3
+
4
+ static = Rack::File.new(File.dirname(__FILE__))
5
+
6
+ App = lambda do |env|
7
+ if Faye::WebSocket.websocket?(env)
8
+ ws = Faye::WebSocket.new(env, ['irc', 'xmpp'], :ping => 5)
9
+ p [:open, ws.url, ws.version, ws.protocol]
10
+
11
+ ws.onmessage = lambda do |event|
12
+ ws.send(event.data)
13
+ end
14
+
15
+ ws.onclose = lambda do |event|
16
+ p [:close, event.code, event.reason]
17
+ ws = nil
18
+ end
19
+
20
+ ws.rack_response
21
+
22
+ elsif Faye::EventSource.eventsource?(env)
23
+ es = Faye::EventSource.new(env)
24
+ time = es.last_event_id.to_i
25
+
26
+ p [:open, es.url, es.last_event_id]
27
+
28
+ loop = EM.add_periodic_timer(2) do
29
+ time += 1
30
+ es.send("Time: #{time}")
31
+ EM.add_timer(1) do
32
+ es.send('Update!!', :event => 'update', :id => time) if es
33
+ end
34
+ end
35
+
36
+ es.send("Welcome!\n\nThis is an EventSource server.")
37
+
38
+ es.onclose = lambda do |event|
39
+ EM.cancel_timer(loop)
40
+ p [:close, es.url]
41
+ es = nil
42
+ end
43
+
44
+ es.rack_response
45
+
46
+ else
47
+ static.call(env)
48
+ end
49
+ end
50
+