websocket-driver 0.7.0 → 0.7.5

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: 0ddf5e80d9f5080fc8d494d3e93611cdd23eb759
4
- data.tar.gz: 15ad3e7d961a9940971a76817ceb6ce0bba8a6f0
2
+ SHA256:
3
+ metadata.gz: 046c514660d2c0dab2cf42bce8314a17e713909377a5da470beaeadf666ff28e
4
+ data.tar.gz: 9830c412394044d102dd6f313346721076ef0f97284d6182aeb06862e1f427b2
5
5
  SHA512:
6
- metadata.gz: 3655528c0e0533a75512fc1e69044b03506fdf9d751d0c41875b1efea98f0eae55024887e6445fa17a30fc8996b85d3ca808e89bb72631c1f7fc6ac99a39e227
7
- data.tar.gz: 0d85ca3a28fcb776bcb292ad90886cd98e27ad84739bcab1478e37b36afe8c964475faf4fda0201fd35cbf3dc006106a89db4da808085585c7d292cac5821739
6
+ metadata.gz: cea4027f342f51a688c8004a92ca29b68af9a9395b5e7f8d2462b8742067862718d23f4be9ca6162f761774490e4cdf33111bb746f9221d5acace6546a749b35
7
+ data.tar.gz: d124aa6acf1cbfa498b44d02d6aa659c99e97e177441e8cbd43958db38c6cf03d703d26ae73998a09719c0612f538554049d58b58aa8cc8ab5f0bb458e27c988
data/CHANGELOG.md CHANGED
@@ -1,127 +1,156 @@
1
+ ### 0.7.5 / 2021-06-12
2
+
3
+ - Do not change the encoding of strings passed to `Driver#text`
4
+
5
+ ### 0.7.4 / 2021-05-24
6
+
7
+ - Optimise conversions between strings and byte arrays and related encoding
8
+ operations, to reduce amount of allocation and copying
9
+
10
+ ### 0.7.3 / 2020-07-09
11
+
12
+ - Let the client accept HTTP responses that have an empty reason phrase
13
+ following the `101` status code
14
+
15
+ ### 0.7.2 / 2020-05-22
16
+
17
+ - Emit `ping` and `pong` events from the `Server` driver
18
+ - Handle draft-76 handshakes correctly if the request's body is a frozen string
19
+
20
+ ### 0.7.1 / 2019-06-10
21
+
22
+ - Catch any exceptions produced while generating a handshake response and send a
23
+ `400 Bad Request` response to the client
24
+ - Pick the RFC-6455 protocol version if the request contains any of the headers
25
+ used by that version
26
+ - Handle errors encountered while handling malformed draft-76 requests
27
+ - Change license from MIT to Apache 2.0
28
+
1
29
  ### 0.7.0 / 2017-09-11
2
30
 
3
- * Add `ping` and `pong` to the set of events users can listen to
31
+ - Add `ping` and `pong` to the set of events users can listen to
4
32
 
5
33
  ### 0.6.5 / 2017-01-22
6
34
 
7
- * Provide a pure-Ruby fallback for the native unmasking code
35
+ - Provide a pure-Ruby fallback for the native unmasking code
8
36
 
9
37
  ### 0.6.4 / 2016-05-20
10
38
 
11
- * Amend warnings issued when running with -W2
12
- * Make sure message strings passed in by the app are transcoded to UTF-8
13
- * Copy strings if necessary for frozen-string compatibility
39
+ - Amend warnings issued when running with -W2
40
+ - Make sure message strings passed in by the app are transcoded to UTF-8
41
+ - Copy strings if necessary for frozen-string compatibility
14
42
 
15
43
  ### 0.6.3 / 2015-11-06
16
44
 
17
- * Reject draft-76 handshakes if their Sec-WebSocket-Key headers are invalid
18
- * Throw a more helpful error if a client is created with an invalid URL
45
+ - Reject draft-76 handshakes if their Sec-WebSocket-Key headers are invalid
46
+ - Throw a more helpful error if a client is created with an invalid URL
19
47
 
20
48
  ### 0.6.2 / 2015-07-18
21
49
 
