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