websocket-driver 0.6.5 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 45f6962eb4b7b927eddff1e14c1368464227ef7f
4
- data.tar.gz: 8e98a4638c104fce2ccc752f87eadd1269f8a643
2
+ SHA256:
3
+ metadata.gz: 91c58586ddd290ad5d320267e36706bdbcffb09c7ba5300d6c3fcd50f342ec2f
4
+ data.tar.gz: 7f5afd33de5b0c0e68d7d4cbab726e7ada8200f1b5916e01feb6a3508e3f3e51
5
5
  SHA512:
6
- metadata.gz: fcc93ef62b0bd24fb9f1687874591ff7747c0a02896d395195ff699381b9c3d4d1888c4fbe079e017442f8e79a8c908c514fdf4550f872303fef6351c7484bb2
7
- data.tar.gz: 1f9215cc0926f4d7d6d083939dc2a8c83a6ca89e1eb30af2ae064ec16239936a12f69cd4a2dda12651358a2402a90fd59533b00b8d18e413d580269a418a2bc3
6
+ metadata.gz: 92ececf04fb09b31c8fd1617a38351d7a04c4306f3a27b7061e1220710a5a703da162ca0b47ee2fbdf63e20606040a022de61c51c651add2356665526adffe46
7
+ data.tar.gz: f89d71520719be104eb649c318ca85bdd107ec875a62405061fe0268d9df08b0487fe333c531d4635950245ef2879c9fc43c8a148a63afc97f508f28cc4c7397
data/CHANGELOG.md CHANGED
@@ -1,123 +1,152 @@
1
+ ### 0.7.4 / 2021-05-24
2
+
3
+ - Optimise conversions between strings and byte arrays and related encoding
4
+ operations, to reduce amount of allocation and copying
5
+
6
+ ### 0.7.3 / 2020-07-09
7
+
8
+ - Let the client accept HTTP responses that have an empty reason phrase
9
+ following the `101` status code
10
+
11
+ ### 0.7.2 / 2020-05-22
12
+
13
+ - Emit `ping` and `pong` events from the `Server` driver
14
+ - Handle draft-76 handshakes correctly if the request's body is a frozen string
15
+
16
+ ### 0.7.1 / 2019-06-10
17
+
18
+ - Catch any exceptions produced while generating a handshake response and send a
19
+ `400 Bad Request` response to the client
20
+ - Pick the RFC-6455 protocol version if the request contains any of the headers
21
+ used by that version
22
+ - Handle errors encountered while handling malformed draft-76 requests
23
+ - Change license from MIT to Apache 2.0
24
+
25
+ ### 0.7.0 / 2017-09-11
26
+
27
+ - Add `ping` and `pong` to the set of events users can listen to
28
+
1
29
  ### 0.6.5 / 2017-01-22
2
30
 
3
- * Provide a pure-Ruby fallback for the native unmasking code
31
+ - Provide a pure-Ruby fallback for the native unmasking code
4
32
 
5
33
  ### 0.6.4 / 2016-05-20
6
34
 
7
- * Amend warnings issued when running with -W2
8
- * Make sure message strings passed in by the app are transcoded to UTF-8
9
- * Copy strings if necessary for frozen-string compatibility
35
+ - Amend warnings issued when running with -W2
36
+ - Make sure message strings passed in by the app are transcoded to UTF-8
37
+ - Copy strings if necessary for frozen-string compatibility
10
38
 
11
39
  ### 0.6.3 / 2015-11-06
12
40
 
13
- * Reject draft-76 handshakes if their Sec-WebSocket-Key headers are invalid
14
- * Throw a more helpful error if a client is created with an invalid URL
41
+ - Reject draft-76 handshakes if their Sec-WebSocket-Key headers are invalid
42
+ - Throw a more helpful error if a client is created with an invalid URL
15
43
 
16
44
  ### 0.6.2 / 2015-07-18
17
45
 
18
- * When the peer sends a close frame with no error code, emit 1000
46
+ - When the peer sends a close frame with no error code, emit 1000
19
47
 
20
48
  ### 0.6.1 / 2015-07-13
21
49
 
22
- * Fix how events are stored in `EventEmitter` to fix a backward-compatibility
50
+ - Fix how events are stored in `EventEmitter` to fix a backward-compatibility
23
51
  violation introduced in the last release
24
- * Use the `Array#pack` and `String#unpack` methods for reading/writing numbers
52
+ - Use the `Array#pack` and `String#unpack` methods for reading/writing numbers
25
53
  to buffers rather than including duplicate logic for this
26
54
 
27
55
  ### 0.6.0 / 2015-07-08
28
56
 
29
- * Use `SecureRandom` to generate the `Sec-WebSocket-Key` header
30
- * Allow the parser to recover cleanly if event listeners raise an error
31
- * Let the `on()` method take a lambda as a positional argument rather than a block
32
- * Add a `pong` method for sending unsolicited pong frames
57
+ - Use `SecureRandom` to generate the `Sec-WebSocket-Key` header
58
+ - Allow the parser to recover cleanly if event listeners raise an error
59
+ - Let the `on()` method take a lambda as a positional argument rather than a
60
+ block
61
+ - Add a `pong` method for sending unsolicited pong frames
33
62
 
34
63
  ### 0.5.4 / 2015-03-29
35
64
 
36
- * Don't emit extra close frames if we receive a close frame after we already
65
+ - Don't emit extra close frames if we receive a close frame after we already
37
66
  sent one
38
- * Fail the connection when the driver receives an invalid
67
+ - Fail the connection when the driver receives an invalid
39
68
  `Sec-WebSocket-Extensions` header
40
69
 
41
70
  ### 0.5.3 / 2015-02-22
42
71
 
43
- * Don't treat incoming data as WebSocket frames if a client driver is closed
72
+ - Don't treat incoming data as WebSocket frames if a client driver is closed
44
73
  before receiving the server handshake
45
74
 
46
75
  ### 0.5.2 / 2015-02-19
47
76
 
48
- * Don't emit multiple `error` events
77
+ - Don't emit multiple `error` events
49
78
 
50
79
  ### 0.5.1 / 2014-12-18
51
80
 
52
- * Don't allow drivers to be created with unrecognized options
81
+ - Don't allow drivers to be created with unrecognized options
53
82
 
54
83
  ### 0.5.0 / 2014-12-13
55
84
 
56
- * Support protocol extensions via the websocket-extensions module
85
+ - Support protocol extensions via the websocket-extensions module
57
86
 
58
87
  ### 0.4.0 / 2014-11-08
59
88
 
60
- * Support connection via HTTP proxies using `CONNECT`
89
+ - Support connection via HTTP proxies using `CONNECT`
61
90
 
62
91
  ### 0.3.5 / 2014-10-04
