webtube 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ GPL-3
2
+ Manifest.txt
3
+ README
4
+ bin/wsc
5
+ lib/webtube.rb
6
+ lib/webtube/vital-statistics.rb
7
+ lib/webtube/webrick.rb
8
+ sample-server.rb
9
+ webtube.gemspec
data/README ADDED
@@ -0,0 +1,779 @@
1
+ This is Webtube, a Ruby implementation of the WebSocket protocol defined in
2
+ RFC 6455.
3
+
4
+
5
+ == Sample client
6
+
7
+ (Also see [[wsc]], a basic command line utility for talking to WebSocket
8
+ servers and included with the Webtube distribution.)
9
+
10
+ require 'webtube'
11
+
12
+ $webtube = Webtube.connect 'ws://echo.websocket.org/'
13
+ $webtube.send_message 'Hello, echo server!'
14
+
15
+ class << $listener = Object.new
16
+ def onmessage webtube, message, opcode
17
+ puts "The echo server says: #{message.inspect}"
18
+ # We only care about one message.
19
+ $webtube.close
20
+ return
21
+ end
22
+ end
23
+ $webtube.run $listener
24
+
25
+
26
+ == Sample server
27
+
28
+ (This code is also available as a separate file; see [[sample-server.rb]].)
29
+
30
+ #! /usr/bin/ruby
31
+
32
+ # A sample WEBrick server using the Webtube API. It listens on port 8888 and
33
+ # provides two services: [[/diag]], which logs all the events from
34
+ # [[Webtube#run]] and remains silent towards the client (although note that
35
+ # the Webtube library pongs the pings), and [[/echo]], which echos.
36
+
37
+ require 'webrick'
38
+ require 'webtube/webrick'
39
+
40
+ class << diagnostic_listener = Object.new
41
+ def respond_to? name
42
+ return (name.to_s =~ /^on/ or super)
43
+ end
44
+
45
+ def method_missing name, *args
46
+ output = "- #{name}("
47
+ args.each_with_index do |arg, i|
48
+ output << ', ' unless i.zero?
49
+ if i.zero? and arg.is_a? Webtube then
50
+ output << arg.to_s
51
+ else
52
+ output << arg.inspect
53
+ end
54
+ end
55
+ output << ")"
56
+ puts output
57
+ return
58
+ end
59
+ end
60
+
61
+ class << echo_listener = Object.new
62
+ def onmessage webtube, data, opcode
63
+ webtube.send_message data, opcode
64
+ return
65
+ end
66
+ end
67
+
68
+ server = WEBrick::HTTPServer.new(:Port => 8888)
69
+ server.mount_webtube '/diag', diagnostic_listener
70
+ server.mount_webtube '/echo', echo_listener
71
+
72
+ begin
73
+ server.start
74
+ ensure
75
+ server.shutdown
76
+ end
77
+
78
+
79
+ == WebSocketCat's commands
80
+
81
+ Webtube comes with [[wsc]], a command line utility for talking to WebSocket
82
+ server. A session might look like this:
83
+
84
+ $ wsc ws://echo.websocket.org/
85
+ Connecting to ws://echo.websocket.org/ ...
86
+ | 101 Web Socket Protocol Handshake
87
+ | connection: Upgrade
88
+ | date: Fri, 10 Oct 2014 12:43:49 GMT
89
+ | sec-websocket-accept: Uk2zpTKu2TaQtJ2esybCgeB89qk=
90
+ | server: Kaazing Gateway
91
+ | upgrade: websocket
92
+
93
+ *** open
94
+ Hello, server!
95
+ <<< Hello, server!
96
+ /ping
97
+ (Ping sent.)
98
+ *** pong ""
99
+ /close 1001
100
+ *** close
101
+
102
+ Prefixed with [[|]] is the HTTP response header from the server, with [[<<<]]
103
+ are incoming text messages (non-text messages are prefixed with [[<N<]] where N
104
+ is the message opcode), and with [[***]] are miscellaneous other events. Lines
105
+ entered by user are sent to the server as text messages. The user can invoke
106
+ some special commands using the slash prefix:
107
+
108
+ - [[/ping [message]]] sends a ping frame to the server.
109
+
110
+ - [[/close [status [explanation]]]] sends a close frame to the server. The
111
+ status code is specified as an unsigned decimal number.
112
+
113
+ - [[/N [payload]]] sends a message or control frame of opcode [[N]], given as a
114
+ single hex digit, to the server. Per protocol specification, [[/1]] is text
115
+ message, [[/2]] is binary message, [[/8]] is close, [[/9]] is ping, [[/A]] is
116
+ pong. Other opcodes can have application-specific meaning. Note that the
117
+ specification requires kicking clients (or servers) who send messages so
118
+ cryptic that the server (or client) can't understand them.
119
+
120
+ - [[/help]] shows online help.
121
+
122
+ If you need to start a text message with a slash, you can double it for escape,
123
+ or you can use the explicit [[/1]] command. EOF from stdin is equivalent to
124
+ [[/close 1000]].
125
+
126
+
127
+ == API overview
128
+
129
+ === The [[Webtube]] class
130
+
131
+ (Direct) instances of the [[Webtube]] class are hashed and [[eql?]]-compared by
132
+ identity, thus behaving in an intuitive manner when used as keys of a [[Hash]]
133
+ or elements of a [[Set]].
134
+
135
+ In addition to the methods described below, each [[Webtube]] instance will have
136
+ the readable and writable attributes [[header]], [[session]], and [[context]].
137
+ [[Webtube]] does not care about them; they are intended to facilitate user code
138
+ associating contextual or environmental data with the [[Webtube]].
139
+
140
+ The [[Webtube]]-[[WEBrick]] integration (in particular,
141
+ [[WEBrick::HTTPServer#accept_webtube]]) sets the [[header]] attribtue of newly
142
+ created [[Webtube]] instances to the [[WEBrick::HTTPRequest]] used to establish
143
+ the WebSocket connection; this may facilitate extracting HTTP header fields,
144
+ URL query parameters or cookies from the request at a later time. (Note that
145
+ because the upgrade request is necessary delivered using the HTTP [[GET]]
146
+ method, the HTTP file upload protocol, which requires [[POST]] is not available
147
+ for WebSocket connections. Also note that the WebDAV extensions are mutually
148
+ incompatible with the WebSocket protocol, within the bounds of a single HTTP
149
+ request, for the same reason.)
150
+
151
+
152
+ ==== [[Webtube::connect]]: connect to a remote server
153
+
154
+ A WebSocket connection to a remote server can be set up by calling
155
+ [[Webtube::connect]]. The calling interface goes like this:
156
+
157
+ Webtube::connect(url,
158
+ header_fields: {},
159
+ ssl_verify_mode: nil,
160
+ on_http_response: nil,
161
+ allow_rsv_bits: 0,
162
+ allow_opcodes: [Webtube::OPCODE_TEXT, Webtube::OPCODE_BINARY],
163
+ close_socket: true)
164
+
165
+ - [[url]] is a [[String]] representing the target of the connection in the URL
166
+ form, using either the [[ws:]] or [[wss:]] protocol prefix. A convenient
167
+ public server for basic testing is available on [[ws://echo.websocket.org/]].
168
+
169
+ - [[header_fields]] is a [[Hash]] for specifying HTTP header fields for the
170
+ request. [[Webtube]] will consider entries specified here to have a priority
171
+ over automatically created header fields even for the fields defined by the
172
+ WebSocket standard such as [[Upgrade]] and [[Sec-WebSocket-Key]]; this should
173
+ be used cautiously.
174
+
175
+ - [[ssl_verify_mode]], if not [[nil]] and if the connection is encrypted, will
176
+ override OpenSSL's default mode of verifying the certificate at the beginning
177
+ of the SSL or TLS session. Supported values are
178
+ [[OpenSSL::SSL::VERIFY_PEER]] (the default, and the recommended value) and
179
+ [[OpenSSL::SSL::VERIFY_NONE]] (to be used with great caution). If the [[url]]
180
+ parameter has an [[ws:]] prefix, so the connection is not to be encrypted,
181
+ [[ssl_verify_mode]] has no effect.
182
+
183
+ - [[on_http_response]], if supplied, will be called with the
184
+ [[Net::HTTPResponse]] instance representing the server's HTTP-level response
185
+ to the request to initiate a WebSocket connection. This allows the client
186
+ code, for an example, to display the response header fields to the user.
187
+
188
+ The following fields will be passed on to [[Webtube::new]] intact:
189
+
190
+ - [[allow_rsv_bits]] is an [[Integer]], a bitmap of the reserved bits (4 for
191
+ RSV1, 2 for RSV2, 1 for RSV3) that, when appearing on inbound frames, should
192
+ be considered 'known'. The WebSocket protocol specification mandates failing
193
+ the connection if a frame with unknown reserved bits should arrive, and
194
+ [[Webtube]] complies. Note that the current version of [[Webtube]] does not
195
+ offer a convenient way for the client code to access the reserved bits of data
196
+ messages, only of control frames.
197
+
198
+ - [[allow_opcodes]] specifies the opcodes of messages and control frames that
199
+ should be considered 'known'. The WebSocket protocol specification mandates
200
+ failing the connection if a frame with an unknown opcode should arrive, and
201
+ [[Webtube]] complies. The [[Webtube]] instance will store this object and use
202
+ its [[include?]] method for the test, thus allowing either [[Array]], [[Set]]
203
+ or [[Range]] instances to work. The opcodes subject to this kind of filtering
204
+ are the data message opcodes 1-7 and the control frame opcodes 8-15; note,
205
+ however, that the control frame opcodes 8 ([[OPCODE_CLOSE]]), 9
206
+ ([[OPCODE_PING]]), and 10 ([[OPCODE_PONG]]) are essential infrastructural
207
+ opcodes defined by the standard, so [[Webtube]] will always consider them
208
+ 'known'. However, control frames of these opcodes will be passed to the
209
+ [[oncontrolframe]] event handler (if any) only if [[allow_opcodes]] approves
210
+ such opcodes.
211
+
212
+ - [[close_socket]] specifies whether the [[Webtube]] instance should close the
213
+ [[Socket]] when the connection is terminated. The default is [[true]]; it
214
+ may need to be set to [[false]] in order to suppress [[Webtube]]'s closure of
215
+ its socket in contexts sockets are managed by other means, such as the WEBrick
216
+ server.
217
+
218
+ Upon success, [[Webtube::connect]] will return the [[Webtube]] instance
219
+ representing the client endpoint. Upon WebSocket-level failure with lower
220
+ layers intact, a [[Webtube::WebSocketUpgradeFailed]] exception will be thrown
221
+ instead. This exception derives from [[StandardError]] as most run-time
222
+ errors. In the the current version of [[Webtube]], the specific known
223
+ subclasses are:
224
+
225
+ - [[Webtube::WebSocketDeclined]] for when the server is not responding to the
226
+ WebSocket connection initiation request affirmatively (which can, by design
227
+ of the protocol, mean that the server wants to speak plain old HTTP);
228
+
229
+ - [[Webtube::WebSocketVersionMismatch]] for when the server requests usage of a
230
+ WebSocket protocol version that [[Webtube]] does not know. At the time of
231
+ [[Webtube]]'s creation, only one WebSocket protocol version -- namely, 13 --
232
+ has been defined (by RFC 6455]), but this may change in the future, and it is
233
+ possible that a hypothetical future server will not be backwards compatible to
234
+ the original WebSocket protocol.
235
+
236
+ Other exceptions representing TCP-level, HTTP-level, or infrastructure failures
237
+ can also occur.
238
+
239
+
240
+ ==== [[Webtube::new]]: wrap a [[Socket]] into a [[Webtube]]
241
+
242
+ An instance of [[Webtube]] represents an endpoint of a WebSocket connection.
243
+ Because the protocol's core is symmetric, both ends can be represented by
244
+ instances of the same class. Note that the constructor assumes the opening
245
+ handshake and initial negotiation is complete. Code needing to connect to a
246
+ remote WebSocket server should usually call [[Webtube::connect]] instead of
247
+ invoking the constructor directly. Code needing to accept incoming WebSocket
248
+ connections should usually call the server-specific glue code such as
249
+ [[WEBrick::HTTPServer#accept_webtube]] (available after [[require
250
+ 'webtube/webrick']]).
251
+
252
+ The constructor's calling interface goes like this:
253
+
254
+ Webtube::new(socket, serverp,
255
+ allow_rsv_bits: 0,
256
+ allow_opcodes: [Webtube::OPCODE_TEXT],
257
+ close_socket: true)
258
+
259
+ - [[socket]] is the underlying [[Socket]] instance for sending and receiving
260
+ data.
261
+
262
+ - [[serverp]] is a Boolean indicating whether this socket represents the server
263
+ (as contrary to the client) side of the connection. While the WebSocket
264
+ protocol is largely symmetric, it requires a special masking procedure on
265
+ frames transmitted by the client to the server, and prohibits it on frames
266
+ transmitted by the server to the client. Along with masking itself, this is
267
+ reflected in a header flag of each frame.
268
+
269
+ - [[allow_rsv_bits]] is an [[Integer]], a bitmap of the reserved bits (4 for
270
+ RSV1, 2 for RSV2, 1 for RSV3) that, when appearing on inbound frames, should
271
+ be considered 'known'. The WebSocket protocol specification mandates failing
272
+ the connection if a frame with unknown reserved bits should arrive, and
273
+ [[Webtube]] complies. Note that the current version of [[Webtube]] does not
274
+ offer a convenient way for the client code to access the reserved bits of data
275
+ messages, only of control frames.
276
+
277
+ - [[allow_opcodes]] specifies the opcodes of messages and control frames that
278
+ should be considered 'known'. The WebSocket protocol specification mandates
279
+ failing the connection if a frame with an unknown opcode should arrive, and
280
+ [[Webtube]] complies. The [[Webtube]] instance will store this object and use
281
+ its [[include?]] method for the test, thus allowing either [[Array]], [[Set]]
282
+ or [[Range]] instances to work. The opcodes subject to this kind of filtering
283
+ are the data message opcodes 1-7 and the control frame opcodes 8-15; note,
284
+ however, that the control frame opcodes 8 ([[OPCODE_CLOSE]]), 9
285
+ ([[OPCODE_PING]]), and 10 ([[OPCODE_PONG]]) are essential infrastructural
286
+ opcodes defined by the standard, so [[Webtube]] will always consider them
287
+ 'known'. However, control frames of these opcodes will be passed to the
288
+ [[oncontrolframe]] event handler (if any) only if [[allow_opcodes]] approves
289
+ such opcodes.
290
+
291
+ - [[close_socket]] specifies whether the [[Webtube]] instance should close the
292
+ [[Socket]] when the connection is terminated. The default is [[true]]; it
293
+ may need to be set to [[false]] in order to suppress [[Webtube]]'s closure of
294
+ its socket in contexts sockets are managed by other means, such as the WEBrick
295
+ server.
296
+
297
+
298
+ ==== [[Webtube#run]]: the loop for incoming events
299
+
300
+ run(listener)
301
+
302
+ This method runs a loop to read all the messages and control frames coming in
303
+ via this WebSocket, and hands events to the given [[listener]]. The listener
304
+ can implement the following methods:
305
+
306
+ - [[onopen(webtube)]] will be called as soon as the channel is set up.
307
+
308
+ - [[onmessage(webtube, message_body, opcode)]] will be called with each
309
+ arriving data message once it has been defragmented. The data will be passed
310
+ to it as a [[String]], encoded in [[UTF-8]] for [[OPCODE_TEXT]] messages and
311
+ in [[ASCII-8BIT]] for all the other message opcodes.
312
+
313
+ - oncontrolframe(webtube, frame) will be called upon receipt of a control frame
314
+ whose opcode is listed in the [[allow_opcodes]] parameter of this [[Webtube]]
315
+ instance. The frame is repreented by an instance of [[Webtube::Frame]]. Note
316
+ that [[Webtube]] handles connection closures ([[OPCODE_CLOSE]]) and ponging
317
+ all the pings ([[OPCODE_PING]]) automatically.
318
+
319
+ - [[onping(webtube, frame)]] will be called upon receipt of an [[OPCODE_PING]]
320
+ frame. [[Webtube]] will take care of ponging all the pings, but the
321
+ listener may want to process such an event for statistical information.
322
+
323
+ - [[onpong(webtube, frame)]] will be called upon receipt of an [[OPCODE_PONG]]
324
+ frame.
325
+
326
+ - [[onclose(webtube)]] will be called upon closure of the connection, for any
327
+ reason.
328
+
329
+ - [[onannoyedclose(webtube, frame)]] will be called upon receipt of an
330
+ [[OPCODE_CLOSE]] frame with an explicit status code other than 1000.
331
+ This typically indicates that the other side is annoyed, so the listener
332
+ may want to log the condition for debugging or further analysis.
333
+ Normally, once the handler returns, [[Webtube]] will respond with a close
334
+ frame of the same status code and close the connection, but the handler
335
+ may call [[Webtube#close]] to request a closure with a different status
336
+ code or without one.
337
+
338
+ - [[onexception(webtube, exception)]] will be called if an unhandled
339
+ [[Exception]] is raised during the [[Webtube]]'s lifecycle, including all of
340
+ the listener event handlers. The handler may log the exception but should
341
+ return normally so that the [[Webtube]] can issue a proper close frame for
342
+ the other end and invoke the [[onclose]] handler, after which the exception
343
+ will be raised again so the caller of [[Webtube#run]] will have a chance of
344
+ handling the exception.
345
+
346
+ Before calling any of the handlers, [[respond_to?]] will be used to check
347
+ implementedness.
348
+
349
+ If an exception occurs during processing, it may implement a specific
350
+ status code to be passed to the other end via the [[OPCODE_CLOSE]] frame by
351
+ implementing the [[websocket_close_status_code]] method returning the code
352
+ as an integer. The default code, used if the exception does not specify
353
+ one, is 1011 'unexpected condition'. An exception may explicitly suppress
354
+ sending any code by having [[websocket_close_status_code]] return [[nil]]
355
+ instead of an integer.
356
+
357
+ Note that [[Webtube#run]] will not return until the connection will have been
358
+ closed. If the caller needs to get other work done in the connection's
359
+ lifetime, it will need to either handle this work inside calls to the
360
+ [[listener]] or set up separate [[Thread]]s for the [[Webtube#run]] and for the
361
+ other work.
362
+
363
+ [[Webtube#run]] will raise instances of [[Webtube::ProtocolError]] if events
364
+ that the WebSocket protocol does not permit should happen. In the current
365
+ version of [[Webtube]], the specific known subclasses are:
366
+
367
+ - [[Webtube::BrokenFrame]] indicates that a complete frame could not be read
368
+ from the underlying TCP connection. The [[partial_frame]] attribute holds as
369
+ much data, as a [[String]] of [[ASCII-8BIT]] encoding, as was available.
370
+
371
+ - [[Webtube::UnknownReservedBit]] indicates that a frame with an RSV bit not
372
+ specifically permitted by the [[allow_rsv_bits]] parameter was received. The
373
+ [[frame]] attribute holds the frame as a [[Webtube::Frame]] instance.
374
+
375
+ - [[Webtube::UnknownOpcode]] indicates that a frame with an opcode not
376
+ specifically permitted by the [[allow_opcodes]] parameter, or by the standard,
377
+ was received. The [[frame]] attribute holds the frame as a [[Webtube::Frame]]
378
+ instance. Note that if the opcode indicates a data message (as contrary to a
379
+ control frame), the [[frame]] will hold only its first fragment, as WebSocket
380
+ data messages are subject to fragmentation and the message's opcode is stored
381
+ in the first fragment.
382
+
383
+ - [[Webtube::UnmaskedFrameToServer]] indicates that a [[Webtube]] running in
384
+ server mode received a frame without masking. As per the WebSocket standard,
385
+ [[Webtube]] considers this a fatal protocol failure. The [[frame]] attribute
386
+ holds the frame as a [[Webtube::Frame]] instance.
387
+
388
+ - [[Webtube::MaskedFrameToClient]] indicates that a [[Webtube]] running in
389
+ client mode received a frame with masking. As per the WebSocket standard,
390
+ [[Webtube]] considers this a fatal protocol failure. The [[frame]] attribute
391
+ holds the frame as a [[Webtube::Frame]] instance.
392
+
393
+ - [[Webtube::MissingContinuationFrame]] indicates receipt of a new data message
394
+ initial frame while the [[Webtube]] was expecting a continuation frame of a
395
+ fragmented data message. Note that control frames are permitted to arrive
396
+ interleaved with fragments of a data message.
397
+
398
+ - [[Webtube::UnexpectedContinuationFrame]] indicates receipt of a data message
399
+ continuation frame while the [[Webtube]] was not expecting one. The [[frame]]
400
+ attribute holds the frame as a [[Webtube::Frame]] instance.
401
+
402
+ - [[Webtube::BadlyEncodedText]] indicates receipt of a text message
403
+ ([[OPCODE_TEXT]]) whose content is not a valid UTF-8 string. As per the
404
+ WebSocket standard, [[Webtube]] considers this a fatal protocol failure. The
405
+ [[data]] attribute holds the payload as a [[String]] instance of the
406
+ [[ASCII-8BIT]] encoding.
407
+
408
+ - [[Webtube::FragmenedControlFrame]] indicates receipt of a control frame whose
409
+ [[FIN]] flag is not set.
410
+
411
+ Other exceptions representing TCP-level, HTTP-level, or infrastructure failures
412
+ can also occur.
413
+
414
+
415
+ ==== [[Webtube#send_message]]: transmit a message or control frame
416
+
417
+ send_message(payload, opcode = Webtube::OPCODE_TEXT)
418
+
419
+ This method transmits the given [[payload]], a [[String]], over this WebSocket
420
+ connection to its other end using the given [[opcode]]. If [[opcode]] is
421
+ [[Webtube::OPCODE_TEXT]] and [[payload]] is not encoded in [[UTF-8]], it will
422
+ recode the payload to [[UTF-8]] first, as required by the WebSocket standard.
423
+
424
+ It is safe to call [[send_message]] from multiple threads concurrently; each
425
+ [[Webtube]] uses an internal lock to make sure that two data messages, despite
426
+ possible fragmentation, will not be interleaved.
427
+
428
+ An exception will be raised if [[send_message]] is called after closure of the
429
+ [[Webtube]]. The exception's class and ancestry is currently not defined,
430
+ except that it will derive, directly or indirectly, from [[StandardError]]. It
431
+ may derive from [[RuntimeError]] but this is not guaranteed.
432
+
433
+
434
+ ==== [[Webtube#close]]: close a WebSocket connection
435
+
436
+ close(status_code = 1000, explanation = "")
437
+
438
+ This method transmits an [[OPCODE_CLOSE]] control frame of the specified
439
+ [[status_code]] and [[explanation]], aborts a pending wait to receive frame (if
440
+ any), and marks the [[Webtube]] dead, thus blocking further transmissions. If
441
+ the [[close_socket]] parameter of the [[Webtube]] is set, it will also close
442
+ the underlying socket.
443
+
444
+ The [[status_code]] can be explicitly set to [[nil]], thus causing the
445
+ transmitted close frame to not contain a payload (that is, neither the status
446
+ code nor the explanation). The default is 1000, indicating normal closure.
447
+
448
+ If [[explanation]] is not encoded in UTF-8, it will be recoded, as required by
449
+ the WebSocket protocol specification.
450
+
451
+ Attempting to close a [[Webtube]] that has already been closed will cause an
452
+ exception as attempting to transmit via a closed [[Webtube]]; see
453
+ [[Webtube#send_message]].
454
+
455
+
456
+ === The [[Webtube::Frame]] class
457
+
458
+ Instances of this class represent individual WebSocket frames. They are
459
+ exposed to user code via the [[oncontrolframe]], [[onping]], and [[onpong]]
460
+ events and some exceptions inheriting from [[ProtocolError]].
461
+
462
+ There is currently no convenient interface for user code to build
463
+ [[Webtube::Frame]] instances by hand, but manipulating some header fields may
464
+ function as expected. (Manipulating the payload will usually not, due to this
465
+ interfering with the length- and masking-related header fields.)
466
+
467
+ The following methods are user-serviceable:
468
+
469
+ - [[Webtube::Frame#header]] returns the header as a [[String]] encoded in
470
+ [[ASCII-8BIT]].
471
+
472
+ - [[Webtube::Frame#header=]] replaces the header. There is no validation; use
473
+ with caution.
474
+
475
+ - [[Webtube::Frame#body]] returns the body as a [[String]] encoded in
476
+ [[ASCII-8BIT]].
477
+
478
+ - [[Webtube::Frame#body=]] replaces the body. There is no validation or
479
+ masking; use with extreme caution.
480
+
481
+ - [[Webtube::Frame#fin?]] extracts and returns (as [[Boolean]]) the [[FIN]]
482
+ flag of this frame.
483
+
484
+ - [[Webtube::Frame#fin=]] replaces the [[FIN]] flag of this frame.
485
+
486
+ - [[Webtube::Frame#rsv1]], [[Webtube::Frame#rsv2]], and [[Webtube::Frame#rsv3]]
487
+ extract the RSV1, RSV2, and RSV3 flags of this frame, as [[Boolean]]
488
+ instances, correspondingly.
489
+
490
+ - [[Webtube::Frame#rsv]] extracts the RSV1, RSV2, and RSV3 bitfield as an
491
+ integer in the range of [[0 .. 7]].
492
+
493
+ - [[Webtube::Frame#opcode]] extracts the opcode of this frame as an integer in
494
+ the range of [[0 .. 15]].
495
+
496
+ - [[Webtube::Frame#opcode=]] replaces the opcode.
497
+
498
+ - [[Webtube::Frame#control_frame?]] checks whether this frame is considered a
499
+ control frame, defined as having an opcode of 8 or greater.
500
+
501
+ - [[Webtube::Frame#masked?]] extracts the [[MSK]] flag of this frame.
502
+
503
+ - [[Webtube::Frame#payload_length]] extracts the payload length, in whichever
504
+ of the three ways defined by the protocol specification it is encoded, from
505
+ the frame's header.
506
+
507
+ - [[Webtube::Frame#mask]] extracts the mask of this frame, as a 4-byte
508
+ [[String]] encoded in [[ASCII-8BIT]], if the [[MSK]] flag is not set. If it
509
+ is not set, this method returns [[nil]].
510
+
511
+ - [[Webtube::Frame#payload]] retrieves the payload of this frame, demasking the
512
+ frame's body if necessary.
513
+
514
+ - [[Webtube::Frame::read_from_socket(socket)]] reads all the bytes of one
515
+ WebSocket frame from the given [[IO]] instance (which must provide data of
516
+ the plain [[ASCII-8BIT]] encoding, and emphatically not a multibyte encoding)
517
+ and returns a [[Webtube::Frame]] instance representing the frame, or raises
518
+ [[Webtube::BrokenFrame]] if the inbound traffic ends before the whole frame
519
+ will have been read. Note that this will involve calling [[IO#read]] twice
520
+ or thrice, and is therefore unsafe to be called in multithreaded code unless
521
+ external locking or synchronisation measures are used. (This method is
522
+ mainly intended for internal use by [[Webtube#run]], but it may be of use in
523
+ other contexts, such as parsing stored sequences of WebSocket frames.)
524
+
525
+
526
+ === WEBrick integration
527
+
528
+ These classes and methods are defined in the separately loadable
529
+ [['webtube/webrick']]. Note that this file will, in addition to defining new
530
+ classes and methods, replace the [[initialize]] and [[shutdown]] methods of the
531
+ [[WEBrick::HTTPServer]] class to make sure all the [[Webtube]] instances
532
+ associated with this server will be properly shut down upon the server's
533
+ shutdown.
534
+
535
+
536
+ ==== [[WEBrick::HTTPRequest#websocket_upgrade_request?]]
537
+
538
+ This method checks whether this HTTP request is a valid request to establish a
539
+ WebSocket connection.
540
+
541
+
542
+ ==== [[WEBrick::HTTPServer#webtubes]]
543
+
544
+ Retrieve the [[Webtube::Vital_Statistics]] instance for this server.
545
+
546
+ ==== [[WEBrick::HTTPServer#accept_webtube]]
547
+
548
+ accept_webtube(request, response, listener,
549
+ session: nil,
550
+ context: nil)
551
+
552
+ Given a [[request]] and a [[response]] object, as prepared by a
553
+ [[WEBrick::HTTPServer]] for processing in a portlet, this method attempts to
554
+ accept the client's request to establish a WebSocket connection. The
555
+ [[request]] must actually contain such a request; see
556
+ [[websocket_upgrade_request?]].
557
+
558
+ The attempt will fail in the theoretical case the client and the server can't
559
+ agree on the protocol version to use. In such a case, [[accept_webtube]] will
560
+ prepare a 426 'Upgrade required' response, explaining in plain text what the
561
+ problem is and advertising, using the [[Sec-WebSocket-Version]] header field,
562
+ the protocol version (specifically, 13) it is prepared to speak. When this
563
+ happens, the WebSocket session will never be set up and no [[listener]] events
564
+ will be called.
565
+
566
+ Note that [[accept_webtube]] will manipulate [[response]] and return
567
+ immediately. The actual WebSocket session will begin once WEBrick attempts to
568
+ deliver the [[response]], and this will be signalled by the newly constructed
569
+ [[Webtube]] instance delivering an [[onopen]] event to [[listener]].
570
+
571
+ Also note that the loop to process incoming WebSocket frames will hog the
572
+ whole thread; in order to deliver asynchronous messages over the
573
+ WebSocket, [[Webtube#send_message]] needs to be called from another
574
+ thread. (For synchronous messages, it can safely be called from the
575
+ handlers inside [[listener]].)
576
+
577
+ See [[Webtube#run]] for a list of the supported methods for the
578
+ [[listener]].
579
+
580
+ The [[session]] and [[context]] parameters, if given, will be stored in the
581
+ [[Webtube]] instance as attributes. The [[Webtube]] itself will not care about
582
+ them, but this mechanism may be of use for the user code. [[accept_webtube]]
583
+ stores the [[request]] in the [[Webtube]] instance's [[header]] attribute; for
584
+ this reason, it does not accept [[header]] as a parameter.
585
+
586
+ ==== [[WEBrick::HTTPServer#mount_webtube]]
587
+
588
+ mount_webtube(dir, listener)
589
+
590
+ This method mounts at the specified virtual directory a WEBrick portlet
591
+ implementing a WebSocket-only service, using the given [[listener]] as its
592
+ backend. (Note that there is only one listener for the whole service but each
593
+ event passed to the listener will have a specific [[Webtube]] instance as its
594
+ first parameter.)
595
+
596
+ The portlet itself is implemented by the class
597
+ [[WEBrick::HTTPServlet::WebtubeHandler]]. The implementation details are
598
+ deliberately left undocumented in the current version of [[Webtube]], and they
599
+ may change radically in the future. For now, the class should be considered
600
+ opaque.
601
+
602
+
603
+ == Limitations and possible future work
604
+
605
+ - The WebSocket specification permits interleaving control frames with
606
+ fragments of a data message. The current [[Webtube]] implementation engages
607
+ an internal lock serialising all calls of [[send_message]]. A future version
608
+ may ignore this lock if [[send_message]] is called to transmit a control frame
609
+ that fits into the [[PIPE_BUF]] limit and is thus not subject to the risk of
610
+ the OS kernel's [[write()]] syscall handling it partially and causing a
611
+ WebSocket frame structure breakage.
612
+
613
+ - Such ignoring may, in a future version, be configurable on a per-opcode
614
+ basis.
615
+
616
+ - The WebSocket specification provides for handling the content of fragmented
617
+ data messages even before the reception of the final frame. The current
618
+ [[Webtube]] implementation sponges up all the fragments before triggering
619
+ [[onmessage]].
620
+
621
+ - Some approaches worth considering involve delivering fragments of data
622
+ messages to the listener as they arrive or extracting parts -- such as text
623
+ lines, sequences of complete UTF-8 code points, or fixed-length data blocks
624
+ -- from the fragment sponge as soon as they can be wholly extracted.
625
+
626
+ - The WebSocket specification provides for extensions defining semantics for
627
+ the reserved bits of both data and control frames, and for extra header fields
628
+ between the WebSocket header and the ultimate payload. [[Webtube]] only
629
+ provides for handling reserved bits of incoming control frames but not data
630
+ frames, and does not provide for a convenient way to transmit frames with
631
+ reserved bits or extra header fields set.
632
+
633
+ - In particular, work is currently underway to define a WebSocket protocol
634
+ extension for transparent compression of frames and/or messages. At this
635
+ time, there are multiple competing proposals, and IETF has not released a
636
+ final specification, but a future version of [[Webtube]] may implement one
637
+ or more such extension. The most promising one, at this time, seems
638
+ [[permessage-deflate]].
639
+
640
+ - The WebSocket specification provides for explicit negotiation of a
641
+ subprotocol between the client and the server. While [[Webtube]] exposes the
642
+ relevant HTTP header field ([[Sec-WebSocket-Protocol]]) to client-side user
643
+ code, it does not provide any sort of direct support, and explicitly
644
+ supporting subprotocols on the server side may be cumbersome. A future
645
+ version of [[Webtube]] may provide a declarative way for configuring the
646
+ subprotocol negotiation: more explicitly expose the subprotocol field on the
647
+ client-side API, and providing parameters for declaring the supported
648
+ subprotocols and their order of precedence, or alternatively a hook to a more
649
+ complex subprotocol choie mechanism, on the server-side API.
650
+
651
+ - On the server side, [[Webtube]] currently only actively integrates with
652
+ WEBrick. A future version may also provide support for integration with
653
+ Puma, Sinatra, and/or EventMachine.
654
+
655
+ - A future version of [[Webtube]] may provide an interface for explicitly
656
+ specifying the fragmentation strategy for outbound data messages instead of
657
+ relying on a one-size-fits-all [[PIPE_BUF]] bases approach.
658
+
659
+ - In particular, on systems exposing the results of Path MTU Discovery of
660
+ connected TCP sockets to userspace code, a future version of [[Webtube]]
661
+ may use these results to choose a message fragment size according to the
662
+ path's MTU. (This will become nontrivial once compression and SSL get
663
+ involved.)
664
+
665
+ - Currently, [[Webtube#run]] necessarily hogs its whole thread until the
666
+ connection closes. A future version may, as an alternative, provide a more
667
+ incremental approach. Some of the partially overlapping and partially
668
+ alternative approaches worth considering include:
669
+
670
+ - a [[receive_message]] method that would hang until a message arrives (buth
671
+ how would it interact with control frames? Particularly, control frames
672
+ not defined by the standard?);
673
+
674
+ - an option for [[run]] to leave the loop after processing one incoming frame
675
+ or message;
676
+
677
+ - an option for [[run]] to leave the loop after passage of a given timeout;
678
+
679
+ - a non-blocking [[has_data?]] method that would check whether the underlying
680
+ [[Socket]] has at last one byte of data available;
681
+
682
+ - a non-blocking [[has_frame?]] method that would check whether the
683
+ underlying [[Socket]] has at least on complete WebSocket frame available
684
+ (if not, this would require storing the partial frame in a slot of
685
+ [[Webtube]] instead of a local variable of [[run]]).
686
+
687
+ - The HTTP specification provides a mechanism for redirecting clients. It is
688
+ not entirely clear how this should affect WebSocket clients, although there
689
+ are some obvious approaches. A future version of [[Webtube::connect]] may
690
+ implement one or more of them.
691
+
692
+ - A future version of the client API for [[Webtube]] may support transparent
693
+ automatic reconnection upon loss of connection while retaining the same
694
+ instance, subject to restrictions for thrashing and persisting failure. This
695
+ may need lead to defining new events for the listener.
696
+
697
+ - A future version of [[Webtube]] may provide for a higher-level interface, for
698
+ example, by transparently JSON-encoding and -decoding objects as they are
699
+ transmitted and received.
700
+
701
+ - A future version of [[Webtube]] may implement a 'queue of unhandled messages'
702
+ inside the [[Webtube]] instance (or more likely, inside an instance of its
703
+ subclass), define a mechanism (or several) for matching outbound and inbound
704
+ messages, and provide for a synchronous method that would transmit a message
705
+ and wait until receipt of a matching response, storing messages arriving in
706
+ the intervening time for use by a future call of this method, or by
707
+ concurrent calls of this method from other threads.
708
+
709
+ - A future version of [[Webtube]] may define a hook for the caller to manually
710
+ check the SSL certificate so as to facilitate secure SSL/TLS connections
711
+ using self-signed certificates validated using a custom procedure instead of
712
+ relying to a 'trusted third party' CA.
713
+
714
+ - Also, or alternatively, it may expose [[OpenSSL]]'s certificate
715
+ verification hooks.
716
+
717
+ - A future version of [[Webtube::connect]] may explicitly support using a
718
+ client SSL certificate.
719
+
720
+ - A future version of [[Webtube::connect]] may expose the proxy configuration
721
+ subsystem of [[Net::HTTP]].
722
+
723
+ - The current implementation of [[Webtube#send_message]] uses a string (and
724
+ thus, implicitly, [[RuntimeError]]), rather than an explicit subclass of
725
+ [[Exception]], to report attempt to transmit data through a dead WebSocket.
726
+ A future version of [[Webtube]] is likely to provide such an explicit
727
+ subclass of defined ancestry. It is not currently clear whether this should
728
+ inherit from [[Webtube::ProtocolError]]; arguments both ways are conceivable.
729
+
730
+ - A future version of [[Webtube]] may offer a better differentiation between
731
+ reasons of a WebSocket's closure.
732
+
733
+ - A future version of [[Webtube]] may perhaps define listener event(s) for
734
+ outbound messages as well as inbound ones.
735
+
736
+ - A future version of [[Webtube]] may define parameters for setting the
737
+ [[IP_TOS]] and [[IP_TTL]] socket options.
738
+
739
+ - The WebSocket protocol specification has a strict rule against multiple
740
+ WebSocket connections between the same pair of endpoints being simultaneously
741
+ in the /connecting/ state. [[Webtube]] is currently not implementing or
742
+ enforcing this. A future version may provide a serialisation mechanism.
743
+
744
+ - A future version of [[Webtube]] may provide an explicit mechanism for
745
+ transmitting hand-crafted [[Frame]] instances.
746
+
747
+ - This will probably need a better abstraction for frame masking.
748
+
749
+ - A future version of [[Webtube]] may provide a standalone WebSocket server
750
+ implementing only the minimal needed amount of HTTP/1.1.
751
+
752
+ - This will need a mechanism for [[Webtube]] to validate the [[Origin]]
753
+ header field in the request. This is currently not implemented (but may be
754
+ implemented in a future version.)
755
+
756
+
757
+ == Copyright and licensing
758
+
759
+ Webtube is copyright (c) 2014 by Andres Soolo and Knitten Development OÜ.
760
+ Webtube is published as free software under the terms and conditions of the
761
+ GNU General Public License version 3.
762
+
763
+ It is the default policy of us at Knitten Development to release our free
764
+ software under the GPL v3, which we believe provides a reasonable and
765
+ well-considered, if somewhat conservative, balance between the interests and
766
+ concerns of producers, consumers, and prosumers of free software. However, we
767
+ realise that some users of our free software may be better served by other
768
+ balances. For this reason, Knitten Development would like it be known that:
769
+
770
+ - we are willing to consider, on a case-by-case basis, offering packages of our
771
+ free software optionally also under certain other open source software
772
+ licenses, as certified by the Open Source Initiative(tm), provided that this
773
+ furthers the creation or advancement of specific free software projects that
774
+ Knitten Development may find worthwile, at our discretion; and
775
+
776
+ - we are available to negotiate standard non-exclusive commercial licenses for
777
+ free software that we have released, in exchange for a reasonable fee.
778
+
779
+ For any enquiries, please write to <licensing@knitten-dev.co.uk>.