protocol-websocket 0.7.4 → 0.9.0

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