63
92
 
64
- * Fix bug where the `Server` driver doesn't pass `ping` callbacks to its
93
+ - Fix bug where the `Server` driver doesn't pass `ping` callbacks to its
65
94
  delegate
66
- * Fix an arity error when calling `fail_request`
67
- * Allow `close` to be called before `start` to close the driver
95
+ - Fix an arity error when calling `fail_request`
96
+ - Allow `close` to be called before `start` to close the driver
68
97
 
69
98
  ### 0.3.4 / 2014-07-06
70
99
 
71
- * Don't hold references to frame buffers after a message has been emitted
72
- * Make sure that `protocol` and `version` are exposed properly by the TCP driver
73
- * Correct HTTP header parsing based on RFC 7230; header names cannot contain
100
+ - Don't hold references to frame buffers after a message has been emitted
101
+ - Make sure that `protocol` and `version` are exposed properly by the TCP driver
102
+ - Correct HTTP header parsing based on RFC 7230; header names cannot contain
74
103
  backslashes
75
104
 
76
105
  ### 0.3.3 / 2014-04-24
77
106
 
78
- * Fix problems with loading C and Java native extension code
79
- * Correct the acceptable characters used in the HTTP parser
80
- * Correct the draft-76 status line reason phrase
107
+ - Fix problems with loading C and Java native extension code
108
+ - Correct the acceptable characters used in the HTTP parser
109
+ - Correct the draft-76 status line reason phrase
81
110
 
82
111
  ### 0.3.2 / 2013-12-29
83
112
 
84
- * Expand `max_length` to cover sequences of continuation frames and
113
+ - Expand `max_length` to cover sequences of continuation frames and
85
114
  `draft-{75,76}`
86
- * Decrease default maximum frame buffer size to 64MB
87
- * Stop parsing when the protocol enters a failure mode, to save CPU cycles
115
+ - Decrease default maximum frame buffer size to 64MB
116
+ - Stop parsing when the protocol enters a failure mode, to save CPU cycles
88
117
 
89
118
  ### 0.3.1 / 2013-12-03
90
119
 
91
- * Add a `max_length` option to limit allowed frame size
120
+ - Add a `max_length` option to limit allowed frame size
92
121
 
93
122
  ### 0.3.0 / 2013-09-09
94
123
 
95
- * Support client URLs with Basic Auth credentials
124
+ - Support client URLs with Basic Auth credentials
96
125
 
97
126
  ### 0.2.3 / 2013-08-04
98
127
 
99
- * Fix bug in EventEmitter#emit when listeners are removed
128
+ - Fix bug in EventEmitter#emit when listeners are removed
100
129
 
101
130
  ### 0.2.2 / 2013-08-04
102
131
 
103
- * Fix bug in EventEmitter#listener_count for unregistered events
132
+ - Fix bug in EventEmitter#listener_count for unregistered events
104
133
 
105
134
  ### 0.2.1 / 2013-07-05
106
135
 
107
- * Queue sent messages if the client has not begun trying to connect
108
- * Encode all strings sent to I/O as `ASCII-8BIT`
136
+ - Queue sent messages if the client has not begun trying to connect
137
+ - Encode all strings sent to I/O as `ASCII-8BIT`
109
138
 
110
139
  ### 0.2.0 / 2013-05-12
111
140
 
112
- * Add API for setting and reading headers
113
- * Add Driver.server() method for getting a driver for TCP servers
141
+ - Add API for setting and reading headers
142
+ - Add Driver.server() method for getting a driver for TCP servers
114
143
 
115
144
  ### 0.1.0 / 2013-05-04
116
145
 
117
- * First stable release
146
+ - First stable release
118
147
 
119
148
  ### 0.0.0 / 2013-04-22
120
149
 
121
- * First release
122
- * Proof of concept for people to try out
123
- * Might be unstable
150
+ - First release
151
+ - Proof of concept for people to try out
152
+ - Might be unstable
data/LICENSE.md CHANGED
@@ -1,22 +1,12 @@
1
- # License
1
+ Copyright 2010-2021 James Coglan
2
2
 
3
- (The MIT License)
3
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use
4
+ this file except in compliance with the License. You may obtain a copy of the
5
+ License at
4
6
 
5
- Copyright (c) 2010-2017 James Coglan
7
+ http://www.apache.org/licenses/LICENSE-2.0
6
8
 
7
- Permission is hereby granted, free of charge, to any person obtaining a copy of
8
- this software and associated documentation files (the 'Software'), to deal in
9
- the Software without restriction, including without limitation the rights to
10
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11
- the Software, and to permit persons to whom the Software is furnished to do so,
12
- subject to the following conditions:
13
-
14
- The above copyright notice and this permission notice shall be included in all
15
- copies or substantial portions of the Software.
16
-
17
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9
+ Unless required by applicable law or agreed to in writing, software distributed
10
+ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
11
+ CONDITIONS OF ANY KIND, either express or implied. See the License for the
12
+ specific language governing permissions and limitations under the License.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # websocket-driver [![Build Status](https://travis-ci.org/faye/websocket-driver-ruby.svg)](https://travis-ci.org/faye/websocket-driver-ruby)
1
+ # websocket-driver
2
2
 
3
3
  This module provides a complete implementation of the WebSocket protocols that
4
4
  can be hooked up to any TCP library. It aims to simplify things by decoupling
@@ -10,21 +10,21 @@ pluggable I/O.
10
10
  Due to this design, you get a lot of things for free. In particular, if you hook
11
11
  this module up to some I/O object, it will do all of this for you:
12
12
 
