protocol-websocket 0.7.4 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ # Extensions
2
+
3
+ WebSockets have a mechanism for implementing extensions. The only published extension is for per-message compression. It operates on complete messages rather than individual frames.
4
+
5
+ ## Setup
6
+
7
+ Clients need to define a set of extensions they want to support. The server then receives this via the `Sec-WebSocket-Extensions` header which includes a list of:
8
+
9
+ Name, Options
10
+
11
+ The server processes this and returns a subset of accepted `(Name, Options)`. It also instantiates the extensions and applies them to the server connection object.
12
+
13
+ The client receives a list of accepted `(Name, Options)` and instantiates the extensions and applies them to the client connection object.
@@ -0,0 +1,148 @@
1
+ # Copyright, 2021, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'extension/compression'
22
+ require_relative 'headers'
23
+
24
+ module Protocol
25
+ module WebSocket
26
+ module Extensions
27
+ def self.parse(headers)
28
+ return to_enum(:parse, headers) unless block_given?
29
+
30
+ headers.each do |header|
31
+ name, *arguments = header.split(/\s*;\s*/)
32
+
33
+ arguments = arguments.map do |argument|
34
+ argument.split('=', 2)
35
+ end
36
+
37
+ yield name, arguments
38
+ end
39
+ end
40
+
41
+ class Client
42
+ def self.default
43
+ self.new([
44
+ [Extension::Compression, {}]
45
+ ])
46
+ end
47
+
48
+ def initialize(extensions = [])
49
+ @extensions = extensions
50
+ @accepted = []
51
+ end
52
+
53
+ attr :extensions
54
+ attr :accepted
55
+
56
+ def named
57
+ @extensions.map do |extension|
58
+ [extension.first::NAME, extension]
59
+ end.to_h
60
+ end
61
+
62
+ def offer
63
+ @extensions.each do |extension, options|
64
+ if header = extension.offer(**options)
65
+ yield header
66
+ end
67
+ end
68
+ end
69
+
70
+ def accept(headers)
71
+ named = self.named
72
+
73
+ # Each response header should map to at least one extension.
74
+ Extensions.parse(headers) do |name, arguments|
75
+ if extension = named.delete(name)
76
+ klass, options = extension
77
+
78
+ options = klass.accept(arguments, **options)
79
+
80
+ @accepted << [klass, options]
81
+ end
82
+ end
83
+ end
84
+
85
+ def apply(connection)
86
+ @accepted.each do |(klass, options)|
87
+ klass.client(connection, **options)
88
+ end
89
+ end
90
+ end
91
+
92
+ class Server
93
+ def self.default
94
+ self.new([
95
+ [Extension::Compression, {}]
96
+ ])
97
+ end
98
+
99
+ def initialize(extensions)
100
+ @extensions = extensions
101
+ @accepted = []
102
+ end
103
+
104
+ attr :extensions
105
+ attr :accepted
106
+
107
+ def named
108
+ @extensions.map do |extension|
109
+ [extension.first::NAME, extension]
110
+ end.to_h
111
+ end
112
+
113
+ def accept(headers)
114
+ extensions = []
115
+
116
+ named = self.named
117
+ response = []
118
+
119
+ # Each response header should map to at least one extension.
120
+ Extensions.parse(headers) do |name, arguments|
121
+ if extension = named[name]
122
+ klass, options = extension
123
+
124
+ if result = klass.negotiate(arguments, **options)
125
+ header, options = result
126
+
127
+ # The extension is accepted and no further offers will be considered:
128
+ named.delete(name)
129
+
130
+ yield header
131
+
132
+ @accepted << [klass, options]
133
+ end
134
+ end
135
+ end
136
+
137
+ return headers
138
+ end
139
+
140
+ def apply(connection)
141
+ @accepted.reverse_each do |(klass, options)|
142
+ klass.server(connection, **options)
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -25,11 +25,22 @@ module Protocol
25
25
  class Frame
26
26
  include Comparable
27
27
 
28
- OPCODE = 0
28
+ RSV1 = 0b0100
29
+ RSV2 = 0b0010
30
+ RSV3 = 0b0001
31
+ RESERVED = RSV1 | RSV2 | RSV3
29
32
 
