http-2 0.6.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.
@@ -0,0 +1,325 @@
1
+ require "helper"
2
+
3
+ describe HTTP2::Framer do
4
+
5
+ let(:f) { Framer.new }
6
+
7
+ context "common header" do
8
+ let(:frame) {
9
+ {
10
+ length: 4,
11
+ type: :headers,
12
+ flags: [:end_stream, :reserved, :end_headers],
13
+ stream: 15,
14
+ }
15
+ }
16
+
17
+ let(:bytes) { [0x04, 0x01, 0x7, 0x0000000F].pack("SCCL") }
18
+
19
+ it "should generate common 8 byte header" do
20
+ f.commonHeader(frame).should eq bytes
21
+ end
22
+
23
+ it "should parse common 8 byte header" do
24
+ f.readCommonHeader(Buffer.new(bytes)).should eq frame
25
+ end
26
+
27
+ it "should raise exception on invalid frame type" do
28
+ expect {
29
+ frame[:type] = :bogus
30
+ f.commonHeader(frame)
31
+ }.to raise_error(CompressionError, /invalid.*type/i)
32
+ end
33
+
34
+ it "should raise exception on invalid stream ID" do
35
+ expect {
36
+ frame[:stream] = Framer::MAX_STREAM_ID + 1
37
+ f.commonHeader(frame)
38
+ }.to raise_error(CompressionError, /stream/i)
39
+ end
40
+
41
+ it "should raise exception on invalid frame flag" do
42
+ expect {
43
+ frame[:flags] = [:bogus]
44
+ f.commonHeader(frame)
45
+ }.to raise_error(CompressionError, /frame flag/)
46
+ end
47
+
48
+ it "should raise exception on invalid frame size" do
49
+ expect {
50
+ frame[:length] = 2**16
51
+ f.commonHeader(frame)
52
+ }.to raise_error(CompressionError, /too large/)
53
+ end
54
+ end
55
+
56
+ context "DATA" do
57
+ it "should generate and parse bytes" do
58
+ frame = {
59
+ length: 4,
60
+ type: :data,
61
+ flags: [:end_stream, :reserved],
62
+ stream: 1,
63
+ payload: 'text'
64
+ }
65
+
66
+ bytes = f.generate(frame)
67
+ bytes.should eq [0x4,0x0,0x3,0x1,*'text'.bytes].pack("SCCLC*")
68
+
69
+ f.parse(Buffer.new(bytes)).should eq frame
70
+ end
71
+ end
72
+
73
+ context "HEADERS" do
74
+ it "should generate and parse bytes" do
75
+ frame = {
76
+ length: 12,
77
+ type: :headers,
78
+ flags: [:end_stream, :reserved, :end_headers],
79
+ stream: 1,
80
+ payload: 'header-block'
81
+ }
82
+
83
+ bytes = f.generate(frame)
84
+ bytes.should eq [0xc,0x1,0x7,0x1,*'header-block'.bytes].pack("SCCLC*")
85
+ f.parse(Buffer.new(bytes)).should eq frame
86
+ end
87
+
88
+ it "should carry an optional stream priority" do
89
+ frame = {
90
+ length: 16,
91
+ type: :headers,
92
+ flags: [:end_headers, :priority],
93
+ stream: 1,
94
+ priority: 15,
95
+ payload: 'header-block'
96
+ }
97
+
98
+ bytes = f.generate(frame)
99
+ bytes.should eq [0x10,0x1,0xc,0x1,0xf,*'header-block'.bytes].pack("SCCLLC*")
100
+ f.parse(Buffer.new(bytes)).should eq frame
101
+ end
102
+ end
103
+
104
+ context "PRIORITY" do
105
+ it "should generate and parse bytes" do
106
+ frame = {
107
+ length: 4,
108
+ type: :priority,
109
+ stream: 1,
110
+ priority: 15
111
+ }
112
+
113
+ bytes = f.generate(frame)
114
+ bytes.should eq [0x4,0x2,0x0,0x1,0xf].pack("SCCLL")
115
+ f.parse(Buffer.new(bytes)).should eq frame
116
+ end
117
+ end
118
+
119
+ context "RST_STREAM" do
120
+ it "should generate and parse bytes" do
121
+ frame = {
122
+ length: 4,
123
+ type: :rst_stream,
124
+ stream: 1,
125
+ error: :stream_closed
126
+ }
127
+
128
+ bytes = f.generate(frame)
129
+ bytes.should eq [0x4,0x3,0x0,0x1,0x5].pack("SCCLL")
130
+ f.parse(Buffer.new(bytes)).should eq frame
131
+ end
132
+ end
133
+
134
+ context "SETTINGS" do
135
+ let(:frame) {
136
+ {
137
+ length: 8,
138
+ type: :settings,
139
+ stream: 0,
140
+ payload: {
141
+ settings_max_concurrent_streams: 10
142
+ }
143
+ }
144
+ }
145
+
146
+ it "should generate and parse bytes" do
147
+
148
+ bytes = f.generate(frame)
149
+ bytes.should eq [0x8,0x4,0x0,0x0,0x4,0xa].pack("SCCLLL")
150
+ f.parse(Buffer.new(bytes)).should eq frame
151
+ end
152
+
153
+ it "should encode custom settings" do
154
+ frame[:length] = 8*3
155
+ frame[:payload] = {
156
+ settings_max_concurrent_streams: 10,
157
+ settings_initial_window_size: 20,
158
+ 55 => 30
159
+ }
160
+
161
+ f.parse(Buffer.new(f.generate(frame))).should eq frame
162
+
163
+ end
164
+
165
+ it "should raise exception on invalid stream ID" do
166
+ expect {
167
+ frame[:stream] = 1
168
+ f.generate(frame)
169
+ }.to raise_error(CompressionError, /Invalid stream ID/)
170
+ end
171
+
172
+ it "should raise exception on invalid setting" do
173
+ expect {
174
+ frame[:payload] = {random: 23}
175
+ f.generate(frame)
176
+ }.to raise_error(CompressionError, /Unknown settings ID/)
177
+ end
178
+ end
179
+
180
+ context "PUSH_PROMISE" do
181
+ it "should generate and parse bytes" do
182
+ frame = {
183
+ length: 11,
184
+ type: :push_promise,
185
+ flags: [:end_push_promise],
186
+ stream: 1,
187
+ promise_stream: 2,
188
+ payload: 'headers'
189
+ }
190
+
191
+ bytes = f.generate(frame)
192
+ bytes.should eq [0xb,0x5,0x1,0x1,0x2,*'headers'.bytes].pack("SCCLLC*")
193
+ f.parse(Buffer.new(bytes)).should eq frame
194
+ end
195
+ end
196
+
197
+ context "PING" do
198
+ let(:frame) {
199
+ {
200
+ length: 8,
201
+ stream: 1,
202
+ type: :ping,
203
+ flags: [:pong],
204
+ payload: '12345678'
205
+ }
206
+ }
207
+
208
+ it "should generate and parse bytes" do
209
+ bytes = f.generate(frame)
210
+ bytes.should eq [0x8,0x6,0x1,0x1,*'12345678'.bytes].pack("SCCLC*")
211
+ f.parse(Buffer.new(bytes)).should eq frame
212
+ end
213
+
214
+ it "should raise exception on invalid payload" do
215
+ expect {
216
+ frame[:payload] = "1234"
217
+ f.generate(frame)
218
+ }.to raise_error(CompressionError, /Invalid payload size/)
219
+ end
220
+ end
221
+
222
+ context "GOAWAY" do
223
+ let(:frame) {
224
+ {
225
+ length: 13,
226
+ stream: 1,
227
+ type: :goaway,
228
+ last_stream: 2,
229
+ error: :no_error,
230
+ payload: 'debug'
231
+ }
232
+ }
233
+
234
+ it "should generate and parse bytes" do
235
+ bytes = f.generate(frame)
236
+ bytes.should eq [0xd,0x7,0x0,0x1,0x2,0x0,*'debug'.bytes].pack("SCCLLLC*")
237
+ f.parse(Buffer.new(bytes)).should eq frame
238
+ end
239
+
240
+ it "should treat debug payload as optional" do
241
+ frame.delete :payload
242
+ frame[:length] = 0x8
243
+
244
+ bytes = f.generate(frame)
245
+ bytes.should eq [0x8,0x7,0x0,0x1,0x2,0x0].pack("SCCLLL")
246
+ f.parse(Buffer.new(bytes)).should eq frame
247
+ end
248
+ end
249
+
250
+ context "WINDOW_UPDATE" do
251
+ it "should generate and parse bytes" do
252
+ frame = {
253
+ length: 4,
254
+ type: :window_update,
255
+ increment: 10
256
+ }
257
+
258
+ bytes = f.generate(frame)
259
+ bytes.should eq [0x4,0x9,0x0,0x0,0xa].pack("SCCLL")
260
+ f.parse(Buffer.new(bytes)).should eq frame
261
+ end
262
+ end
263
+
264
+ context "CONTINUATION" do
265
+ it "should generate and parse bytes" do
266
+ frame = {
267
+ length: 12,
268
+ type: :continuation,
269
+ stream: 1,
270
+ flags: [:end_stream, :end_headers],
271
+ payload: 'header-block'
272
+ }
273
+
274
+ bytes = f.generate(frame)
275
+ bytes.should eq [0xc,0xa,0x3,0x1,*'header-block'.bytes].pack("SCCLC*")
276
+ f.parse(Buffer.new(bytes)).should eq frame
277
+ end
278
+ end
279
+
280
+ it "should determine frame length" do
281
+ frames = [
282
+ [{type: :data, stream: 1, flags: [:end_stream], payload: "abc"}, 3],
283
+ [{type: :headers, stream: 1, payload: "abc"}, 3],
284
+ [{type: :priority, stream: 3, priority: 30}, 4],
285
+ [{type: :rst_stream, stream: 3, error: 100}, 4],
286
+ [{type: :settings, payload: {settings_max_concurrent_streams: 10}}, 8],
287
+ [{type: :push_promise, promise_stream: 5, payload: "abc"}, 7],
288
+ [{type: :ping, payload: "blob"*2}, 8],
289
+ [{type: :goaway, last_stream: 5, error: 20, payload: "blob"}, 12],
290
+ [{type: :window_update, stream: 1, increment: 1024}, 4],
291
+ [{type: :continuation, stream: 1, payload: "abc"}, 3]
292
+ ]
293
+
294
+ frames.each do |(frame, size)|
295
+ bytes = f.generate(frame)
296
+ Buffer.new(bytes).slice(0,2).unpack("S").first.should eq size
297
+ end
298
+ end
299
+
300
+ it "should parse single frame at a time" do
301
+ frames = [
302
+ {type: :headers, stream: 1, payload: "headers"},
303
+ {type: :data, stream: 1, flags: [:end_stream], payload: "abc"}
304
+ ]
305
+
306
+ buf = Buffer.new(f.generate(frames[0]) + f.generate(frames[1]))
307
+
308
+ f.parse(buf).should eq frames[0]
309
+ f.parse(buf).should eq frames[1]
310
+ end
311
+
312
+ it "should process full frames only" do
313
+ frame = {type: :headers, stream: 1, payload: "headers"}
314
+ bytes = f.generate(frame)
315
+
316
+ buf = Buffer.new(bytes[0...-1])
317
+ f.parse(buf).should be_nil
318
+ buf.should eq bytes[0...-1]
319
+
320
+ buf = Buffer.new(bytes)
321
+ f.parse(buf).should eq frame
322
+ buf.should be_empty
323
+ end
324
+
325
+ end
data/spec/helper.rb ADDED
@@ -0,0 +1,98 @@
1
+ require 'http/2'
2
+
3
+ include HTTP2
4
+ include HTTP2::Header
5
+ include HTTP2::Error
6
+
7
+ DATA = {
8
+ type: :data,
9
+ flags: [:end_stream],
10
+ stream: 1,
11
+ payload: 'text'
12
+ }
13
+
14
+ HEADERS = {
15
+ type: :headers,
16
+ flags: [:end_headers],
17
+ stream: 1,
18
+ payload: 'header-block'
19
+ }
20
+
21
+ HEADERS_END_STREAM = {
22
+ type: :headers,
23
+ flags: [:end_headers, :end_stream],
24
+ stream: 1,
25
+ payload: 'header-block'
26
+ }
27
+
28
+ PRIORITY = {
29
+ type: :priority,
30
+ stream: 1,
31
+ priority: 15
32
+ }
33
+
34
+ RST_STREAM = {
35
+ type: :rst_stream,
36
+ stream: 1,
37
+ error: :stream_closed
38
+ }
39
+
40
+ SETTINGS = {
41
+ type: :settings,
42
+ stream: 0,
43
+ payload: {
44
+ settings_max_concurrent_streams: 10,
45
+ settings_flow_control_options: 1
46
+ }
47
+ }
48
+
49
+ PUSH_PROMISE = {
50
+ type: :push_promise,
51
+ flags: [:end_push_promise],
52
+ stream: 1,
53
+ promise_stream: 2,
54
+ payload: 'headers'
55
+ }
56
+
57
+ PING = {
58
+ stream: 0,
59
+ type: :ping,
60
+ payload: '12345678'
61
+ }
62
+
63
+ PONG = {
64
+ stream: 0,
65
+ type: :ping,
66
+ flags: [:pong],
67
+ payload: '12345678'
68
+ }
69
+
70
+ GOAWAY = {
71
+ type: :goaway,
72
+ last_stream: 2,
73
+ error: :no_error,
74
+ payload: 'debug'
75
+ }
76
+
77
+ WINDOW_UPDATE = {
78
+ type: :window_update,
79
+ increment: 10
80
+ }
81
+
82
+ CONTINUATION = {
83
+ type: :continuation,
84
+ flags: [:end_headers],
85
+ payload: '-second-block'
86
+ }
87
+
88
+ FRAME_TYPES = [
89
+ DATA, HEADERS, PRIORITY, RST_STREAM, SETTINGS, PUSH_PROMISE,
90
+ PING, GOAWAY, WINDOW_UPDATE, CONTINUATION
91
+ ]
92
+
93
+ def set_stream_id(bytes, id)
94
+ head = bytes.slice!(0,8).unpack("SCCL")
95
+ head[3] = id
96
+
97
+ head.pack("SCCL") + bytes
98
+ end