22
- * When the peer sends a close frame with no error code, emit 1000
50
+ - When the peer sends a close frame with no error code, emit 1000
23
51
 
24
52
  ### 0.6.1 / 2015-07-13
25
53
 
26
- * Fix how events are stored in `EventEmitter` to fix a backward-compatibility
54
+ - Fix how events are stored in `EventEmitter` to fix a backward-compatibility
27
55
  violation introduced in the last release
28
- * Use the `Array#pack` and `String#unpack` methods for reading/writing numbers
56
+ - Use the `Array#pack` and `String#unpack` methods for reading/writing numbers
29
57
  to buffers rather than including duplicate logic for this
30
58
 
31
59
  ### 0.6.0 / 2015-07-08
32
60
 
33
- * Use `SecureRandom` to generate the `Sec-WebSocket-Key` header
34
- * Allow the parser to recover cleanly if event listeners raise an error
35
- * Let the `on()` method take a lambda as a positional argument rather than a block
36
- * Add a `pong` method for sending unsolicited pong frames
61
+ - Use `SecureRandom` to generate the `Sec-WebSocket-Key` header
62
+ - Allow the parser to recover cleanly if event listeners raise an error
63
+ - Let the `on()` method take a lambda as a positional argument rather than a
64
+ block
65
+ - Add a `pong` method for sending unsolicited pong frames
37
66
 
38
67
  ### 0.5.4 / 2015-03-29
39
68
 
40
- * Don't emit extra close frames if we receive a close frame after we already
69
+ - Don't emit extra close frames if we receive a close frame after we already
41
70
  sent one
42
- * Fail the connection when the driver receives an invalid
71
+ - Fail the connection when the driver receives an invalid
43
72
  `Sec-WebSocket-Extensions` header
44
73
 
45
74
  ### 0.5.3 / 2015-02-22
46
75
 
47
- * Don't treat incoming data as WebSocket frames if a client driver is closed
76
+ - Don't treat incoming data as WebSocket frames if a client driver is closed
48
77
  before receiving the server handshake
49
78
 
50
79
  ### 0.5.2 / 2015-02-19
51
80
 
52
- * Don't emit multiple `error` events
81
+ - Don't emit multiple `error` events
53
82
 
54
83
  ### 0.5.1 / 2014-12-18
55
84
 
56
- * Don't allow drivers to be created with unrecognized options
85
+ - Don't allow drivers to be created with unrecognized options
57
86
 
58
87
  ### 0.5.0 / 2014-12-13
59
88
 
60
- * Support protocol extensions via the websocket-extensions module
89
+ - Support protocol extensions via the websocket-extensions module
61
90
 
62
91
  ### 0.4.0 / 2014-11-08
63
92
 
64
- * Support connection via HTTP proxies using `CONNECT`
93
+ - Support connection via HTTP proxies using `CONNECT`
65
94
 
66
95
  ### 0.3.5 / 2014-10-04
67
96
 
68
- * Fix bug where the `Server` driver doesn't pass `ping` callbacks to its
97
+ - Fix bug where the `Server` driver doesn't pass `ping` callbacks to its
69
98
  delegate
70
- * Fix an arity error when calling `fail_request`
71
- * Allow `close` to be called before `start` to close the driver
99
+ - Fix an arity error when calling `fail_request`
100
+ - Allow `close` to be called before `start` to close the driver
72
101
 
73
102
  ### 0.3.4 / 2014-07-06
74
103
 
75
- * Don't hold references to frame buffers after a message has been emitted
76
- * Make sure that `protocol` and `version` are exposed properly by the TCP driver
77
- * Correct HTTP header parsing based on RFC 7230; header names cannot contain
104
+ - Don't hold references to frame buffers after a message has been emitted
105
+ - Make sure that `protocol` and `version` are exposed properly by the TCP driver
106
+ - Correct HTTP header parsing based on RFC 7230; header names cannot contain
78
107
  backslashes
79
108
 
80
109
  ### 0.3.3 / 2014-04-24
81
110
 
82
- * Fix problems with loading C and Java native extension code
83
- * Correct the acceptable characters used in the HTTP parser
84
- * Correct the draft-76 status line reason phrase
111
+ - Fix problems with loading C and Java native extension code
112
+ - Correct the acceptable characters used in the HTTP parser
113
+ - Correct the draft-76 status line reason phrase
85
114
 