30
- # @param length [Integer] the length of the payload, or nil if the header has not been read yet.
31
- def initialize(finished = true, payload = nil, opcode: self.class::OPCODE, mask: false)
33
+ OPCODE = 0
34
+
35
+ # @parameter length [Integer] The length of the payload, or nil if the header has not been read yet.
36
+ # @parameter mask [Boolean | String] An optional 4-byte string which is used to mask the payload.
37
+ def initialize(finished = true, payload = nil, flags: 0, opcode: self.class::OPCODE, mask: false)
38
+ if mask == true
39
+ mask = SecureRandom.bytes(4)
40
+ end
41
+
32
42
  @finished = finished
43
+ @flags = flags
33
44
  @opcode = opcode
34
45
  @mask = mask
35
46
  @length = payload&.bytesize
@@ -41,11 +52,11 @@ module Protocol
41
52
  end
42
53
 
43
54
  def to_ary
44
- [@finished, @opcode, @mask, @length, @payload]
55
+ [@finished, @flags, @opcode, @mask, @length, @payload]
45
56
  end
46
57
 
47
58
  def control?
48
- @opcode & 0x8
59
+ @opcode & 0x8 != 0
49
60
  end
50
61
 
51
62
  def data?
@@ -82,6 +93,7 @@ module Protocol
82
93
  # +---------------------------------------------------------------+
83
94
 
84
95
  attr_accessor :finished
96
+ attr_accessor :flags
85
97
  attr_accessor :opcode
86
98
  attr_accessor :mask
87
99
  attr_accessor :length
@@ -93,9 +105,9 @@ module Protocol
93
105
  if length.bit_length > 63
94
106
  raise ProtocolError, "Frame length #{@length} bigger than allowed maximum!"
95
107
  end
96
-
108
+
97
109
  if @mask
98
- @payload = String.new.b
110
+ @payload = String.new(encoding: Encoding::BINARY)
99
111
 
100
112
  for i in 0...data.bytesize do
101
113
  @payload << (data.getbyte(i) ^ mask.getbyte(i % 4))
@@ -106,11 +118,13 @@ module Protocol
106
118
  @payload = data
107
119
  @length = length
108
120
  end
121
+
122
+ return self
109
123
  end
110
124
 
111
125
  def unpack
112
- if @mask
113
- data = String.new.b
126
+ if @mask and !@payload.empty?
127
+ data = String.new(encoding: Encoding::BINARY)
114
128
 
115
129
  for i in 0...@payload.bytesize do
116
130
  data << (@payload.getbyte(i) ^ @mask.getbyte(i % 4))
@@ -130,19 +144,33 @@ module Protocol
130
144
  byte = buffer.unpack("C").first
131
145
 
132
146
  finished = (byte & 0b1000_0000 != 0)
133
- # rsv = byte & 0b0111_0000
147
+ flags = (byte & 0b0111_0000) >> 4
134
148
  opcode = byte & 0b0000_1111
135
149
 
136
- return finished, opcode
150
+ if (0x3 .. 0x7).include?(opcode)
151
+ raise ProtocolError, "non-control opcode = #{opcode} is reserved!"
152
+ elsif (0xB .. 0xF).include?(opcode)
153
+ raise ProtocolError, "control opcode = #{opcode} is reserved!"
154
+ end
155
+
156
+ return finished, flags, opcode
137
157
  end
138
158
 
139
- def self.read(finished, opcode, stream, maximum_frame_size)
159
+ def self.read(finished, flags, opcode, stream, maximum_frame_size)
140
160
  buffer = stream.read(1) or raise EOFError, "Could not read header!"
141
161
  byte = buffer.unpack("C").first
142
162
 
143
163
  mask = (byte & 0b1000_0000 != 0)
144
164
  length = byte & 0b0111_1111
145
165
 
166
+ if opcode & 0x8 != 0
167
+ if length > 125
168
+ raise ProtocolError, "Invalid control frame payload length: #{length} > 125!"
169
+ elsif !finished
170
+ raise ProtocolError, "Fragmented control frame!"
171
+ end
172
+ end
173
+
146
174
  if length == 126
147
175
  buffer = stream.read(2) or raise EOFError, "Could not read length!"
148
176
  length = buffer.unpack('n').first
@@ -165,11 +193,11 @@ module Protocol
165
193
  raise EOFError, "Incorrect payload length: #{@length} != #{@payload.bytesize}!"
