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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.travis.yml +14 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +7 -0
  6. data/LICENSE +21 -0
  7. data/README.md +14 -0
  8. data/Rakefile +12 -0
  9. data/bin/.gitkeep +0 -0
  10. data/examples/local_server.rb +206 -0
  11. data/examples/static_server.rb +157 -0
  12. data/lib/plum.rb +21 -0
  13. data/lib/plum/binary_string.rb +74 -0
  14. data/lib/plum/connection.rb +201 -0
  15. data/lib/plum/connection_utils.rb +38 -0
  16. data/lib/plum/errors.rb +35 -0
  17. data/lib/plum/event_emitter.rb +19 -0
  18. data/lib/plum/flow_control.rb +97 -0
  19. data/lib/plum/frame.rb +163 -0
  20. data/lib/plum/frame_factory.rb +53 -0
  21. data/lib/plum/frame_utils.rb +50 -0
  22. data/lib/plum/hpack/constants.rb +331 -0
  23. data/lib/plum/hpack/context.rb +55 -0
  24. data/lib/plum/hpack/decoder.rb +145 -0
  25. data/lib/plum/hpack/encoder.rb +105 -0
  26. data/lib/plum/hpack/huffman.rb +42 -0
  27. data/lib/plum/http_connection.rb +33 -0
  28. data/lib/plum/https_connection.rb +24 -0
  29. data/lib/plum/stream.rb +217 -0
  30. data/lib/plum/stream_utils.rb +58 -0
  31. data/lib/plum/version.rb +3 -0
  32. data/plum.gemspec +29 -0
  33. data/test/plum/connection/test_handle_frame.rb +70 -0
  34. data/test/plum/hpack/test_context.rb +63 -0
  35. data/test/plum/hpack/test_decoder.rb +291 -0
  36. data/test/plum/hpack/test_encoder.rb +49 -0
  37. data/test/plum/hpack/test_huffman.rb +36 -0
  38. data/test/plum/stream/test_handle_frame.rb +262 -0
  39. data/test/plum/test_binary_string.rb +64 -0
  40. data/test/plum/test_connection.rb +96 -0
  41. data/test/plum/test_connection_utils.rb +29 -0
  42. data/test/plum/test_error.rb +13 -0
  43. data/test/plum/test_flow_control.rb +167 -0
  44. data/test/plum/test_frame.rb +59 -0
  45. data/test/plum/test_frame_factory.rb +56 -0
  46. data/test/plum/test_frame_utils.rb +46 -0
  47. data/test/plum/test_https_connection.rb +37 -0
  48. data/test/plum/test_stream.rb +32 -0
  49. data/test/plum/test_stream_utils.rb +16 -0
  50. data/test/server.crt +19 -0
  51. data/test/server.csr +16 -0
  52. data/test/server.key +27 -0
  53. data/test/test_helper.rb +28 -0
  54. data/test/utils/assertions.rb +60 -0
  55. data/test/utils/server.rb +63 -0
  56. 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