86
115
  ### 0.3.2 / 2013-12-29
87
116
 
88
- * Expand `max_length` to cover sequences of continuation frames and
117
+ - Expand `max_length` to cover sequences of continuation frames and
89
118
  `draft-{75,76}`
90
- * Decrease default maximum frame buffer size to 64MB
91
- * Stop parsing when the protocol enters a failure mode, to save CPU cycles
119
+ - Decrease default maximum frame buffer size to 64MB
120
+ - Stop parsing when the protocol enters a failure mode, to save CPU cycles
92
121
 
93
122
  ### 0.3.1 / 2013-12-03
94
123
 
95
- * Add a `max_length` option to limit allowed frame size
124
+ - Add a `max_length` option to limit allowed frame size
96
125
 
97
126
  ### 0.3.0 / 2013-09-09
98
127
 
99
- * Support client URLs with Basic Auth credentials
128
+ - Support client URLs with Basic Auth credentials
100
129
 
101
130
  ### 0.2.3 / 2013-08-04
102
131
 
103
- * Fix bug in EventEmitter#emit when listeners are removed
132
+ - Fix bug in EventEmitter#emit when listeners are removed
104
133
 
105
134
  ### 0.2.2 / 2013-08-04
106
135
 
107
- * Fix bug in EventEmitter#listener_count for unregistered events
136
+ - Fix bug in EventEmitter#listener_count for unregistered events
108
137
 
109
138
  ### 0.2.1 / 2013-07-05
110
139
 
111
- * Queue sent messages if the client has not begun trying to connect
112
- * Encode all strings sent to I/O as `ASCII-8BIT`
140
+ - Queue sent messages if the client has not begun trying to connect
141
+ - Encode all strings sent to I/O as `ASCII-8BIT`
113
142
 
114
143
  ### 0.2.0 / 2013-05-12
115
144
 
116
- * Add API for setting and reading headers
117
- * Add Driver.server() method for getting a driver for TCP servers
145
+ - Add API for setting and reading headers
146
+ - Add Driver.server() method for getting a driver for TCP servers
118
147
 
119
148
  ### 0.1.0 / 2013-05-04
120
149
 
121
- * First stable release
150
+ - First stable release
122
151
 
123
152
  ### 0.0.0 / 2013-04-22
124
153
 
125
- * First release
126
- * Proof of concept for people to try out
127
- * Might be unstable
154
+ - First release
155
+ - Proof of concept for people to try out
156
+ - Might be unstable
data/LICENSE.md CHANGED
@@ -1,20 +1,12 @@
1
- # The MIT License
1
+ Copyright 2010-2021 James Coglan
2
2
 
3
- Copyright (c) 2010-2017 James Coglan
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
- Permission is hereby granted, free of charge, to any person obtaining a copy of
6
- this software and associated documentation files (the 'Software'), to deal in
7
- the Software without restriction, including without limitation the rights to
8
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
- the Software, and to permit persons to whom the Software is furnished to do so,
10
- subject to the following conditions:
7
+ http://www.apache.org/licenses/LICENSE-2.0
11
8
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
- 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,33 +283,33 @@ 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
 
298
- #### `driver.on :ping, -> (event) { }`
307
+ #### `driver.on :ping, -> (event) {}`
299
308
 
300
309
  Adds a callback block to execute when a ping is received. You do not need to
301
310
  handle this by sending a pong frame yourself; the driver handles this for you.
302
311
 
303
- #### `driver.on :pong, -> (event) { }`
312
+ #### `driver.on :pong, -> (event) {}`
304
313
 
305
314
  Adds a callback block to execute when a pong is received. If this was in
306
315
  response to a ping you sent, you can also handle this event 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,9 +44,6 @@ 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)
@@ -101,15 +98,24 @@ module WebSocket
101
98
 
102
99
  def start
103
100
  return false unless @ready_state == 0
104
- response = handshake_response
105
- 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
+
106
112
  @socket.write(response)
107
113
  open unless @stage == -1
108
114
  true
109
115
  end