166
194
  end
167
195
 
168
- return self.new(finished, payload, opcode: opcode, mask: mask)
196
+ return self.new(finished, payload, flags: flags, opcode: opcode, mask: mask)
169
197
  end
170
198
 
171
199
  def write(stream)
172
- buffer = String.new.b
200
+ buffer = String.new(encoding: Encoding::BINARY)
173
201
 
174
202
  if @payload&.bytesize != @length
175
203
  raise ProtocolError, "Invalid payload length: #{@length} != #{@payload.bytesize} for #{self}!"
@@ -188,7 +216,7 @@ module Protocol
188
216
  end
189
217
 
190
218
  buffer << [
191
- (@finished ? 0b1000_0000 : 0) | @opcode,
219
+ (@finished ? 0b1000_0000 : 0) | (@flags << 4) | @opcode,
192
220
  (@mask ? 0b1000_0000 : 0) | short_length,
193
221
  ].pack('CC')
194
222
 
@@ -29,7 +29,7 @@ require_relative 'pong_frame'
29
29
 
30
30
  module Protocol
31
31
  module WebSocket
32
- # HTTP/2 frame type mapping as defined by the spec
32
+ # HTTP/2 frame type mapping as defined by the spec.
33
33
  FRAMES = {
34
34
  0x0 => ContinuationFrame,
35
35
  0x1 => TextFrame,
@@ -39,8 +39,10 @@ module Protocol
39
39
  0xA => PongFrame,
40
40
  }.freeze
41
41
 
42
+ # The maximum allowed frame size in bytes.
42
43
  MAXIMUM_ALLOWED_FRAME_SIZE = 2**63
43
44
 
45
+ # Wraps an underlying {Async::IO::Stream} for reading and writing binary data into structured frames.
44
46
  class Framer
45
47
  def initialize(stream, frames = FRAMES)
46
48
  @stream = stream
@@ -55,13 +57,15 @@ module Protocol
55
57
  @stream.flush
56
58
  end
57
59
 
60
+ # Read a frame from the underlying stream.
61
+ # @returns [Frame]
58
62
  def read_frame(maximum_frame_size = MAXIMUM_ALLOWED_FRAME_SIZE)
59
63
  # Read the header:
60
- finished, opcode = read_header
64
+ finished, flags, opcode = read_header
61
65
 
62
66
  # Read the frame:
63
67
  klass = @frames[opcode] || Frame
64
- frame = klass.read(finished, opcode, @stream, maximum_frame_size)
68
+ frame = klass.read(finished, flags, opcode, @stream, maximum_frame_size)
65
69
 
66
70
  return frame
67
71
  end
@@ -27,13 +27,17 @@ module Protocol
27
27
  # The protocol string used for the `upgrade:` header (HTTP/1) and `:protocol` pseudo-header (HTTP/2).
28
28
  PROTOCOL = "websocket".freeze
29
29
 
30
- # These general headers are used to negotiate the connection.
30
+ # The WebSocket protocol header, used for application level protocol negotiation.
31
31
  SEC_WEBSOCKET_PROTOCOL = 'sec-websocket-protocol'.freeze
32
+
33
+ # The WebSocket version header. Used for negotiating binary protocol version.
32
34
  SEC_WEBSOCKET_VERSION = 'sec-websocket-version'.freeze
33
35
 
34
36
  SEC_WEBSOCKET_KEY = 'sec-websocket-key'.freeze
35
37
  SEC_WEBSOCKET_ACCEPT = 'sec-websocket-accept'.freeze
36
38
 
39
+ SEC_WEBSOCKET_EXTENSIONS = 'sec-websocket-extensions'.freeze
40
+
37
41
  module Nounce
38
42
  GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
39
43
 
