websocket-driver 0.7.5 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/LICENSE.md +1 -1
- data/README.md +11 -6
- data/lib/websocket/driver/client.rb +1 -2
- data/lib/websocket/driver/draft75.rb +1 -0
- data/lib/websocket/driver/hybi.rb +17 -5
- data/lib/websocket/driver/proxy.rb +1 -3
- data/lib/websocket/driver.rb +25 -7
- data/lib/websocket/http/headers.rb +7 -4
- data/lib/websocket/http/request.rb +12 -5
- metadata +19 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf509f60cf8b74b4a6e2dd7cf7038b53817271cfc2985d2234037946543e9fed
|
|
4
|
+
data.tar.gz: 3f4433596095d12a4f70b4477c20f0d6f4bfe96ef18ca711fee0996815a991a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a558a93b903403cc48e0683ce3247c02a613f9e4b19ee3bc476ccc621cdf3a23160a4eb2615cc259fe227a74de54f3728d11b6732fa2e88a6d1d672ba96240d4
|
|
7
|
+
data.tar.gz: 9f591c43f2f34de6444069d3c34ec7b5b4813cc9c2e76203f8d1d48942f6b1712c1ad2847887f47759012d9c0ac95a90f82bb144d0311fb94e4a9d9ce0eae10e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
### 0.8.2 / 2026-06-23
|
|
2
|
+
|
|
3
|
+
- Gracefully handle malformed `Host` headers in the `Server` driver
|
|
4
|
+
|
|
5
|
+
### 0.8.1 / 2026-06-04
|
|
6
|
+
|
|
7
|
+
- Close a draft-75/76 connection if a length header grows to exceed the
|
|
8
|
+
configured max length
|
|
9
|
+
- Fail the connection if a message is larger than the configured max length
|
|
10
|
+
after extension processing
|
|
11
|
+
- Limit the total HTTP request line and headers size to 32K
|
|
12
|
+
|
|
13
|
+
### 0.8.0 / 2025-05-25
|
|
14
|
+
|
|
15
|
+
- Emit binary message as a string with `Encoding::BINARY` instead of an array
|
|
16
|
+
- Add the option `:binary_data_format` to force the previous behaviour
|
|
17
|
+
|
|
18
|
+
### 0.7.7 / 2025-01-04
|
|
19
|
+
|
|
20
|
+
- Add `base64` gem to the dependencies to support Ruby 3.4
|
|
21
|
+
|
|
22
|
+
### 0.7.6 / 2023-07-25
|
|
23
|
+
|
|
24
|
+
- Fix handling of default ports in `Host` headers on Ruby 3.1+
|
|
25
|
+
|
|
1
26
|
### 0.7.5 / 2021-06-12
|
|
2
27
|
|
|
3
28
|
- Do not change the encoding of strings passed to `Driver#text`
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
|
@@ -275,6 +275,10 @@ keys:
|
|
|
275
275
|
- `:protocols` - an array of strings representing acceptable subprotocols for
|
|
276
276
|
use over the socket. The driver will negotiate one of these to use via the
|
|
277
277
|
`Sec-WebSocket-Protocol` header if supported by the other peer.
|
|
278
|
+
- `:binary_data_format` - in older versions of this library, binary messages
|
|
279
|
+
were represented as arrays of bytes, whereas they're now represented as
|
|
280
|
+
strings with `Encoding::BINARY` for performance reasons. Set this option to
|
|
281
|
+
`:array` to restore the old behaviour.
|
|
278
282
|
|
|
279
283
|
All drivers respond to the following API methods, but some of them are no-ops
|
|
280
284
|
depending on whether the client supports the behaviour.
|
|
@@ -290,8 +294,8 @@ Adds a callback block to execute when the socket becomes open.
|
|
|
290
294
|
#### `driver.on :message, -> (event) {}`
|
|
291
295
|
|
|
292
296
|
Adds a callback block to execute when a message is received. `event` will have a
|
|
293
|
-
`data` attribute
|
|
294
|
-
|
|
297
|
+
`data` attribute whose value is a string with the encoding `Encoding::UTF_8` for
|
|
298
|
+
text message, and `Encoding::BINARY` for binary message.
|
|
295
299
|
|
|
296
300
|
#### `driver.on :error, -> (event) {}`
|
|
297
301
|
|
|
@@ -346,11 +350,12 @@ Sends a text message over the socket. If the socket handshake is not yet
|
|
|
346
350
|
complete, the message will be queued until it is. Returns `true` if the message
|
|
347
351
|
was sent or queued, and `false` if the socket can no longer send messages.
|
|
348
352
|
|
|
349
|
-
#### `driver.binary(
|
|
353
|
+
#### `driver.binary(buffer)`
|
|
350
354
|
|
|
351
|
-
Takes
|
|
352
|
-
|
|
353
|
-
|
|
355
|
+
Takes either a string with encoding `Encoding::BINARY`, or an array of
|
|
356
|
+
byte-sized integers, and sends it as a binary message. Will queue and return
|
|
357
|
+
`true` or `false` the same way as the `text` method. It will also return `false`
|
|
358
|
+
if the driver does not support binary messages.
|
|
354
359
|
|
|
355
360
|
#### `driver.ping(string = '', &callback)`
|
|
356
361
|
|
|
@@ -23,11 +23,10 @@ module WebSocket
|
|
|
23
23
|
raise URIError, "#{ socket.url } is not a valid WebSocket URL"
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
host = uri.host + (uri.port ? ":#{ uri.port }" : '')
|
|
27
26
|
path = (uri.path == '') ? '/' : uri.path
|
|
28
27
|
@pathname = path + (uri.query ? '?' + uri.query : '')
|
|
29
28
|
|
|
30
|
-
@headers['Host'] =
|
|
29
|
+
@headers['Host'] = Driver.host_header(uri)
|
|
31
30
|
@headers['Upgrade'] = 'websocket'
|
|
32
31
|
@headers['Connection'] = 'Upgrade'
|
|
33
32
|
@headers['Sec-WebSocket-Key'] = @key
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
1
3
|
module WebSocket
|
|
2
4
|
class Driver
|
|
3
5
|
|
|
@@ -161,11 +163,13 @@ module WebSocket
|
|
|
161
163
|
message = Message.new
|
|
162
164
|
frame = Frame.new
|
|
163
165
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
is_binary = (Array === buffer or buffer.encoding == Encoding::BINARY)
|
|
167
|
+
payload = Driver.encode(buffer, is_binary ? nil : Encoding::UTF_8)
|
|
168
|
+
payload = [code, payload].pack('S>a*') if code
|
|
169
|
+
type ||= is_binary ? :binary : :text
|
|
166
170
|
|
|
167
|
-
|
|
168
|
-
|
|
171
|
+
message.rsv1 = message.rsv2 = message.rsv3 = false
|
|
172
|
+
message.opcode = OPCODES[type]
|
|
169
173
|
message.data = payload
|
|
170
174
|
|
|
171
175
|
if MESSAGE_OPCODES.include?(message.opcode)
|
|
@@ -396,12 +400,20 @@ module WebSocket
|
|
|
396
400
|
|
|
397
401
|
payload = message.data
|
|
398
402
|
|
|
403
|
+
if payload.bytesize > @max_length
|
|
404
|
+
return fail(:too_large, 'WebSocket frame length too large')
|
|
405
|
+
end
|
|
406
|
+
|
|
399
407
|
case message.opcode
|
|
400
408
|
when OPCODES[:text] then
|
|
401
409
|
payload = Driver.encode(payload, Encoding::UTF_8)
|
|
402
410
|
payload = nil unless payload.valid_encoding?
|
|
403
411
|
when OPCODES[:binary]
|
|
404
|
-
|
|
412
|
+
if @binary_data_format == :array
|
|
413
|
+
payload = payload.bytes.to_a
|
|
414
|
+
else
|
|
415
|
+
payload = Driver.encode(payload, Encoding::BINARY)
|
|
416
|
+
end
|
|
405
417
|
end
|
|
406
418
|
|
|
407
419
|
if payload
|
|
@@ -4,8 +4,6 @@ module WebSocket
|
|
|
4
4
|
class Proxy
|
|
5
5
|
include EventEmitter
|
|
6
6
|
|
|
7
|
-
PORTS = { 'ws' => 80, 'wss' => 443 }
|
|
8
|
-
|
|
9
7
|
attr_reader :status, :headers
|
|
10
8
|
|
|
11
9
|
def initialize(client, origin, options)
|
|
@@ -20,7 +18,7 @@ module WebSocket
|
|
|
20
18
|
@state = 0
|
|
21
19
|
|
|
22
20
|
@headers = Headers.new
|
|
23
|
-
@headers['Host'] =
|
|
21
|
+
@headers['Host'] = Driver.host_header(@origin)
|
|
24
22
|
@headers['Connection'] = 'keep-alive'
|
|
25
23
|
@headers['Proxy-Connection'] = 'keep-alive'
|
|
26
24
|
|
data/lib/websocket/driver.rb
CHANGED
|
@@ -42,6 +42,7 @@ module WebSocket
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
MAX_LENGTH = 0x3ffffff
|
|
45
|
+
PORTS = { 'ws' => 80, 'wss' => 443 }
|
|
45
46
|
STATES = [:connecting, :open, :closing, :closed]
|
|
46
47
|
|
|
47
48
|
ConnectEvent = Struct.new(nil)
|
|
@@ -70,7 +71,7 @@ module WebSocket
|
|
|
70
71
|
|
|
71
72
|
def initialize(socket, options = {})
|
|
72
73
|
super()
|
|
73
|
-
Driver.validate_options(options, [:max_length, :masking, :require_masking, :protocols])
|
|
74
|
+
Driver.validate_options(options, [:max_length, :masking, :require_masking, :protocols, :binary_data_format])
|
|
74
75
|
|
|
75
76
|
@socket = socket
|
|
76
77
|
@reader = StreamReader.new
|
|
@@ -79,6 +80,8 @@ module WebSocket
|
|
|
79
80
|
@headers = Headers.new
|
|
80
81
|
@queue = []
|
|
81
82
|
@ready_state = 0
|
|
83
|
+
|
|
84
|
+
@binary_data_format = options[:binary_data_format] || :string
|
|
82
85
|
end
|
|
83
86
|
|
|
84
87
|
def state
|
|
@@ -196,17 +199,26 @@ module WebSocket
|
|
|
196
199
|
|
|
197
200
|
def self.encode(data, encoding = nil)
|
|
198
201
|
if Array === data
|
|
202
|
+
data = data.pack('C*')
|
|
199
203
|
encoding ||= Encoding::BINARY
|
|
200
|
-
return data.pack('C*').force_encoding(encoding)
|
|
201
204
|
end
|
|
202
205
|
|
|
203
|
-
encoding
|
|
206
|
+
return data if encoding.nil? or data.encoding == encoding
|
|
204
207
|
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
if data.encoding == Encoding::BINARY
|
|
209
|
+
data = data.dup if data.frozen?
|
|
210
|
+
data.force_encoding(encoding)
|
|
211
|
+
else
|
|
212
|
+
data.encode(encoding)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
207
215
|
|
|
208
|
-
|
|
209
|
-
|
|
216
|
+
def self.host_header(uri)
|
|
217
|
+
host = uri.host
|
|
218
|
+
if uri.port and uri.port != PORTS[uri.scheme]
|
|
219
|
+
host += ":#{uri.port}"
|
|
220
|
+
end
|
|
221
|
+
host
|
|
210
222
|
end
|
|
211
223
|
|
|
212
224
|
def self.validate_options(options, valid_keys)
|
|
@@ -215,6 +227,12 @@ module WebSocket
|
|
|
215
227
|
raise ConfigurationError, "Unrecognized option: #{ key.inspect }"
|
|
216
228
|
end
|
|
217
229
|
end
|
|
230
|
+
|
|
231
|
+
if options[:binary_data_format]
|
|
232
|
+
unless [:array, :string].include?(options[:binary_data_format])
|
|
233
|
+
raise ConfigurationError, "Invalid :binary_data_format: #{options[:binary_data_format].inspect}"
|
|
234
|
+
end
|
|
235
|
+
end
|
|
218
236
|
end
|
|
219
237
|
|
|
220
238
|
def self.websocket?(env)
|
|
@@ -2,7 +2,7 @@ module WebSocket
|
|
|
2
2
|
module HTTP
|
|
3
3
|
|
|
4
4
|
module Headers
|
|
5
|
-
|
|
5
|
+
MAX_REQUEST_SIZE = 32768
|
|
6
6
|
CR = 0x0D
|
|
7
7
|
LF = 0x0A
|
|
8
8
|
|
|
@@ -38,6 +38,7 @@ module WebSocket
|
|
|
38
38
|
attr_reader :headers
|
|
39
39
|
|
|
40
40
|
def initialize
|
|
41
|
+
@size = 0
|
|
41
42
|
@buffer = []
|
|
42
43
|
@env = {}
|
|
43
44
|
@headers = {}
|
|
@@ -54,6 +55,9 @@ module WebSocket
|
|
|
54
55
|
|
|
55
56
|
def parse(chunk)
|
|
56
57
|
chunk.each_byte do |octet|
|
|
58
|
+
@size += 1
|
|
59
|
+
return error if @size > MAX_REQUEST_SIZE
|
|
60
|
+
|
|
57
61
|
if octet == LF and @stage < 2
|
|
58
62
|
@buffer.pop if @buffer.last == CR
|
|
59
63
|
if @buffer.empty?
|
|
@@ -71,9 +75,8 @@ module WebSocket
|
|
|
71
75
|
end
|
|
72
76
|
end
|
|
73
77
|
@buffer = []
|
|
74
|
-
|
|
75
|
-
@buffer << octet
|
|
76
|
-
error if @stage < 2 and @buffer.size > MAX_LINE_LENGTH
|
|
78
|
+
elsif @stage >= 0
|
|
79
|
+
@buffer << octet
|
|
77
80
|
end
|
|
78
81
|
end
|
|
79
82
|
@env['rack.input'] = StringIO.new(string_buffer)
|
|
@@ -33,11 +33,18 @@ module WebSocket
|
|
|
33
33
|
rack_name = "HTTP_#{ rack_name }" unless RESERVED_HEADERS.include?(name)
|
|
34
34
|
@env[rack_name] = value
|
|
35
35
|
end
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
|
|
37
|
+
set_server_vars
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def set_server_vars
|
|
41
|
+
return unless host = @env['HTTP_HOST']
|
|
42
|
+
|
|
43
|
+
uri = URI.parse("http://#{ host }")
|
|
44
|
+
@env['SERVER_NAME'] = uri.host
|
|
45
|
+
@env['SERVER_PORT'] = uri.port.to_s
|
|
46
|
+
rescue URI::InvalidURIError
|
|
47
|
+
error
|
|
41
48
|
end
|
|
42
49
|
end
|
|
43
50
|
|
metadata
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: websocket-driver
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Coglan
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
13
26
|
- !ruby/object:Gem::Dependency
|
|
14
27
|
name: websocket-extensions
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,7 +93,6 @@ dependencies:
|
|
|
80
93
|
- - ">="
|
|
81
94
|
- !ruby/object:Gem::Version
|
|
82
95
|
version: '0'
|
|
83
|
-
description:
|
|
84
96
|
email: jcoglan@gmail.com
|
|
85
97
|
executables: []
|
|
86
98
|
extensions:
|
|
@@ -115,8 +127,8 @@ files:
|
|
|
115
127
|
homepage: https://github.com/faye/websocket-driver-ruby
|
|
116
128
|
licenses:
|
|
117
129
|
- Apache-2.0
|
|
118
|
-
metadata:
|
|
119
|
-
|
|
130
|
+
metadata:
|
|
131
|
+
changelog_uri: https://github.com/faye/websocket-driver-ruby/blob/main/CHANGELOG.md
|
|
120
132
|
rdoc_options:
|
|
121
133
|
- "--main"
|
|
122
134
|
- README.md
|
|
@@ -135,8 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
135
147
|
- !ruby/object:Gem::Version
|
|
136
148
|
version: '0'
|
|
137
149
|
requirements: []
|
|
138
|
-
rubygems_version: 3.
|
|
139
|
-
signing_key:
|
|
150
|
+
rubygems_version: 3.6.9
|
|
140
151
|
specification_version: 4
|
|
141
152
|
summary: WebSocket protocol handler with pluggable I/O
|
|
142
153
|
test_files: []
|