110
116
 
111
117
  def text(message)
112
- message = message.encode(UNICODE) unless message.encoding.name == UNICODE
118
+ message = Driver.encode(message, Encoding::UTF_8)
113
119
  frame(message, :text)
114
120
  end
115
121
 
@@ -134,6 +140,24 @@ module WebSocket
134
140
 
135
141
  private
136
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
+
137
161
  def open
138
162
  @ready_state = 1
139
163
  @queue.each { |message| frame(*message) }
@@ -155,35 +179,40 @@ module WebSocket
155
179
  end
156
180
 
157
181
  def self.rack(socket, options = {})
158
- env = socket.env
159
- 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
160
189
  Hybi.new(socket, options.merge(:require_masking => true))
161
- elsif env['HTTP_SEC_WEBSOCKET_KEY1']
190
+ elsif key1 or key2
162
191
  Draft76.new(socket, options)
163
192
  else
164
193
  Draft75.new(socket, options)
165
194
  end
166
195
  end
167
196
 
168
- def self.encode(string, encoding = nil)
169
- case string
170
- when Array then
171
- string = string.pack('C*')
172
- encoding ||= BINARY
173
- when String then
174
- encoding ||= UNICODE
197
+ def self.encode(data, encoding = nil)
198
+ if Array === data
199
+ encoding ||= Encoding::BINARY
200
+ return data.pack('C*').force_encoding(encoding)
175
201
  end
176
- unless string.encoding.name == encoding
177
- string = string.dup if string.frozen?
178
- string.force_encoding(encoding)
179
- end
180
- string.valid_encoding? ? string : nil
202
+
203
+ encoding ||= Encoding::UTF_8
204
+
205
+ return data if data.encoding == encoding
206
+ return data.encode(encoding) unless data.encoding == Encoding::BINARY
207
+
208
+ data = data.dup if data.frozen?
209
+ data.force_encoding(encoding)
181
210
  end
182
211
 
183
212
  def self.validate_options(options, valid_keys)
184
213
  options.keys.each do |key|
185
214
  unless valid_keys.include?(key)
186
- raise ConfigurationError, "Unrecognized option: #{key.inspect}"
215
+ raise ConfigurationError, "Unrecognized option: #{ key.inspect }"
187
216
  end
188
217
  end
189
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)
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
 
@@ -371,7 +380,7 @@ module WebSocket
371
380
  emit(:ping, PingEvent.new(payload))
372
381
 
373
382
  when OPCODES[:pong] then
374
- message = Driver.encode(payload, UNICODE)
383
+ message = Driver.encode(payload, Encoding::UTF_8)
375
384
  callback = @ping_callbacks[message]
376
385
  @ping_callbacks.delete(message)
377
386
  callback.call if callback
@@ -389,7 +398,8 @@ module WebSocket
389
398
 
390
399
  case message.opcode
391
400
  when OPCODES[:text] then
392
- payload = Driver.encode(payload, UNICODE)
401
+ payload = Driver.encode(payload, Encoding::UTF_8)
402
+ payload = nil unless payload.valid_encoding?
393
403
  when OPCODES[:binary]
394
404
  payload = payload.bytes.to_a
395
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.7.0
4
+ version: 0.7.5
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-09-11 00:00:00.000000000 Z
11
+ date: 2021-06-12 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:
@@ -114,9 +114,9 @@ files:
114
114
  - lib/websocket/websocket_mask.rb
115
115
  homepage: https://github.com/faye/websocket-driver-ruby
116
116
  licenses:
117
- - MIT
117
+ - Apache-2.0
118
118
  metadata: {}
119
- post_install_message:
119
+ post_install_message:
120
120
  rdoc_options:
121
121
  - "--main"
122
122
  - README.md
@@ -135,9 +135,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
135
  - !ruby/object:Gem::Version
136
136
  version: '0'
137
137
  requirements: []
138
- rubyforge_project:
139
- rubygems_version: 2.6.11
140
- signing_key:
138
+ rubygems_version: 3.1.6
139
+ signing_key:
141
140
  specification_version: 4
142
141
  summary: WebSocket protocol handler with pluggable I/O
143
142
  test_files: []