@@ -0,0 +1,61 @@
1
+ # Copyright, 2022, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require_relative 'frame'
22
+
23
+ module Protocol
24
+ module WebSocket
25
+ class Message
26
+ def initialize(buffer)
27
+ @buffer = buffer
28
+ end
29
+
30
+ attr :buffer
31
+
32
+ def size
33
+ @buffer.bytesize
34
+ end
35
+
36
+ def == other
37
+ @buffer == other
38
+ end
39
+
40
+ def to_str
41
+ @buffer
42
+ end
43
+
44
+ def encoding
45
+ @buffer.encoding
46
+ end
47
+ end
48
+
49
+ class TextMessage < Message
50
+ def send(connection, **options)
51
+ connection.send_text(@buffer, **options)
52
+ end
53
+ end
54
+
55
+ class BinaryMessage < Message
56
+ def send(connection, **options)
57
+ connection.send_binary(@buffer, **options)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -26,10 +26,13 @@ module Protocol
26
26
  class PingFrame < Frame
27
27
  OPCODE = 0x9
28
28
 
29
- def reply
30
- PongFrame.new(true, @payload, mask: @mask)
29
+ # Generate a suitable reply.
30
+ # @returns [PongFrame]
31
+ def reply(**options)
32
+ PongFrame.new(true, self.unpack, **options)
31
33
  end
32
34
 
35
+ # Apply this frame to the specified connection.
33
36
  def apply(connection)
34
37
  connection.receive_ping(self)
35
38
  end
@@ -25,6 +25,7 @@ module Protocol
25
25
  class PongFrame < Frame
26
26
  OPCODE = 0xA
27
27
 
28
+ # Apply this frame to the specified connection.
28
29
  def apply(connection)
29
30
  connection.receive_pong(self)
30
31
  end
@@ -19,9 +19,11 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require_relative 'frame'
22
+ require_relative 'message'
22
23
 
23
24
  module Protocol
24
25
  module WebSocket
26
+ # Implements the text frame for sending and receiving text.
25
27
  class TextFrame < Frame
26
28
  OPCODE = 0x1
27
29
 
@@ -29,14 +31,19 @@ module Protocol
29
31
  true
30
32
  end
31
33
 
32
- def unpack
33
- super.force_encoding(Encoding::UTF_8)
34
- end
35
-
36
- def pack(data)
37
- super(data.b)
34
+ # Decode the binary buffer into a suitable text message.
35
+ # @parameter buffer [String] The binary data to unpack.
36
+ def read_message(buffer)
37
+ buffer.force_encoding(Encoding::UTF_8)
38
+
39
+ unless buffer.valid_encoding?
40
+ raise ProtocolError, "invalid UTF-8 in text frame!"
41
+ end
42
+
43
+ return TextMessage.new(buffer)
38
44
  end
39
45
 
46
+ # Apply this frame to the specified connection.
40
47
  def apply(connection)
41
48
  connection.receive_text(self)
42
49
  end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Protocol
22
22
  module WebSocket
23
- VERSION = "0.7.4"
23
+ VERSION = "0.9.0"
24
24
  end
25
25
  end
@@ -20,3 +20,4 @@
20
20
 
21
21
  require_relative 'websocket/version'
22
22
  require_relative 'websocket/framer'
23
+ require_relative 'websocket/connection'
data.tar.gz.sig ADDED
Binary file
metadata CHANGED
@@ -1,14 +1,47 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protocol-websocket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
- autorequire:
8
+ - Aurora
9
+ - Soumya
10
+ - Olle Jonsson
11
+ - William T. Nelson
12
+ autorequire:
9
13
  bindir: bin
10
- cert_chain: []
11
- date: 2019-12-15 00:00:00.000000000 Z
14
+ cert_chain:
15
+ - |
16
+ -----BEGIN CERTIFICATE-----
17
+ MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
18
+ ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
19
+ CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
20
+ MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
21
+ MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
22
+ bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
23
+ igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
24
+ 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
25
+ sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
26
+ e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
27
+ XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
28
+ RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
29
+ tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
30
+ zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
31
+ xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
32
+ BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
33
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
34
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
35
+ cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
36
+ xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
37
+ c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
38
+ 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
39
+ JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
40
+ eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
41
+ Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
42
+ voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
43
+ -----END CERTIFICATE-----
44
+ date: 2022-08-22 00:00:00.000000000 Z
12
45
  dependencies:
13
46
  - !ruby/object:Gem::Dependency
14
47
  name: protocol-http
@@ -39,7 +72,7 @@ dependencies:
39
72
  - !ruby/object:Gem::Version
40
73
  version: '0.2'
41
74
  - !ruby/object:Gem::Dependency
42
- name: covered
75
+ name: bundler
43
76
  requirement: !ruby/object:Gem::Requirement
