webtube 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>.