plum 0.0.1
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 +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/Guardfile +7 -0
- data/LICENSE +21 -0
- data/README.md +14 -0
- data/Rakefile +12 -0
- data/bin/.gitkeep +0 -0
- data/examples/local_server.rb +206 -0
- data/examples/static_server.rb +157 -0
- data/lib/plum.rb +21 -0
- data/lib/plum/binary_string.rb +74 -0
- data/lib/plum/connection.rb +201 -0
- data/lib/plum/connection_utils.rb +38 -0
- data/lib/plum/errors.rb +35 -0
- data/lib/plum/event_emitter.rb +19 -0
- data/lib/plum/flow_control.rb +97 -0
- data/lib/plum/frame.rb +163 -0
- data/lib/plum/frame_factory.rb +53 -0
- data/lib/plum/frame_utils.rb +50 -0
- data/lib/plum/hpack/constants.rb +331 -0
- data/lib/plum/hpack/context.rb +55 -0
- data/lib/plum/hpack/decoder.rb +145 -0
- data/lib/plum/hpack/encoder.rb +105 -0
- data/lib/plum/hpack/huffman.rb +42 -0
- data/lib/plum/http_connection.rb +33 -0
- data/lib/plum/https_connection.rb +24 -0
- data/lib/plum/stream.rb +217 -0
- data/lib/plum/stream_utils.rb +58 -0
- data/lib/plum/version.rb +3 -0
- data/plum.gemspec +29 -0
- data/test/plum/connection/test_handle_frame.rb +70 -0
- data/test/plum/hpack/test_context.rb +63 -0
- data/test/plum/hpack/test_decoder.rb +291 -0
- data/test/plum/hpack/test_encoder.rb +49 -0
- data/test/plum/hpack/test_huffman.rb +36 -0
- data/test/plum/stream/test_handle_frame.rb +262 -0
- data/test/plum/test_binary_string.rb +64 -0
- data/test/plum/test_connection.rb +96 -0
- data/test/plum/test_connection_utils.rb +29 -0
- data/test/plum/test_error.rb +13 -0
- data/test/plum/test_flow_control.rb +167 -0
- data/test/plum/test_frame.rb +59 -0
- data/test/plum/test_frame_factory.rb +56 -0
- data/test/plum/test_frame_utils.rb +46 -0
- data/test/plum/test_https_connection.rb +37 -0
- data/test/plum/test_stream.rb +32 -0
- data/test/plum/test_stream_utils.rb +16 -0
- data/test/server.crt +19 -0
- data/test/server.csr +16 -0
- data/test/server.key +27 -0
- data/test/test_helper.rb +28 -0
- data/test/utils/assertions.rb +60 -0
- data/test/utils/server.rb +63 -0
- metadata +234 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class HPACKEncoderTest < Minitest::Test
|
4
|
+
# C.1.1
|
5
|
+
def test_hpack_encode_integer_small
|
6
|
+
result = new_encoder.__send__(:encode_integer, 10, 5)
|
7
|
+
assert_equal([0b00001010].pack("C*"), result)
|
8
|
+
end
|
9
|
+
|
10
|
+
# C.1.2
|
11
|
+
def test_hpack_encode_integer_big
|
12
|
+
result = new_encoder.__send__(:encode_integer, 1337, 5)
|
13
|
+
assert_equal([0b00011111, 0b10011010, 0b00001010].pack("C*"), result)
|
14
|
+
end
|
15
|
+
|
16
|
+
# C.1.3
|
17
|
+
def test_hpack_encode_integer_8prefix
|
18
|
+
result = new_encoder.__send__(:encode_integer, 42, 8)
|
19
|
+
assert_equal([0b00101010].pack("C*"), result)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_hpack_encode_single
|
23
|
+
headers = [["custom-key", "custom-header"]]
|
24
|
+
encoded = new_encoder.encode(headers)
|
25
|
+
decoded = new_decoder.decode(encoded)
|
26
|
+
assert_equal(headers, decoded)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_hpack_encode_multiple
|
30
|
+
headers = [
|
31
|
+
[":method", "GET"],
|
32
|
+
[":scheme", "http"],
|
33
|
+
[":path", "/"],
|
34
|
+
[":authority", "www.example.com"]
|
35
|
+
]
|
36
|
+
encoded = new_encoder.encode(headers)
|
37
|
+
decoded = new_decoder.decode(encoded)
|
38
|
+
assert_equal(headers, decoded)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def new_decoder(settings_header_table_size = 1 << 31)
|
43
|
+
Plum::HPACK::Decoder.new(settings_header_table_size)
|
44
|
+
end
|
45
|
+
|
46
|
+
def new_encoder(settings_header_table_size = 1 << 31)
|
47
|
+
Plum::HPACK::Encoder.new(settings_header_table_size)
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class HPACKHuffmanTest < Minitest::Test
|
4
|
+
def test_hpack_huffman_encode
|
5
|
+
text = "\x10\x60\x2a\x1d\x94\x47\x82\x2c\x3d\x19\xbf\x8e\xd9\x24\xba\xe6\xb4\x1a\xe1\x5c\x39\x0f\x61\xf4\x3b\x08\x62\x54\x15\x0c"
|
6
|
+
expected = "\xff\xff\xfe\xdf\xff\xbf\x3f\xff\xff\xf3\xff\xff\xdd\x8b\xff\xf9\xfe\xa0\xff\xff\xff\x5f\xff\xfe\x3f\xff\xfd\x7f\xff\xe5\xff\xcf\xff\xfc\x7f\xff\xe8\xff\xff\xdb\xff\xff\xfe\xdf\xff\xfe\x7f\xff\xc1\xff\xff\xff\xec\x1f\xff\xff\xe7\xfb\xff\xff\xfe\x88\xf7\xff\xff\xff\x97\xff\xff\xf5\x7f"
|
7
|
+
assert_equal(expected.force_encoding(Encoding::BINARY), Plum::HPACK::Huffman.encode(text).force_encoding(Encoding::BINARY))
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_hpack_huffman_decode
|
11
|
+
encoded = "\xff\xff\xfe\xdf\xff\xbf\x3f\xff\xff\xf3\xff\xff\xdd\x8b\xff\xf9\xfe\xa0\xff\xff\xff\x5f\xff\xfe\x3f\xff\xfd\x7f\xff\xe5\xff\xcf\xff\xfc\x7f\xff\xe8\xff\xff\xdb\xff\xff\xfe\xdf\xff\xfe\x7f\xff\xc1\xff\xff\xff\xec\x1f\xff\xff\xe7\xfb\xff\xff\xfe\x88\xf7\xff\xff\xff\x97\xff\xff\xf5\x7f"
|
12
|
+
expected = "\x10\x60\x2a\x1d\x94\x47\x82\x2c\x3d\x19\xbf\x8e\xd9\x24\xba\xe6\xb4\x1a\xe1\x5c\x39\x0f\x61\xf4\x3b\x08\x62\x54\x15\x0c"
|
13
|
+
assert_equal(expected.force_encoding(Encoding::BINARY), Plum::HPACK::Huffman.decode(encoded).force_encoding(Encoding::BINARY))
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_too_long_padding
|
17
|
+
encoded = "\x1c\x64\xff\xff\xff"
|
18
|
+
assert_raises(Plum::HPACKError) {
|
19
|
+
Plum::HPACK::Huffman.decode(encoded)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_padding_not_filled
|
24
|
+
encoded = "\x1c\x71\xfe"
|
25
|
+
assert_raises(Plum::HPACKError) {
|
26
|
+
Plum::HPACK::Huffman.decode(encoded)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_eos_in_encoded
|
31
|
+
encoded = "\xff\xff\xff\xff" # \xff\xff\xff\xfc + padding
|
32
|
+
assert_raises(Plum::HPACKError) {
|
33
|
+
Plum::HPACK::Huffman.decode(encoded)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,262 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
using Plum::BinaryString
|
4
|
+
|
5
|
+
class StreamHandleFrameTest < Minitest::Test
|
6
|
+
## DATA
|
7
|
+
def test_stream_handle_data
|
8
|
+
payload = "ABC" * 5
|
9
|
+
open_new_stream(state: :open) {|stream|
|
10
|
+
data = nil
|
11
|
+
stream.on(:data) {|_data| data = _data }
|
12
|
+
stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
|
13
|
+
flags: [], payload: payload))
|
14
|
+
assert_equal(payload, data)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_stream_handle_data_padded
|
19
|
+
payload = "ABC" * 5
|
20
|
+
open_new_stream(state: :open) {|stream|
|
21
|
+
data = nil
|
22
|
+
stream.on(:data) {|_data| data = _data }
|
23
|
+
stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
|
24
|
+
flags: [:padded], payload: "".push_uint8(6).push(payload).push("\x00"*6)))
|
25
|
+
assert_equal(payload, data)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_stream_handle_data_too_long_padding
|
30
|
+
payload = "ABC" * 5
|
31
|
+
open_new_stream(state: :open) {|stream|
|
32
|
+
assert_connection_error(:protocol_error) {
|
33
|
+
stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
|
34
|
+
flags: [:padded], payload: "".push_uint8(100).push(payload).push("\x00"*6)))
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_stream_handle_data_end_stream
|
40
|
+
payload = "ABC" * 5
|
41
|
+
open_new_stream(state: :open) {|stream|
|
42
|
+
stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
|
43
|
+
flags: [:end_stream], payload: payload))
|
44
|
+
assert_equal(:half_closed_remote, stream.state)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_stream_handle_data_invalid_state
|
49
|
+
payload = "ABC" * 5
|
50
|
+
open_new_stream(state: :half_closed_remote) {|stream|
|
51
|
+
assert_stream_error(:stream_closed) {
|
52
|
+
stream.receive_frame(Frame.new(type: :data, stream_id: stream.id,
|
53
|
+
flags: [:end_stream], payload: payload))
|
54
|
+
}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
## HEADERS
|
59
|
+
def test_stream_handle_headers_single
|
60
|
+
open_new_stream {|stream|
|
61
|
+
headers = nil
|
62
|
+
stream.on(:headers) {|_headers|
|
63
|
+
headers = _headers
|
64
|
+
}
|
65
|
+
stream.receive_frame(Frame.new(type: :headers,
|
66
|
+
stream_id: stream.id,
|
67
|
+
flags: [:end_headers],
|
68
|
+
payload: HPACK::Encoder.new(0).encode([[":path", "/"]])))
|
69
|
+
assert_equal(:open, stream.state)
|
70
|
+
assert_equal([[":path", "/"]], headers)
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_stream_handle_headers_continuation
|
75
|
+
open_new_stream {|stream|
|
76
|
+
payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
|
77
|
+
headers = nil
|
78
|
+
stream.on(:headers) {|_headers|
|
79
|
+
headers = _headers
|
80
|
+
}
|
81
|
+
stream.receive_frame(Frame.new(type: :headers,
|
82
|
+
stream_id: stream.id,
|
83
|
+
flags: [:end_stream],
|
84
|
+
payload: payload[0..4]))
|
85
|
+
assert_equal(nil, headers) # wait CONTINUATION
|
86
|
+
stream.receive_frame(Frame.new(type: :continuation,
|
87
|
+
stream_id: stream.id,
|
88
|
+
flags: [:end_headers],
|
89
|
+
payload: payload[5..-1]))
|
90
|
+
assert_equal(:half_closed_remote, stream.state)
|
91
|
+
assert_equal([[":path", "/"]], headers)
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_stream_handle_headers_padded
|
96
|
+
open_new_stream {|stream|
|
97
|
+
payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
|
98
|
+
headers = nil
|
99
|
+
stream.on(:headers) {|_headers|
|
100
|
+
headers = _headers
|
101
|
+
}
|
102
|
+
stream.receive_frame(Frame.new(type: :headers,
|
103
|
+
stream_id: stream.id,
|
104
|
+
flags: [:end_headers, :padded],
|
105
|
+
payload: "".push_uint8(payload.bytesize).push(payload).push("\x00"*payload.bytesize)))
|
106
|
+
assert_equal([[":path", "/"]], headers)
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_stream_handle_headers_too_long_padding
|
111
|
+
open_new_stream {|stream|
|
112
|
+
payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
|
113
|
+
assert_connection_error(:protocol_error) {
|
114
|
+
stream.receive_frame(Frame.new(type: :headers,
|
115
|
+
stream_id: stream.id,
|
116
|
+
flags: [:end_headers, :padded],
|
117
|
+
payload: "".push_uint8(payload.bytesize+1).push(payload).push("\x00"*(payload.bytesize+1))))
|
118
|
+
}
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_stream_handle_headers_broken
|
123
|
+
open_new_stream {|stream|
|
124
|
+
payload = "\x00\x01\x02"
|
125
|
+
assert_connection_error(:compression_error) {
|
126
|
+
stream.receive_frame(Frame.new(type: :headers,
|
127
|
+
stream_id: stream.id,
|
128
|
+
flags: [:end_headers],
|
129
|
+
payload: payload))
|
130
|
+
}
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_stream_handle_headers_state
|
135
|
+
_payload = HPACK::Encoder.new(0).encode([[":path", "/"]])
|
136
|
+
open_new_stream(state: :reserved_local) {|stream|
|
137
|
+
assert_connection_error(:protocol_error) {
|
138
|
+
stream.receive_frame(Frame.new(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
|
139
|
+
}
|
140
|
+
}
|
141
|
+
open_new_stream(state: :closed) {|stream|
|
142
|
+
assert_connection_error(:stream_closed) {
|
143
|
+
stream.receive_frame(Frame.new(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
|
144
|
+
}
|
145
|
+
}
|
146
|
+
open_new_stream(state: :half_closed_remote) {|stream|
|
147
|
+
assert_stream_error(:stream_closed) {
|
148
|
+
stream.receive_frame(Frame.new(type: :headers, stream_id: stream.id, flags: [:end_headers, :end_stream], payload: _payload))
|
149
|
+
}
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_stream_handle_headers_priority
|
154
|
+
open_server_connection {|con|
|
155
|
+
parent = open_new_stream(con)
|
156
|
+
stream = open_new_stream(con)
|
157
|
+
|
158
|
+
headers = nil
|
159
|
+
stream.on(:headers) {|_headers| headers = _headers }
|
160
|
+
header_block = HPACK::Encoder.new(0).encode([[":path", "/"]])
|
161
|
+
payload = "".push_uint32((1 << 31) | parent.id)
|
162
|
+
.push_uint8(50)
|
163
|
+
.push(header_block)
|
164
|
+
stream.receive_frame(Frame.new(type: :headers,
|
165
|
+
stream_id: stream.id,
|
166
|
+
flags: [:end_headers, :priority],
|
167
|
+
payload: payload))
|
168
|
+
assert_equal(true, stream.exclusive)
|
169
|
+
assert_equal(parent, stream.parent)
|
170
|
+
assert_equal(50, stream.weight)
|
171
|
+
assert_equal([[":path", "/"]], headers)
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
## PRIORITY
|
176
|
+
def test_stream_handle_priority
|
177
|
+
open_server_connection {|con|
|
178
|
+
parent = open_new_stream(con)
|
179
|
+
stream = open_new_stream(con)
|
180
|
+
|
181
|
+
payload = "".push_uint32((1 << 31) | parent.id)
|
182
|
+
.push_uint8(50)
|
183
|
+
stream.receive_frame(Frame.new(type: :priority,
|
184
|
+
stream_id: stream.id,
|
185
|
+
payload: payload))
|
186
|
+
assert_equal(true, stream.exclusive)
|
187
|
+
assert_equal(parent, stream.parent)
|
188
|
+
assert_equal(50, stream.weight)
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
def test_stream_handle_priority_self_depend
|
193
|
+
open_server_connection {|con|
|
194
|
+
stream = open_new_stream(con)
|
195
|
+
payload = "".push_uint32((1 << 31) | stream.id).push_uint8(6)
|
196
|
+
stream.receive_frame(Frame.new(type: :priority,
|
197
|
+
stream_id: stream.id,
|
198
|
+
payload: payload))
|
199
|
+
last = sent_frames.last
|
200
|
+
assert_equal(:rst_stream, last.type)
|
201
|
+
assert_equal(HTTPError::ERROR_CODES[:protocol_error], last.payload.uint32)
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_stream_handle_priority_exclusive
|
206
|
+
open_server_connection {|con|
|
207
|
+
parent = open_new_stream(con)
|
208
|
+
stream0 = open_new_stream(con, parent: parent)
|
209
|
+
stream1 = open_new_stream(con, parent: parent)
|
210
|
+
stream2 = open_new_stream(con, parent: parent)
|
211
|
+
|
212
|
+
payload = "".push_uint32((1 << 31) | parent.id).push_uint8(6)
|
213
|
+
stream0.receive_frame(Frame.new(type: :priority,
|
214
|
+
stream_id: stream0.id,
|
215
|
+
payload: payload))
|
216
|
+
assert_equal(parent, stream0.parent)
|
217
|
+
assert_equal(stream0, stream1.parent)
|
218
|
+
assert_equal(stream0, stream2.parent)
|
219
|
+
}
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_stream_handle_frame_size_error
|
223
|
+
open_new_stream {|stream|
|
224
|
+
assert_stream_error(:frame_size_error) {
|
225
|
+
stream.receive_frame(Frame.new(type: :priority,
|
226
|
+
stream_id: stream.id,
|
227
|
+
payload: "\x00"))
|
228
|
+
}
|
229
|
+
}
|
230
|
+
end
|
231
|
+
|
232
|
+
## RST_STREAM
|
233
|
+
def test_stream_handle_rst_stream
|
234
|
+
open_new_stream(state: :reserved_local) {|stream|
|
235
|
+
stream.receive_frame(Frame.new(type: :rst_stream,
|
236
|
+
stream_id: stream.id,
|
237
|
+
payload: "\x00\x00\x00\x00"))
|
238
|
+
assert_equal(:closed, stream.state)
|
239
|
+
}
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_stream_handle_rst_stream_idle
|
243
|
+
open_new_stream(state: :idle) {|stream|
|
244
|
+
assert_connection_error(:protocol_error) {
|
245
|
+
stream.receive_frame(Frame.new(type: :rst_stream,
|
246
|
+
stream_id: stream.id,
|
247
|
+
payload: "\x00\x00\x00\x00"))
|
248
|
+
}
|
249
|
+
}
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_stream_handle_rst_stream_frame_size
|
253
|
+
open_new_stream(state: :reserved_local) {|stream|
|
254
|
+
assert_connection_error(:frame_size_error) {
|
255
|
+
stream.receive_frame(Frame.new(type: :rst_stream,
|
256
|
+
stream_id: stream.id,
|
257
|
+
payload: "\x00\x00\x00"))
|
258
|
+
}
|
259
|
+
}
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
using BinaryString
|
4
|
+
|
5
|
+
class BinaryStringTest < Minitest::Test
|
6
|
+
def test_uint8
|
7
|
+
assert_equal(0x67, "\x67".uint8)
|
8
|
+
assert_equal(0x75, "\x67\x75".uint8(1))
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_uint16
|
12
|
+
assert_equal(0x78ff, "\x78\xff".uint16)
|
13
|
+
assert_equal(0xee55, "\x78\xee\x55".uint16(1))
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_uint24
|
17
|
+
assert_equal(0x005554, "\x00\x55\x54".uint24)
|
18
|
+
assert_equal(0x005554, "\x2f\xaa\x00\x55\x54".uint24(2))
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_uint32
|
22
|
+
assert_equal(0x00555400, "\x00\x55\x54\x00".uint32)
|
23
|
+
assert_equal(0x00555400, "\x2f\xaa\x00\x55\x54\x00".uint32(2))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_push_uint8
|
27
|
+
assert_equal("\x24", "".push_uint8(0x24))
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_push_uint16
|
31
|
+
assert_equal("\x24\x11", "".push_uint16(0x2411))
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_push_uint24
|
35
|
+
assert_equal("\x11\x11\x24", "".push_uint24(0x111124))
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_push_uint32
|
39
|
+
assert_equal("\x10\x00\x00\x24", "".push_uint32(0x10000024))
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_push
|
43
|
+
assert_equal("adh", "ad".push("h"))
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_byteshift
|
47
|
+
sushi = "\u{1f363}".encode(Encoding::UTF_8)
|
48
|
+
assert_equal("\xf0".b, sushi.byteshift(1).b)
|
49
|
+
assert_equal("\x9f\x8d\xa3".b, sushi.b)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_each_byteslice_block
|
53
|
+
ret = []
|
54
|
+
string = "12345678"
|
55
|
+
string.each_byteslice(3) {|part| ret << part }
|
56
|
+
assert_equal(["123", "456", "78"], ret)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_each_byteslice_enume
|
60
|
+
string = "12345678"
|
61
|
+
ret = string.each_byteslice(3)
|
62
|
+
assert_equal(["123", "456", "78"], ret.to_a)
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
using Plum::BinaryString
|
4
|
+
|
5
|
+
class ConnectionTest < Minitest::Test
|
6
|
+
def test_server_must_raise_frame_size_error_when_exeeeded_max_size
|
7
|
+
_settings = "".push_uint16(Frame::SETTINGS_TYPE[:max_frame_size]).push_uint32(2**14)
|
8
|
+
limit = 2 ** 14
|
9
|
+
|
10
|
+
new_con = -> (&blk) {
|
11
|
+
c = open_server_connection
|
12
|
+
c.settings(max_frame_size: limit)
|
13
|
+
blk.call c
|
14
|
+
}
|
15
|
+
|
16
|
+
new_con.call {|con|
|
17
|
+
assert_no_error {
|
18
|
+
con << Frame.new(type: :settings, stream_id: 0, payload: _settings * (limit / 6)).assemble
|
19
|
+
}
|
20
|
+
}
|
21
|
+
new_con.call {|con|
|
22
|
+
assert_connection_error(:frame_size_error) {
|
23
|
+
con << Frame.new(type: :settings, stream_id: 0, payload: _settings * (limit / 6 + 1)).assemble
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
new_con.call {|con|
|
28
|
+
assert_connection_error(:frame_size_error) {
|
29
|
+
con << Frame.new(type: :headers, stream_id: 3, payload: "\x00" * (limit + 1)).assemble
|
30
|
+
}
|
31
|
+
}
|
32
|
+
new_con.call {|con|
|
33
|
+
assert_stream_error(:frame_size_error) {
|
34
|
+
con << Frame.new(type: :headers, stream_id: 3, flags: [:end_headers], payload: "").assemble
|
35
|
+
con << Frame.new(type: :data, stream_id: 3, payload: "\x00" * (limit + 1)).assemble
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_server_raise_cprotocol_error_illegal_control_stream
|
41
|
+
[:data, :headers, :priority, :rst_stream, :push_promise, :continuation].each do |type|
|
42
|
+
con = open_server_connection
|
43
|
+
assert_connection_error(:protocol_error) {
|
44
|
+
con << Frame.new(type: type, stream_id: 0).assemble
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_server_ignore_unknown_frame_type
|
50
|
+
open_server_connection {|con|
|
51
|
+
assert_no_error {
|
52
|
+
con << "\x00\x00\x00\x0f\x00\x00\x00\x00\x00" # type: 0x0f, no flags, no payload, stream 0
|
53
|
+
}
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_server_raise_cprotocol_error_client_start_even_stream_id
|
58
|
+
con = open_server_connection
|
59
|
+
assert_connection_error(:protocol_error) {
|
60
|
+
con << Frame.new(type: :headers, flags: [:end_headers], stream_id: 2).assemble
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_server_raise_cprotocol_error_client_start_small_stream_id
|
65
|
+
con = open_server_connection
|
66
|
+
con << Frame.new(type: :headers, flags: [:end_headers], stream_id: 51).assemble
|
67
|
+
assert_connection_error(:protocol_error) {
|
68
|
+
con << Frame.new(type: :headers, flags: [:end_headers], stream_id: 31).assemble
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_server_raise_cprotocol_error_invalid_continuation_state
|
73
|
+
prepare = -> &blk {
|
74
|
+
con = open_server_connection
|
75
|
+
con << Frame.new(type: :headers, flags: [:end_headers], stream_id: 1).assemble
|
76
|
+
con << Frame.new(type: :headers, flags: [:end_stream], stream_id: 3).assemble
|
77
|
+
blk.call(con)
|
78
|
+
}
|
79
|
+
|
80
|
+
prepare.call {|con|
|
81
|
+
assert_connection_error(:protocol_error) {
|
82
|
+
con << Frame.new(type: :data, stream_id: 1, payload: "hello").assemble
|
83
|
+
}
|
84
|
+
}
|
85
|
+
prepare.call {|con|
|
86
|
+
assert_connection_error(:protocol_error) {
|
87
|
+
con << Frame.new(type: :data, stream_id: 3, payload: "hello").assemble
|
88
|
+
}
|
89
|
+
}
|
90
|
+
prepare.call {|con|
|
91
|
+
assert_equal(:waiting_continuation, con.state)
|
92
|
+
con << Frame.new(type: :continuation, flags: [:end_headers], stream_id: 3, payload: "hello").assemble
|
93
|
+
assert_equal(:open, con.state)
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|