44
77
  requirements:
45
78
  - - ">="
@@ -53,7 +86,7 @@ dependencies:
53
86
  - !ruby/object:Gem::Version
54
87
  version: '0'
55
88
  - !ruby/object:Gem::Dependency
56
- name: bundler
89
+ name: covered
57
90
  requirement: !ruby/object:Gem::Requirement
58
91
  requirements:
59
92
  - - ">="
@@ -67,65 +100,52 @@ dependencies:
67
100
  - !ruby/object:Gem::Version
68
101
  version: '0'
69
102
  - !ruby/object:Gem::Dependency
70
- name: rake
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '10.0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '10.0'
83
- - !ruby/object:Gem::Dependency
84
- name: rspec
103
+ name: sus
85
104
  requirement: !ruby/object:Gem::Requirement
86
105
  requirements:
87
106
  - - "~>"
88
107
  - !ruby/object:Gem::Version
89
- version: '3.0'
108
+ version: 0.9.1
90
109
  type: :development
91
110
  prerelease: false
92
111
  version_requirements: !ruby/object:Gem::Requirement
93
112
  requirements:
94
113
  - - "~>"
95
114
  - !ruby/object:Gem::Version
96
- version: '3.0'
97
- description:
115
+ version: 0.9.1
116
+ description:
98
117
  email:
99
- - samuel.williams@oriontransfer.co.nz
100
118
  executables: []
101
119
  extensions: []
102
120
  extra_rdoc_files: []
103
121
  files:
104
- - ".gitignore"
105
- - ".rspec"
106
- - ".travis.yml"
107
- - Gemfile
108
- - README.md
109
- - Rakefile
122
+ - lib/.DS_Store
123
+ - lib/protocol/.DS_Store
110
124
  - lib/protocol/websocket.rb
111
125
  - lib/protocol/websocket/binary_frame.rb
112
126
  - lib/protocol/websocket/close_frame.rb
113
127
  - lib/protocol/websocket/connection.rb
114
128
  - lib/protocol/websocket/continuation_frame.rb
115
129
  - lib/protocol/websocket/error.rb
130
+ - lib/protocol/websocket/extension/compression.rb
131
+ - lib/protocol/websocket/extension/compression/constants.rb
132
+ - lib/protocol/websocket/extension/compression/deflate.rb
133
+ - lib/protocol/websocket/extension/compression/inflate.rb
134
+ - lib/protocol/websocket/extensions.md
135
+ - lib/protocol/websocket/extensions.rb
116
136
  - lib/protocol/websocket/frame.rb
117
137
  - lib/protocol/websocket/framer.rb
118
138
  - lib/protocol/websocket/headers.rb
139
+ - lib/protocol/websocket/message.rb
119
140
  - lib/protocol/websocket/ping_frame.rb
120
141
  - lib/protocol/websocket/pong_frame.rb
121
142
  - lib/protocol/websocket/text_frame.rb
122
143
  - lib/protocol/websocket/version.rb
123
- - protocol-websocket.gemspec
124
144
  homepage: https://github.com/socketry/protocol-websocket
125
145
  licenses:
126
146
  - MIT
127
147
  metadata: {}
128
- post_install_message:
148
+ post_install_message:
129
149
  rdoc_options: []
130
150
  require_paths:
131
151
  - lib
@@ -133,15 +153,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
153
  requirements:
134
154
  - - ">="
135
155
  - !ruby/object:Gem::Version
136
- version: '0'
156
+ version: 2.5.0
137
157
  required_rubygems_version: !ruby/object:Gem::Requirement
138
158
  requirements:
139
159
  - - ">="
140
160
  - !ruby/object:Gem::Version
141
161
  version: '0'
142
162
  requirements: []
143
- rubygems_version: 3.0.6
144
- signing_key:
163
+ rubygems_version: 3.3.7
164
+ signing_key:
145
165
  specification_version: 4
146
166
  summary: A low level implementation of the WebSocket protocol.
147
167
  test_files: []
metadata.gz.sig ADDED
Binary file
data/.gitignore DELETED
@@ -1,13 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- # rspec failure tracking
11
- .rspec_status
12
- Gemfile.lock
13
- .covered.db
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --warnings
3
- --require spec_helper