13
- * Select the correct server-side driver to talk to the client
14
- * Generate and send both server- and client-side handshakes
15
- * Recognize when the handshake phase completes and the WS protocol begins
16
- * Negotiate subprotocol selection based on `Sec-WebSocket-Protocol`
17
- * Negotiate and use extensions via the
13
+ - Select the correct server-side driver to talk to the client
14
+ - Generate and send both server- and client-side handshakes
15
+ - Recognize when the handshake phase completes and the WS protocol begins
16
+ - Negotiate subprotocol selection based on `Sec-WebSocket-Protocol`
17
+ - Negotiate and use extensions via the
18
18
  [websocket-extensions](https://github.com/faye/websocket-extensions-ruby)
19
19
  module
20
- * Buffer sent messages until the handshake process is finished
21
- * Deal with proxies that defer delivery of the draft-76 handshake body
22
- * Notify you when the socket is open and closed and when messages arrive
23
- * Recombine fragmented messages
24
- * Dispatch text, binary, ping, pong and close frames
25
- * Manage the socket-closing handshake process
26
- * Automatically reply to ping frames with a matching pong
27
- * Apply masking to messages sent by the client
20
+ - Buffer sent messages until the handshake process is finished
21
+ - Deal with proxies that defer delivery of the draft-76 handshake body
22
+ - Notify you when the socket is open and closed and when messages arrive
23
+ - Recombine fragmented messages
24
+ - Dispatch text, binary, ping, pong and close frames
25
+ - Manage the socket-closing handshake process
26
+ - Automatically reply to ping frames with a matching pong
27
+ - Apply masking to messages sent by the client
28
28
 
29
29
  This library was originally extracted from the [Faye](http://faye.jcoglan.com)
30
30
  project but now aims to provide simple WebSocket support for any Ruby server or
@@ -43,12 +43,12 @@ $ gem install websocket-driver
43
43
  To build either a server-side or client-side socket, the only requirement is
44
44
  that you supply a `socket` object with these methods:
45
45
 
46
- * `socket.url` - returns the full URL of the socket as a string.
47
- * `socket.write(string)` - writes the given string to a TCP stream.
46
+ - `socket.url` - returns the full URL of the socket as a string.
47
+ - `socket.write(string)` - writes the given string to a TCP stream.
48
48
 
49
49
  Server-side sockets require one additional method:
50
50
 
51
- * `socket.env` - returns a Rack-style env hash that will contain some of the
51
+ - `socket.env` - returns a Rack-style env hash that will contain some of the
52
52
  following fields. Their values are strings containing the value of the named
53
53
  header, unless stated otherwise.
54
54
  * `HTTP_CONNECTION`
@@ -150,7 +150,16 @@ module Connection
150
150
  if WebSocket::Driver.websocket?(@driver.env)
151
151
  @driver.start
152
152
  else
153
- # handle other HTTP requests
153
+ # handle other HTTP requests, for example
154
+ body = '<h1>hello</h1>'
155
+ response = [
156
+ 'HTTP/1.1 200 OK',
157
+ 'Content-Type: text/plain',
158
+ "Content-Length: #{body.bytesize}",
159
+ '',
160
+ body
161
+ ]
162
+ send_data response.join("\r\n")
154
163
  end
155
164
  end
156
165
 
@@ -193,8 +202,8 @@ and send outgoing data.
193
202
  Client drivers have two additional methods for reading the HTTP data that was
194
203
  sent back by the server:
195
204
 
196
- * `driver.status` - the integer value of the HTTP status code
197
- * `driver.headers` - a hash-like object containing the response headers
205
+ - `driver.status` - the integer value of the HTTP status code
206
+ - `driver.headers` - a hash-like object containing the response headers
198
207
 
199
208
 
200
209
  ### HTTP Proxies
@@ -261,9 +270,9 @@ frames.
261
270
  The `options` argument is optional, and is a hash. It may contain the following
262
271
  keys:
263
272
 
264
- * `:max_length` - the maximum allowed size of incoming message frames, in bytes.
273
+ - `:max_length` - the maximum allowed size of incoming message frames, in bytes.
265
274
  The default value is `2^26 - 1`, or 1 byte short of 64 MiB.
266
- * `:protocols` - an array of strings representing acceptable subprotocols for
275
+ - `:protocols` - an array of strings representing acceptable subprotocols for
267
276
  use over the socket. The driver will negotiate one of these to use via the
268
277
  `Sec-WebSocket-Protocol` header if supported by the other peer.
269
278
 
@@ -274,27 +283,38 @@ Note that most of these methods are commands: if they produce data that should
274
283
  be sent over the socket, they will give this to you by calling
275
284
  `socket.write(string)`.
276
285
 
277
- #### `driver.on :open, -> (event) { }`
286
+ #### `driver.on :open, -> (event) {}`
278
287
 
279
288
  Adds a callback block to execute when the socket becomes open.
280
289
 
281
- #### `driver.on :message, -> (event) { }`
290
+ #### `driver.on :message, -> (event) {}`
282
291
 
283
292
  Adds a callback block to execute when a message is received. `event` will have a
284
293
  `data` attribute containing either a string in the case of a text message or an
285
294
  array of integers in the case of a binary message.
286
295
 
287
- #### `driver.on :error, -> (event) { }`
296
+ #### `driver.on :error, -> (event) {}`
288
297
 
289
298
  Adds a callback to execute when a protocol error occurs due to the other peer
290
299
  sending an invalid byte sequence. `event` will have a `message` attribute
291
300
  describing the error.
292
301
 
293
- #### `driver.on :close, -> (event) { }`
302
+ #### `driver.on :close, -> (event) {}`
294
303
 
295
304
  Adds a callback block to execute when the socket becomes closed. The `event`
296
305
  object has `code` and `reason` attributes.
297
306
 
307
+ #### `driver.on :ping, -> (event) {}`
308
+
309
+ Adds a callback block to execute when a ping is received. You do not need to
310
+ handle this by sending a pong frame yourself; the driver handles this for you.
311
+
312
+ #### `driver.on :pong, -> (event) {}`
313
+
314
+ Adds a callback block to execute when a pong is received. If this was in
315
+ response to a ping you sent, you can also handle this event via the
316
+ `driver.ping(message) { ... }` callback.
317
+
298
318
  #### `driver.add_extension(extension)`
299
319
 
300
320
  Registers a protocol extension whose operation will be negotiated via the
@@ -1,8 +1,6 @@
1
1
  package com.jcoglan.websocket;
2
2
 
3
- import java.lang.Long;
4
3
  import java.io.IOException;
5
-
6
4
  import org.jruby.Ruby;
7
5
  import org.jruby.RubyClass;
8
6
  import org.jruby.RubyModule;
@@ -15,41 +13,45 @@ import org.jruby.runtime.builtin.IRubyObject;
15
13
  import org.jruby.runtime.load.BasicLibraryService;
16
14
 
17
15
  public class WebsocketMaskService implements BasicLibraryService {
18
- private Ruby runtime;
16
+ private Ruby runtime;
19
17
 
20
- public boolean basicLoad(Ruby runtime) throws IOException {
21
- this.runtime = runtime;
22
- RubyModule websocket = runtime.defineModule("WebSocket");
18
+ public boolean basicLoad(Ruby runtime) throws IOException {
19
+ this.runtime = runtime;
23
20
 
24
- RubyClass webSocketMask = websocket.defineClassUnder("Mask", runtime.getObject(), new ObjectAllocator() {
25
- public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
26
- return new WebsocketMask(runtime, rubyClass);
27
- }
28
- });
21
+ RubyModule websocket = runtime.defineModule("WebSocket");
22
+ RubyClass webSocketMask = websocket.defineClassUnder("Mask", runtime.getObject(), getAllocator());
29
23
 
30
- webSocketMask.defineAnnotatedMethods(WebsocketMask.class);
31
- return true;
32
- }
24
+ webSocketMask.defineAnnotatedMethods(WebsocketMask.class);
25
+ return true;
26
+ }
33
27
 
34
- public class WebsocketMask extends RubyObject {
35
- public WebsocketMask(final Ruby runtime, RubyClass rubyClass) {
36
- super(runtime, rubyClass);
28
+ ObjectAllocator getAllocator() {
29
+ return new ObjectAllocator() {
30
+ public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
31
+ return new WebsocketMask(runtime, rubyClass);
32
+ }
33
+ };
37
34
  }
38
35
 
39
- @JRubyMethod
40
- public IRubyObject mask(ThreadContext context, IRubyObject payload, IRubyObject mask) {
41
- if (mask.isNil()) return payload;
36
+ public class WebsocketMask extends RubyObject {
37
+ public WebsocketMask(final Ruby runtime, RubyClass rubyClass) {
38
+ super(runtime, rubyClass);
39
+ }
40
+
41
+ @JRubyMethod
42
+ public IRubyObject mask(ThreadContext context, IRubyObject payload, IRubyObject mask) {
43
+ if (mask.isNil()) return payload;
42
44
 
43
- byte[] payload_a = ((RubyString)payload).getBytes();
44
- byte[] mask_a = ((RubyString)mask).getBytes();
45
- int i, n = payload_a.length;
45
+ byte[] payload_a = ((RubyString)payload).getBytes();
46
+ byte[] mask_a = ((RubyString)mask).getBytes();
47
+ int i, n = payload_a.length;
46
48
 
47
- if (n == 0) return payload;
49
+ if (n == 0) return payload;
48
50
 
49
- for (i = 0; i < n; i++) {
50
- payload_a[i] ^= mask_a[i % 4];
51
- }
52
- return RubyString.newStringNoCopy(runtime, payload_a);
51
+ for (i = 0; i < n; i++) {
52
+ payload_a[i] ^= mask_a[i % 4];
53
+ }
54
+ return RubyString.newStringNoCopy(runtime, payload_a);
55
+ }
53
56
  }
54
- }
55
57
  }
@@ -1,41 +1,32 @@
1
1
  #include <ruby.h>
2
2
 
3
- VALUE WebSocket = Qnil;
4
- VALUE WebSocketMask = Qnil;
5
-
6
- void Init_websocket_mask();
7
- VALUE method_websocket_mask(VALUE self, VALUE payload, VALUE mask);
8
-
9
- void
10
- Init_websocket_mask()
3
+ VALUE method_websocket_mask(VALUE self, VALUE payload, VALUE mask)
11
4
  {
12
- WebSocket = rb_define_module("WebSocket");
13
- WebSocketMask = rb_define_module_under(WebSocket, "Mask");
14
- rb_define_singleton_method(WebSocketMask, "mask", method_websocket_mask, 2);
15
- }
5
+ char *payload_s, *mask_s, *unmasked_s;
6
+ long i, n;
7
+ VALUE unmasked;
16
8
 
17
- VALUE
18
- method_websocket_mask(VALUE self,
19
- VALUE payload,
20
- VALUE mask)
21
- {
22
- char *payload_s, *mask_s, *unmasked_s;
23
- long i, n;
24
- VALUE unmasked;
9
+ if (mask == Qnil || RSTRING_LEN(mask) != 4) {
10
+ return payload;
11
+ }
25
12
 
26
- if (mask == Qnil || RSTRING_LEN(mask) != 4) {
27
- return payload;
28
- }
13
+ payload_s = RSTRING_PTR(payload);
14
+ mask_s = RSTRING_PTR(mask);
15
+ n = RSTRING_LEN(payload);
29
16
 
30
- payload_s = RSTRING_PTR(payload);
31
- mask_s = RSTRING_PTR(mask);
32
- n = RSTRING_LEN(payload);
17
+ unmasked = rb_str_new(0, n);
18
+ unmasked_s = RSTRING_PTR(unmasked);
33
19
 
34
- unmasked = rb_str_new(0, n);
35
- unmasked_s = RSTRING_PTR(unmasked);
20
+ for (i = 0; i < n; i++) {
21
+ unmasked_s[i] = payload_s[i] ^ mask_s[i % 4];
22
+ }
23
+ return unmasked;
24
+ }
25
+
26
+ void Init_websocket_mask()
27
+ {
28
+ VALUE WebSocket = rb_define_module("WebSocket");
29
+ VALUE Mask = rb_define_module_under(WebSocket, "Mask");
36
30
 
37
- for (i = 0; i < n; i++) {
38
- unmasked_s[i] = payload_s[i] ^ mask_s[i % 4];
39
- }
40
- return unmasked;
31
+ rb_define_singleton_method(Mask, "mask", method_websocket_mask, 2);
41
32
  }
@@ -44,12 +44,11 @@ module WebSocket
44
44
  MAX_LENGTH = 0x3ffffff
45
45
  STATES = [:connecting, :open, :closing, :closed]
46
46
 
47
- BINARY = 'ASCII-8BIT'
48
- UNICODE = 'UTF-8'
49
-
50
47
  ConnectEvent = Struct.new(nil)
51
48
  OpenEvent = Struct.new(nil)
52
49
  MessageEvent = Struct.new(:data)
50
+ PingEvent = Struct.new(:data)
51
+ PongEvent = Struct.new(:data)
53
52
  CloseEvent = Struct.new(:code, :reason)
54
53
 
55
54
  ProtocolError = Class.new(StandardError)
@@ -99,15 +98,24 @@ module WebSocket
99
98
 
100
99
  def start
101
100
  return false unless @ready_state == 0
102
- response = handshake_response
103
- return false unless response
101
+
102
+ unless Driver.websocket?(@socket.env)
103
+ return fail_handshake(ProtocolError.new('Not a WebSocket request'))
104
+ end
105
+
106
+ begin
107
+ response = handshake_response
108
+ rescue => error
109
+ return fail_handshake(error)
110
+ end
111
+
104
112
  @socket.write(response)
105
113
  open unless @stage == -1
106
114
  true
107
115
  end
108
116
 
109
117
  def text(message)
110
- message = message.encode(UNICODE) unless message.encoding.name == UNICODE
118
+ message = message.encode(Encoding::UTF_8) unless message.encoding == Encoding::UTF_8
111
119
  frame(message, :text)
112
120
  end
113
121
 
@@ -132,6 +140,24 @@ module WebSocket
132
140
 
133
141
  private
134
142
 
143
+ def fail_handshake(error)
144
+ headers = Headers.new
145
+ headers['Content-Type'] = 'text/plain'
146
+ headers['Content-Length'] = error.message.bytesize
147
+
148
+ headers = ['HTTP/1.1 400 Bad Request', headers.to_s, error.message]
149
+ @socket.write(headers.join("\r\n"))
150
+ fail(:protocol_error, error.message)
151
+
152
+ false
153
+ end
154
+
155
+ def fail(type, message)
156
+ @ready_state = 2
157
+ emit(:error, ProtocolError.new(message))
158
+ close
159
+ end
160
+
135
161
  def open
136
162
  @ready_state = 1
137
163
  @queue.each { |message| frame(*message) }
@@ -153,10 +179,15 @@ module WebSocket
153
179
  end
154
180
 
155
181
  def self.rack(socket, options = {})
156
- env = socket.env
157
- if env['HTTP_SEC_WEBSOCKET_VERSION']
182
+ env = socket.env
183
+ version = env['HTTP_SEC_WEBSOCKET_VERSION']
184
+ key = env['HTTP_SEC_WEBSOCKET_KEY']
185
+ key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
186
+ key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
187
+
188
+ if version or key
158
189
  Hybi.new(socket, options.merge(:require_masking => true))
159
- elsif env['HTTP_SEC_WEBSOCKET_KEY1']
190
+ elsif key1 or key2
160
191
  Draft76.new(socket, options)
161
192
  else
162
193
  Draft75.new(socket, options)
@@ -167,21 +198,21 @@ module WebSocket
167
198
  case string
168
199
  when Array then
169
200
  string = string.pack('C*')
170
- encoding ||= BINARY
201
+ encoding ||= Encoding::BINARY
171
202
  when String then
172
- encoding ||= UNICODE
203
+ encoding ||= Encoding::UTF_8
173
204
  end
174
- unless string.encoding.name == encoding
205
+ unless string.encoding == encoding
175
206
  string = string.dup if string.frozen?
176
207
  string.force_encoding(encoding)
177
208
  end
178
- string.valid_encoding? ? string : nil
209
+ string
179
210
  end
180
211
 
181
212
  def self.validate_options(options, valid_keys)
182
213
  options.keys.each do |key|
183
214
  unless valid_keys.include?(key)
184
- raise ConfigurationError, "Unrecognized option: #{key.inspect}"
215
+ raise ConfigurationError, "Unrecognized option: #{ key.inspect }"
185
216
  end
186
217
  end
187
218
  end
@@ -20,10 +20,10 @@ module WebSocket
20
20
 
21
21
  uri = URI.parse(@socket.url)
22
22
  unless VALID_SCHEMES.include?(uri.scheme)
23
- raise URIError, "#{socket.url} is not a valid WebSocket URL"
23
+ raise URIError, "#{ socket.url } is not a valid WebSocket URL"
24
24
  end
25
25
 
26
- host = uri.host + (uri.port ? ":#{uri.port}" : '')
26
+ host = uri.host + (uri.port ? ":#{ uri.port }" : '')
27
27
  path = (uri.path == '') ? '/' : uri.path
28
28
  @pathname = path + (uri.query ? '?' + uri.query : '')
29
29
 
@@ -31,7 +31,7 @@ module WebSocket
31
31
  @headers['Upgrade'] = 'websocket'
32
32
  @headers['Connection'] = 'Upgrade'
33
33
  @headers['Sec-WebSocket-Key'] = @key
34
- @headers['Sec-WebSocket-Version'] = '13'
34
+ @headers['Sec-WebSocket-Version'] = VERSION
35
35
 
36
36
  if @protocols.size > 0
37
37
  @headers['Sec-WebSocket-Protocol'] = @protocols * ', '
@@ -44,7 +44,7 @@ module WebSocket
44
44
  end
45
45
 
46
46
  def version
47
- 'hybi-13'
47
+ "hybi-#{ VERSION }"
48
48
  end
49
49
 
50
50
  def proxy(origin, options = {})
@@ -73,19 +73,19 @@ module WebSocket
73
73
  parse(@http.body)
74
74
  end
75
75
 
76
- private
76
+ private
77
77
 
78
78
  def handshake_request
79
79
  extensions = @extensions.generate_offer
80
80
  @headers['Sec-WebSocket-Extensions'] = extensions if extensions
81
81
 
82
- start = "GET #{@pathname} HTTP/1.1"
82
+ start = "GET #{ @pathname } HTTP/1.1"
83
83
  headers = [start, @headers.to_s, '']
84
84
  headers.join("\r\n")
85
85
  end
86
86
 
87
87
  def fail_handshake(message)
88
- message = "Error during WebSocket handshake: #{message}"
88
+ message = "Error during WebSocket handshake: #{ message }"
89
89
  @ready_state = 3
90
90
  emit(:error, ProtocolError.new(message))
91
91
  emit(:close, CloseEvent.new(ERRORS[:protocol_error], message))
@@ -96,7 +96,7 @@ module WebSocket
96
96
  @headers = Headers.new(@http.headers)
97
97
 
98
98
  unless @http.code == 101
99
- return fail_handshake("Unexpected response code: #{@http.code}")
99
+ return fail_handshake("Unexpected response code: #{ @http.code }")
100
100
  end
101
101
 
102
102
  upgrade = @http['Upgrade'] || ''
@@ -56,7 +56,7 @@ module WebSocket
56
56
  when 2 then
57
57
  if octet == 0xFF
58
58
  @stage = 0
59
- emit(:message, MessageEvent.new(Driver.encode(@buffer, UNICODE)))
59
+ emit(:message, MessageEvent.new(Driver.encode(@buffer, Encoding::UTF_8)))
60
60
  else
61
61
  if @length
62
62
  @skipped += 1
@@ -6,9 +6,10 @@ module WebSocket
6
6
 
7
7
  def initialize(socket, options = {})
8
8
  super
9
- input = @socket.env['rack.input']
9
+ input = (@socket.env['rack.input'] || StringIO.new('')).read
10
+ input = input.dup if input.frozen?
10
11
  @stage = -1
11
- @body = (input ? input.read : String.new('')).force_encoding(BINARY)
12
+ @body = input.force_encoding(Encoding::BINARY)
12
13
 
13
14
  @headers.clear
14
15
  @headers['Upgrade'] = 'WebSocket'
@@ -29,7 +30,7 @@ module WebSocket
29
30
 
30
31
  def close(reason = nil, code = nil)
31
32
  return false if @ready_state == 3
32
- @socket.write([0xFF, 0x00].pack('C*'))
33
+ @socket.write([0xFF, 0x00].pack('C*')) if @ready_state == 1
33
34
  @ready_state = 3
34
35
  emit(:close, CloseEvent.new(nil, nil))
35
36
  true
@@ -39,19 +40,20 @@ module WebSocket
39
40
 
40
41
  def handshake_response
41
42
  env = @socket.env
42
-
43
43
  key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
44
+ key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
45
+
46
+ raise ProtocolError.new('Missing required header: Sec-WebSocket-Key1') unless key1
47
+ raise ProtocolError.new('Missing required header: Sec-WebSocket-Key2') unless key2
48
+
44
49
  number1 = number_from_key(key1)
45
50
  spaces1 = spaces_in_key(key1)
46
51
 
47
- key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
48
52
  number2 = number_from_key(key2)
49
53
  spaces2 = spaces_in_key(key2)
50
54
 
51
55
  if number1 % spaces1 != 0 or number2 % spaces2 != 0
52
- emit(:error, ProtocolError.new('Client sent invalid Sec-WebSocket-Key headers'))
53
- close
54
- return nil
56
+ raise ProtocolError.new('Client sent invalid Sec-WebSocket-Key headers')
55
57
  end
56
58
 
57
59
  @key_values = [number1 / spaces1, number2 / spaces2]
@@ -84,7 +86,8 @@ module WebSocket
84
86
  end
85
87
 
86
88
  def number_from_key(key)
87
- key.scan(/[0-9]/).join('').to_i(10)
89
+ number = key.scan(/[0-9]/).join('')
90
+ number == '' ? Float::NAN : number.to_i(10)
88
91
  end
89
92
 
90
93
  def spaces_in_key(key)
@@ -25,7 +25,7 @@ module WebSocket
25
25
  return if value.nil?
26
26
  key = HTTP.normalize_header(name)
27
27
  return unless @sent.add?(key) or ALLOWED_DUPLICATES.include?(key)
28
- @lines << "#{name.strip}: #{value.to_s.strip}\r\n"
28
+ @lines << "#{ name.strip }: #{ value.to_s.strip }\r\n"
29
29
  end
30
30
 
31
31
  def inspect
@@ -11,7 +11,8 @@ module WebSocket
11
11
  Base64.strict_encode64(Digest::SHA1.digest(key + GUID))
12
12
  end
13
13
 
14
- GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
14
+ VERSION = '13'
15
+ GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
15
16
 
16
17
  BYTE = 0b11111111
17
18
  FIN = MASK = 0b10000000
@@ -51,7 +52,7 @@ module WebSocket
51
52
  MIN_RESERVED_ERROR = 3000
52
53
  MAX_RESERVED_ERROR = 4999
53
54
 
54
- PACK_FORMATS = {2 => 'n', 8 => 'Q>'}
55
+ PACK_FORMATS = { 2 => 'S>', 8 => 'Q>' }
55
56
 
56
57
  def initialize(socket, options = {})
57
58
  super
@@ -68,22 +69,16 @@ module WebSocket
68
69
 
69
70
  return unless @socket.respond_to?(:env)
70
71
 
71
- sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
72
- protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
73
-
74
- @headers['Upgrade'] = 'websocket'
75
- @headers['Connection'] = 'Upgrade'
76
- @headers['Sec-WebSocket-Accept'] = Hybi.generate_accept(sec_key)
77
-
78
72
  if protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
79
73
  protos = protos.split(/ *, */) if String === protos
80
74
  @protocol = protos.find { |p| @protocols.include?(p) }
81
- @headers['Sec-WebSocket-Protocol'] = @protocol if @protocol
75
+ else
76
+ @protocol = nil
82
77
  end
83
78
  end
84
79
 
85
80
  def version
86
- "hybi-#{@socket.env['HTTP_SEC_WEBSOCKET_VERSION']}"
81
+ "hybi-#{ VERSION }"
87
82
  end
88
83
 
89
84
  def add_extension(extension)
@@ -165,14 +160,13 @@ module WebSocket
165
160
 
166
161
  message = Message.new
167
162
  frame = Frame.new
168
- is_text = String === buffer
169
163
 
170
164
  message.rsv1 = message.rsv2 = message.rsv3 = false
171
- message.opcode = OPCODES[type || (is_text ? :text : :binary)]
165
+ message.opcode = OPCODES[type || (String === buffer ? :text : :binary)]
172
166
 
173
- payload = is_text ? buffer.bytes.to_a : buffer
174
- payload = [code].pack(PACK_FORMATS[2]).bytes.to_a + payload if code
175
- message.data = payload.pack('C*')
167
+ payload = Driver.encode(buffer, Encoding::BINARY)
168
+ payload = [code, payload].pack('S>a*') if code
169
+ message.data = payload
176
170
 
177
171
  if MESSAGE_OPCODES.include?(message.opcode)
178
172
  message = @extensions.process_outgoing_message(message)
@@ -199,43 +193,59 @@ module WebSocket
199
193
 
200
194
  def send_frame(frame)
201
195
  length = frame.length
202
- buffer = []
196
+ values = []
197
+ format = 'C2'
203
198
  masked = frame.masked ? MASK : 0
204
199
 
205
- buffer[0] = (frame.final ? FIN : 0) |
200
+ values[0] = (frame.final ? FIN : 0) |
206
201
  (frame.rsv1 ? RSV1 : 0) |
207
202
  (frame.rsv2 ? RSV2 : 0) |
208
203
  (frame.rsv3 ? RSV3 : 0) |
209
204
  frame.opcode
210
205
 
211
206
  if length <= 125
212
- buffer[1] = masked | length
207
+ values[1] = masked | length
213
208
  elsif length <= 65535
214
- buffer[1] = masked | 126
215
- buffer[2..3] = [length].pack(PACK_FORMATS[2]).bytes.to_a
209
+ values[1] = masked | 126
210
+ values[2] = length
211
+ format << 'S>'
216
212
  else
217
- buffer[1] = masked | 127
218
- buffer[2..9] = [length].pack(PACK_FORMATS[8]).bytes.to_a
213
+ values[1] = masked | 127
214
+ values[2] = length
215
+ format << 'Q>'
219
216
  end
220
217
 
221
218
  if frame.masked
222
- buffer.concat(frame.masking_key.bytes.to_a)
223
- buffer.concat(Mask.mask(frame.payload, frame.masking_key).bytes.to_a)
219
+ values << frame.masking_key
220
+ values << Mask.mask(frame.payload, frame.masking_key)
221
+ format << 'a4a*'
224
222
  else
225
- buffer.concat(frame.payload.bytes.to_a)
223
+ values << frame.payload
224
+ format << 'a*'
226
225
  end
227
226
 
228
- @socket.write(buffer.pack('C*'))
227
+ @socket.write(values.pack(format))
229
228
  end
230
229
 
231
230
  def handshake_response
232
- begin
233
- extensions = @extensions.generate_response(@socket.env['HTTP_SEC_WEBSOCKET_EXTENSIONS'])
234
- rescue => error
235
- fail(:protocol_error, error.message)
236
- return nil
231
+ sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
232
+ version = @socket.env['HTTP_SEC_WEBSOCKET_VERSION']
233
+
234
+ unless version == VERSION
235
+ raise ProtocolError.new("Unsupported WebSocket version: #{ VERSION }")
236
+ end
237
+
238
+ unless sec_key
239
+ raise ProtocolError.new('Missing handshake request header: Sec-WebSocket-Key')
237
240
  end
238
241
 
242
+ @headers['Upgrade'] = 'websocket'
243
+ @headers['Connection'] = 'Upgrade'
244
+ @headers['Sec-WebSocket-Accept'] = Hybi.generate_accept(sec_key)
245
+
246
+ @headers['Sec-WebSocket-Protocol'] = @protocol if @protocol
247
+
248
+ extensions = @extensions.generate_response(@socket.env['HTTP_SEC_WEBSOCKET_EXTENSIONS'])
239
249
  @headers['Sec-WebSocket-Extensions'] = extensions if extensions
240
250
 
241
251
  start = 'HTTP/1.1 101 Switching Protocols'
@@ -275,17 +285,17 @@ module WebSocket
275
285
 
276
286
  unless @extensions.valid_frame_rsv?(@frame)
277
287
  return fail(:protocol_error,
278
- "One or more reserved bits are on: reserved1 = #{@frame.rsv1 ? 1 : 0}" +
279
- ", reserved2 = #{@frame.rsv2 ? 1 : 0 }" +
280
- ", reserved3 = #{@frame.rsv3 ? 1 : 0 }")
288
+ "One or more reserved bits are on: reserved1 = #{ @frame.rsv1 ? 1 : 0 }" +
289
+ ", reserved2 = #{ @frame.rsv2 ? 1 : 0 }" +
290
+ ", reserved3 = #{ @frame.rsv3 ? 1 : 0 }")
281
291
  end
282
292
 
283
293
  unless OPCODES.values.include?(@frame.opcode)
284
- return fail(:protocol_error, "Unrecognized frame opcode: #{@frame.opcode}")
294
+ return fail(:protocol_error, "Unrecognized frame opcode: #{ @frame.opcode }")
285
295
  end
286
296
 
287
297
  unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.final
288
- return fail(:protocol_error, "Received fragmented control frame: opcode = #{@frame.opcode}")
298
+ return fail(:protocol_error, "Received fragmented control frame: opcode = #{ @frame.opcode }")
289
299
  end
290
300
 
291
301
  if @message and OPENING_OPCODES.include?(@frame.opcode)
@@ -315,7 +325,7 @@ module WebSocket
315
325
  @stage = @frame.masked ? 3 : 4
316
326
 
317
327
  unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.length <= 125
318
- return fail(:protocol_error, "Received control frame having too long payload: #{@frame.length}")
328
+ return fail(:protocol_error, "Received control frame having too long payload: #{ @frame.length }")
319
329
  end
320
330
 
321
331
  return unless check_frame_length
@@ -337,7 +347,6 @@ module WebSocket
337
347
  opcode = frame.opcode
338
348
  payload = frame.payload = Mask.mask(buffer, @frame.masking_key)
339
349
  bytesize = payload.bytesize
340
- bytes = payload.bytes.to_a
341
350
 
342
351
  @frame = nil
343
352
 
@@ -351,8 +360,8 @@ module WebSocket
351
360
  @message << frame
352
361
 
353
362
  when OPCODES[:close] then
354
- code = (bytesize >= 2) ? payload.unpack(PACK_FORMATS[2]).first : nil
355
- reason = (bytesize > 2) ? Driver.encode(bytes[2..-1] || [], UNICODE) : nil
363
+ code, reason = payload.unpack('S>a*') if bytesize >= 2
364
+ reason = Driver.encode(reason || '', Encoding::UTF_8)
356
365
 
357
366
  unless (bytesize == 0) or
358
367
  (code && code >= MIN_RESERVED_ERROR && code <= MAX_RESERVED_ERROR) or
@@ -360,7 +369,7 @@ module WebSocket
360
369
  code = ERRORS[:protocol_error]
361
370
  end
362
371
 
363
- if bytesize > 125 or (bytesize > 2 and reason.nil?)
372
+ if bytesize > 125 or !reason.valid_encoding?
364
373
  code = ERRORS[:protocol_error]
365
374
  end
366
375
 
@@ -368,12 +377,14 @@ module WebSocket
368
377
 
369
378
  when OPCODES[:ping] then
370
379
  frame(payload, :pong)
380
+ emit(:ping, PingEvent.new(payload))
371
381
 
372
382
  when OPCODES[:pong] then
373
- message = Driver.encode(payload, UNICODE)
383
+ message = Driver.encode(payload, Encoding::UTF_8)
374
384
  callback = @ping_callbacks[message]
375
385
  @ping_callbacks.delete(message)
376
386
  callback.call if callback
387
+ emit(:pong, PongEvent.new(payload))
377
388
  end
378
389
 
379
390
  emit_message if frame.final and MESSAGE_OPCODES.include?(opcode)
@@ -387,7 +398,8 @@ module WebSocket
387
398
 
388
399
  case message.opcode
389
400
  when OPCODES[:text] then
390
- payload = Driver.encode(payload, UNICODE)
401
+ payload = Driver.encode(payload, Encoding::UTF_8)
402
+ payload = nil unless payload.valid_encoding?
391
403
  when OPCODES[:binary]
392
404
  payload = payload.bytes.to_a
393
405
  end
@@ -14,7 +14,7 @@ module WebSocket
14
14
  @rsv2 = false
15
15
  @rsv3 = false
16
16
  @opcode = nil
17
- @data = String.new('').force_encoding(BINARY)
17
+ @data = String.new('').force_encoding(Encoding::BINARY)
18
18
  end
19
19
 
20
20
  def <<(frame)
@@ -4,7 +4,7 @@ module WebSocket
4
4
  class Proxy
5
5
  include EventEmitter
6
6
 
7
- PORTS = {'ws' => 80, 'wss' => 443}
7
+ PORTS = { 'ws' => 80, 'wss' => 443 }
8
8
 
9
9
  attr_reader :status, :headers
10
10
 
@@ -20,7 +20,7 @@ module WebSocket
20
20
  @state = 0
21
21
 
22
22
  @headers = Headers.new
23
- @headers['Host'] = @origin.host + (@origin.port ? ":#{@origin.port}" : '')
23
+ @headers['Host'] = @origin.host + (@origin.port ? ":#{ @origin.port }" : '')
24
24
  @headers['Connection'] = 'keep-alive'
25
25
  @headers['Proxy-Connection'] = 'keep-alive'
26
26
 
@@ -41,7 +41,7 @@ module WebSocket
41
41
  @state = 1
42
42
 
43
43
  port = @origin.port || PORTS[@origin.scheme]
44
- start = "CONNECT #{@origin.host}:#{port} HTTP/1.1"
44
+ start = "CONNECT #{ @origin.host }:#{ port } HTTP/1.1"
45
45
  headers = [start, @headers.to_s, '']
46
46
 
47
47
  @socket.write(headers.join("\r\n"))
@@ -58,7 +58,7 @@ module WebSocket
58
58
  if @status == 200
59
59
  emit(:connect, ConnectEvent.new)
60
60
  else
61
- message = "Can't establish a connection to the server at #{@socket.url}"
61
+ message = "Can't establish a connection to the server at #{ @socket.url }"
62
62
  emit(:error, ProtocolError.new(message))
63
63
  end
64
64
  end
@@ -2,7 +2,7 @@ module WebSocket
2
2
  class Driver
3
3
 
4
4
  class Server < Driver
5
- EVENTS = %w[open message error close]
5
+ EVENTS = %w[open message error close ping pong]
6
6
 
7
7
  def initialize(socket, options = {})
8
8
  super
@@ -17,9 +17,9 @@ module WebSocket
17
17
  def url
18
18
  return nil unless e = env
19
19
 
20
- url = "ws://#{e['HTTP_HOST']}"
20
+ url = "ws://#{ e['HTTP_HOST'] }"
21
21
  url << e['PATH_INFO']
22
- url << "?#{e['QUERY_STRING']}" unless e['QUERY_STRING'] == ''
22
+ url << "?#{ e['QUERY_STRING'] }" unless e['QUERY_STRING'] == ''
23
23
  url
24
24
  end
25
25
 
@@ -6,13 +6,13 @@ module WebSocket
6
6
  MINIMUM_AUTOMATIC_PRUNE_OFFSET = 128
7
7
 
8
8
  def initialize
9
- @buffer = String.new('').force_encoding(BINARY)
9
+ @buffer = String.new('').force_encoding(Encoding::BINARY)
10
10
  @offset = 0
11
11
  end
12
12
 
13
13
  def put(chunk)
14
14
  return unless chunk and chunk.bytesize > 0
15
- @buffer << chunk.force_encoding(BINARY)
15
+ @buffer << chunk.force_encoding(Encoding::BINARY)
16
16
  end
17
17
 
18
18
  # Read bytes from the data:
@@ -42,7 +42,7 @@ module WebSocket
42
42
  buffer_size = @buffer.bytesize
43
43
 
44
44
  if @offset > buffer_size
45
- @buffer = String.new('').force_encoding(BINARY)
45
+ @buffer = String.new('').force_encoding(Encoding::BINARY)
46
46
  else
47
47
  @buffer = @buffer.byteslice(@offset, buffer_size - @offset)
48
48
  end
@@ -30,11 +30,11 @@ module WebSocket
30
30
  super
31
31
  @headers.each do |name, value|
32
32
  rack_name = name.upcase.gsub(/-/, '_')
33
- rack_name = "HTTP_#{rack_name}" unless RESERVED_HEADERS.include?(name)
33
+ rack_name = "HTTP_#{ rack_name }" unless RESERVED_HEADERS.include?(name)
34
34
  @env[rack_name] = value
35
35
  end
36
36
  if host = @env['HTTP_HOST']
37
- uri = URI.parse("http://#{host}")
37
+ uri = URI.parse("http://#{ host }")
38
38
  @env['SERVER_NAME'] = uri.host
39
39
  @env['SERVER_PORT'] = uri.port.to_s
40
40
  end
@@ -4,7 +4,7 @@ module WebSocket
4
4
  class Response
5
5
  include Headers
6
6
 
7
- STATUS_LINE = /^(HTTP\/[0-9]+\.[0-9]+) ([0-9]{3}) ([\x20-\x7e]+)$/
7
+ STATUS_LINE = /^(HTTP\/[0-9]+\.[0-9]+) ([0-9]{3}) ([\x20-\x7e]*)$/
8
8
 
9
9
  attr_reader :code
10
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: websocket-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Coglan
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-22 00:00:00.000000000 Z
11
+ date: 2021-05-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: websocket-extensions
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: rake-compiler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 0.8.0
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 0.8.0
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description:
83
+ description:
84
84
  email: jcoglan@gmail.com
85
85
  executables: []
86
86
  extensions:
@@ -91,7 +91,6 @@ files:
91
91
  - CHANGELOG.md
92
92
  - LICENSE.md
93
93
  - README.md
94
- - examples/tcp_server.rb
95
94
  - ext/websocket-driver/WebsocketMaskService.java
96
95
  - ext/websocket-driver/extconf.rb
97
96
  - ext/websocket-driver/websocket_mask.c
@@ -113,11 +112,11 @@ files:
113
112
  - lib/websocket/http/response.rb
114
113
  - lib/websocket/mask.rb
115
114
  - lib/websocket/websocket_mask.rb
116
- homepage: http://github.com/faye/websocket-driver-ruby
115
+ homepage: https://github.com/faye/websocket-driver-ruby
117
116
  licenses:
118
- - MIT
117
+ - Apache-2.0
119
118
  metadata: {}
120
- post_install_message:
119
+ post_install_message:
121
120
  rdoc_options:
122
121
  - "--main"
123
122
  - README.md
@@ -136,9 +135,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
135
  - !ruby/object:Gem::Version
137
136
  version: '0'
138
137
  requirements: []
139
- rubyforge_project:
140
- rubygems_version: 2.6.8
141
- signing_key:
138
+ rubygems_version: 3.1.6
139
+ signing_key:
142
140
  specification_version: 4
143
141
  summary: WebSocket protocol handler with pluggable I/O
144
142
  test_files: []
@@ -1,28 +0,0 @@
1
- require 'rubygems'
2
- require 'bundler/setup'
3
- require 'eventmachine'
4
- require 'websocket/driver'
5
- require 'permessage_deflate'
6
-
7
- module Connection
8
- def initialize
9
- @driver = WebSocket::Driver.server(self)
10
- @driver.add_extension(PermessageDeflate)
11
-
12
- @driver.on(:connect) { |e| @driver.start if WebSocket::Driver.websocket? @driver.env }
13
- @driver.on(:message) { |e| @driver.frame(e.data) }
14
- @driver.on(:close) { |e| close_connection_after_writing }
15
- end
16
-
17
- def receive_data(data)
18
- @driver.parse(data)
19
- end
20
-
21
- def write(data)
22
- send_data(data)
23
- end
24
- end
25
-
26
- EM.run {
27
- EM.start_server('127.0.0.1', ARGV[0], Connection)
28
- }