mieps_http-2 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,794 @@
1
+ require 'helper'
2
+
3
+ RSpec.describe HTTP2::Stream do
4
+ before(:each) do
5
+ @client = Client.new
6
+ @stream = @client.new_stream
7
+ end
8
+
9
+ context 'stream states' do
10
+ it 'should initiliaze all streams to IDLE' do
11
+ expect(@stream.state).to eq :idle
12
+ end
13
+
14
+ it 'should set custom stream priority' do
15
+ stream = @client.new_stream(weight: 3, dependency: 2, exclusive: true)
16
+ expect(stream.weight).to eq 3
17
+ end
18
+
19
+ context 'idle' do
20
+ it 'should transition to open on sent HEADERS' do
21
+ @stream.send HEADERS.deep_dup
22
+ expect(@stream.state).to eq :open
23
+ end
24
+ it 'should transition to open on received HEADERS' do
25
+ @stream.receive HEADERS
26
+ expect(@stream.state).to eq :open
27
+ end
28
+ it 'should transition to reserved (local) on sent PUSH_PROMISE' do
29
+ @stream.send PUSH_PROMISE.deep_dup
30
+ expect(@stream.state).to eq :reserved_local
31
+ end
32
+ it 'should transition to reserved (remote) on received PUSH_PROMISE' do
33
+ @stream.receive PUSH_PROMISE
34
+ expect(@stream.state).to eq :reserved_remote
35
+ end
36
+ it 'should reprioritize stream on sent PRIORITY' do
37
+ expect { @stream.send PRIORITY.dup }.to_not raise_error
38
+ expect(@stream.weight).to eq 20
39
+ end
40
+ it 'should reprioritize stream on received PRIORITY' do
41
+ expect { @stream.send PRIORITY.dup }.to_not raise_error
42
+ expect(@stream.weight).to eq 20
43
+ end
44
+ end
45
+
46
+ context 'reserved (local)' do
47
+ before(:each) { @stream.send PUSH_PROMISE.deep_dup }
48
+
49
+ it 'should transition on sent PUSH_PROMISE' do
50
+ expect(@stream.state).to eq :reserved_local
51
+ end
52
+
53
+ it 'should allow HEADERS to be sent' do
54
+ expect { @stream.send HEADERS.deep_dup }.to_not raise_error
55
+ end
56
+
57
+ it 'should raise error if sending invalid frames' do
58
+ (FRAME_TYPES - [HEADERS, RST_STREAM]).each do |type|
59
+ expect { @stream.dup.send type }.to raise_error InternalError
60
+ end
61
+ end
62
+
63
+ it 'should raise error on receipt of invalid frames' do
64
+ what_types = (FRAME_TYPES - [PRIORITY, RST_STREAM, WINDOW_UPDATE])
65
+ what_types.each do |type|
66
+ expect { @stream.dup.receive type }.to raise_error InternalError
67
+ end
68
+ end
69
+
70
+ it 'should transition to half closed (remote) on sent HEADERS' do
71
+ @stream.send HEADERS.deep_dup
72
+ expect(@stream.state).to eq :half_closed_remote
73
+ end
74
+
75
+ it 'should transition to closed on sent RST_STREAM' do
76
+ @stream.close
77
+ expect(@stream.state).to eq :closed
78
+ end
79
+
80
+ it 'should transition to closed on received RST_STREAM' do
81
+ @stream.receive RST_STREAM
82
+ expect(@stream.state).to eq :closed
83
+ end
84
+
85
+ it 'should reprioritize stream on PRIORITY' do
86
+ expect { @stream.receive PRIORITY }.to_not raise_error
87
+ expect(@stream.weight).to eq 20
88
+ end
89
+
90
+ it 'should increment remote_window on received WINDOW_UPDATE' do
91
+ expect { @stream.receive WINDOW_UPDATE }.to_not raise_error
92
+ expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW + WINDOW_UPDATE[:increment]
93
+ end
94
+ end
95
+
96
+ context 'reserved (remote)' do
97
+ before(:each) { @stream.receive PUSH_PROMISE }
98
+
99
+ it 'should transition on received PUSH_PROMISE' do
100
+ expect(@stream.state).to eq :reserved_remote
101
+ end
102
+
103
+ it 'should raise error if sending invalid frames' do
104
+ (FRAME_TYPES - [PRIORITY, RST_STREAM, WINDOW_UPDATE]).each do |type|
105
+ expect { @stream.dup.send type }.to raise_error InternalError
106
+ end
107
+ end
108
+
109
+ it 'should raise error on receipt of invalid frames' do
110
+ (FRAME_TYPES - [HEADERS, RST_STREAM]).each do |type|
111
+ expect { @stream.dup.receive type }.to raise_error InternalError
112
+ end
113
+ end
114
+
115
+ it 'should transition to half closed (local) on received HEADERS' do
116
+ @stream.receive HEADERS
117
+ expect(@stream.state).to eq :half_closed_local
118
+ end
119
+
120
+ it 'should transition to closed on sent RST_STREAM' do
121
+ @stream.close
122
+ expect(@stream.state).to eq :closed
123
+ end
124
+
125
+ it 'should transition to closed on received RST_STREAM' do
126
+ @stream.receive RST_STREAM
127
+ expect(@stream.state).to eq :closed
128
+ end
129
+
130
+ it 'should reprioritize stream on PRIORITY' do
131
+ expect { @stream.send PRIORITY.dup }.to_not raise_error
132
+ expect(@stream.weight).to eq 20
133
+ end
134
+
135
+ it 'should increment local_window on sent WINDOW_UPDATE' do
136
+ expect { @stream.send WINDOW_UPDATE.dup }.to_not raise_error
137
+ expect(@stream.local_window).to eq DEFAULT_FLOW_WINDOW + WINDOW_UPDATE[:increment]
138
+ end
139
+ end
140
+
141
+ context 'open' do
142
+ before(:each) { @stream.receive HEADERS }
143
+
144
+ it 'should allow any valid frames types to be sent' do
145
+ (FRAME_TYPES - [PING, GOAWAY, SETTINGS]).each do |type|
146
+ expect { @stream.dup.send type.deep_dup }.to_not raise_error
147
+ end
148
+ end
149
+
150
+ it 'should allow frames of any type to be received' do
151
+ FRAME_TYPES.each do |type|
152
+ expect { @stream.dup.receive type }.to_not raise_error
153
+ end
154
+ end
155
+
156
+ it 'should transition to half closed (local) if sending END_STREAM' do
157
+ [DATA, HEADERS].each do |frame|
158
+ s, f = @stream.dup, frame.deep_dup
159
+ f[:flags] = [:end_stream]
160
+
161
+ s.send f
162
+ expect(s.state).to eq :half_closed_local
163
+ end
164
+ end
165
+
166
+ it 'should transition to half closed (remote) if receiving END_STREAM' do
167
+ [DATA, HEADERS].each do |frame|
168
+ s, f = @stream.dup, frame.dup
169
+ f[:flags] = [:end_stream]
170
+
171
+ s.receive f
172
+ expect(s.state).to eq :half_closed_remote
173
+ end
174
+ end
175
+
176
+ it 'should transition to half closed if remote opened with END_STREAM' do
177
+ s = @client.new_stream
178
+ hclose = HEADERS.dup
179
+ hclose[:flags] = [:end_stream]
180
+
181
+ s.receive hclose
182
+ expect(s.state).to eq :half_closed_remote
183
+ end
184
+
185
+ it 'should transition to half closed if local opened with END_STREAM' do
186
+ s = @client.new_stream
187
+ hclose = HEADERS.deep_dup
188
+ hclose[:flags] = [:end_stream]
189
+
190
+ s.send hclose
191
+ expect(s.state).to eq :half_closed_local
192
+ end
193
+
194
+ it 'should transition to closed if sending RST_STREAM' do
195
+ @stream.close
196
+ expect(@stream.state).to eq :closed
197
+ end
198
+
199
+ it 'should transition to closed if receiving RST_STREAM' do
200
+ @stream.receive RST_STREAM
201
+ expect(@stream.state).to eq :closed
202
+ end
203
+
204
+ it 'should emit :active on open transition' do
205
+ openp, openr = false, false
206
+ sp = @client.new_stream
207
+ sr = @client.new_stream
208
+ sp.on(:active) { openp = true }
209
+ sr.on(:active) { openr = true }
210
+
211
+ sp.receive HEADERS
212
+ sr.send HEADERS.deep_dup
213
+
214
+ expect(openp).to be_truthy
215
+ expect(openr).to be_truthy
216
+ end
217
+
218
+ it 'should not emit :active on transition from open' do
219
+ order, stream = [], @client.new_stream
220
+
221
+ stream.on(:active) { order << :active }
222
+ stream.on(:half_close) { order << :half_close }
223
+ stream.on(:close) { order << :close }
224
+
225
+ req = HEADERS.deep_dup
226
+ req[:flags] = [:end_headers]
227
+
228
+ stream.send req
229
+ stream.send DATA.dup
230
+ expect(order).to eq [:active, :half_close]
231
+ end
232
+
233
+ it 'should emit :close on close transition' do
234
+ closep, closer = false, false
235
+ sp, sr = @stream.dup, @stream.dup
236
+
237
+ sp.on(:close) { closep = true }
238
+ sr.on(:close) { closer = true }
239
+
240
+ sp.receive RST_STREAM
241
+ sr.close
242
+
243
+ expect(closep).to be_truthy
244
+ expect(closer).to be_truthy
245
+ end
246
+
247
+ it 'should emit :close after frame is processed' do
248
+ order, stream = [], @client.new_stream
249
+
250
+ stream.on(:active) { order << :active }
251
+ stream.on(:data) { order << :data }
252
+ stream.on(:half_close) { order << :half_close }
253
+ stream.on(:close) { order << :close }
254
+
255
+ req = HEADERS.deep_dup
256
+ req[:flags] = [:end_stream, :end_headers]
257
+
258
+ stream.send req
259
+ stream.receive HEADERS
260
+ stream.receive DATA
261
+
262
+ expect(order).to eq [:active, :half_close, :data, :close]
263
+ end
264
+
265
+ it 'should emit :close with reason' do
266
+ reason = nil
267
+ @stream.on(:close) { |r| reason = r }
268
+ @stream.receive RST_STREAM
269
+ expect(reason).not_to be_nil
270
+ end
271
+
272
+ it 'should reprioritize stream on sent PRIORITY' do
273
+ expect { @stream.send PRIORITY.dup }.to_not raise_error
274
+ expect(@stream.weight).to eq 20
275
+ end
276
+ it 'should reprioritize stream on received PRIORITY' do
277
+ expect { @stream.receive PRIORITY }.to_not raise_error
278
+ expect(@stream.weight).to eq 20
279
+ end
280
+ end
281
+
282
+ context 'half closed (local)' do
283
+ before(:each) { @stream.send HEADERS_END_STREAM.deep_dup }
284
+
285
+ it 'should raise error on attempt to send invalid frames' do
286
+ (FRAME_TYPES - [PRIORITY, RST_STREAM, WINDOW_UPDATE]).each do |frame|
287
+ expect { @stream.dup.send frame }.to raise_error InternalError
288
+ end
289
+ end
290
+
291
+ it 'should transition to closed on receipt of END_STREAM flag' do
292
+ [DATA, HEADERS, CONTINUATION].each do |frame|
293
+ s, f = @stream.dup, frame.dup
294
+ f[:flags] = [:end_stream]
295
+
296
+ s.receive f
297
+ expect(s.state).to eq :closed
298
+ end
299
+ end
300
+
301
+ it 'should transition to closed on receipt of RST_STREAM frame' do
302
+ @stream.receive RST_STREAM
303
+ expect(@stream.state).to eq :closed
304
+ end
305
+
306
+ it 'should transition to closed if RST_STREAM frame is sent' do
307
+ @stream.send RST_STREAM.deep_dup
308
+ expect(@stream.state).to eq :closed
309
+ end
310
+
311
+ it 'should ignore received WINDOW_UPDATE frames' do
312
+ expect { @stream.receive WINDOW_UPDATE }.to_not raise_error
313
+ expect(@stream.state).to eq :half_closed_local
314
+ end
315
+
316
+ it 'should ignore received PRIORITY frames' do
317
+ expect { @stream.receive PRIORITY }.to_not raise_error
318
+ expect(@stream.state).to eq :half_closed_local
319
+ end
320
+
321
+ it 'should reprioritize stream on sent PRIORITY' do
322
+ expect { @stream.send PRIORITY.dup }.to_not raise_error
323
+ expect(@stream.weight).to eq 20
324
+ end
325
+
326
+ it 'should reprioritize stream (and decendants) on received PRIORITY' do
327
+ expect { @stream.receive PRIORITY }.to_not raise_error
328
+ expect(@stream.weight).to eq 20
329
+ end
330
+
331
+ it 'should increment local_window on sent WINDOW_UPDATE' do
332
+ expect { @stream.send WINDOW_UPDATE.dup }.to_not raise_error
333
+ expect(@stream.local_window).to eq DEFAULT_FLOW_WINDOW + WINDOW_UPDATE[:increment]
334
+ end
335
+
336
+ it 'should emit :half_close event on transition' do
337
+ order = []
338
+ stream = @client.new_stream
339
+ stream.on(:active) { order << :active }
340
+ stream.on(:half_close) { order << :half_close }
341
+
342
+ req = HEADERS.deep_dup
343
+ req[:flags] = [:end_stream, :end_headers]
344
+
345
+ stream.send req
346
+ expect(order).to eq [:active, :half_close]
347
+ end
348
+
349
+ it 'should emit :close event on transition to closed' do
350
+ closed = false
351
+ @stream.on(:close) { closed = true }
352
+ @stream.receive RST_STREAM
353
+
354
+ expect(@stream.state).to eq :closed
355
+ expect(closed).to be_truthy
356
+ end
357
+ end
358
+
359
+ context 'half closed (remote)' do
360
+ before(:each) { @stream.receive HEADERS_END_STREAM }
361
+
362
+ it 'should raise STREAM_CLOSED error on reciept of frames' do
363
+ (FRAME_TYPES - [PRIORITY, RST_STREAM, WINDOW_UPDATE]).each do |frame|
364
+ expect do
365
+ @stream.dup.receive frame
366
+ end.to raise_error(StreamClosed)
367
+ end
368
+ end
369
+
370
+ it 'should transition to closed if END_STREAM flag is sent' do
371
+ [DATA, HEADERS].each do |frame|
372
+ s, f = @stream.dup, frame.deep_dup
373
+ f[:flags] = [:end_stream]
374
+
375
+ s.on(:close) { expect(s.state).to eq :closed }
376
+ s.send f
377
+ expect(s.state).to eq :closed
378
+ end
379
+ end
380
+
381
+ it 'should transition to closed if RST_STREAM is sent' do
382
+ @stream.close
383
+ expect(@stream.state).to eq :closed
384
+ end
385
+
386
+ it 'should transition to closed on reciept of RST_STREAM frame' do
387
+ @stream.receive RST_STREAM
388
+ expect(@stream.state).to eq :closed
389
+ end
390
+
391
+ it 'should ignore sent WINDOW_UPDATE frames' do
392
+ expect { @stream.send WINDOW_UPDATE.dup }.to_not raise_error
393
+ expect(@stream.state).to eq :half_closed_remote
394
+ end
395
+
396
+ it 'should increment remote_window on received WINDOW_UPDATE' do
397
+ expect { @stream.receive WINDOW_UPDATE }.to_not raise_error
398
+ expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW + WINDOW_UPDATE[:increment]
399
+ end
400
+
401
+ it 'should reprioritize stream on sent PRIORITY' do
402
+ expect { @stream.send PRIORITY.dup }.to_not raise_error
403
+ expect(@stream.weight).to eq 20
404
+ end
405
+ it 'should reprioritize stream on received PRIORITY' do
406
+ expect { @stream.receive PRIORITY }.to_not raise_error
407
+ expect(@stream.weight).to eq 20
408
+ end
409
+
410
+ it 'should emit :half_close event on transition' do
411
+ order = []
412
+ stream = @client.new_stream
413
+ stream.on(:active) { order << :active }
414
+ stream.on(:half_close) { order << :half_close }
415
+
416
+ req = HEADERS.dup
417
+ req[:flags] = [:end_stream, :end_headers]
418
+
419
+ stream.receive req
420
+ expect(order).to eq [:active, :half_close]
421
+ end
422
+
423
+ it 'should emit :close event on close transition' do
424
+ closed = false
425
+ @stream.on(:close) { closed = true }
426
+ @stream.close
427
+
428
+ expect(@stream.state).to eq :closed
429
+ expect(closed).to be_truthy
430
+ end
431
+ end
432
+
433
+ context 'closed' do
434
+ context 'remote closed stream' do
435
+ before(:each) do
436
+ @stream.send HEADERS_END_STREAM.deep_dup # half closed local
437
+ @stream.receive HEADERS_END_STREAM # closed by remote
438
+ end
439
+
440
+ it 'should raise STREAM_CLOSED on attempt to send frames' do
441
+ (FRAME_TYPES - [PRIORITY, RST_STREAM]).each do |frame|
442
+ expect do
443
+ @stream.dup.send frame
444
+ end.to raise_error(StreamClosed)
445
+ end
446
+ end
447
+
448
+ it 'should raise STREAM_CLOSED on receipt of frame' do
449
+ (FRAME_TYPES - [PRIORITY, RST_STREAM, WINDOW_UPDATE]).each do |frame|
450
+ expect do
451
+ @stream.dup.receive frame
452
+ end.to raise_error(StreamClosed)
453
+ end
454
+ end
455
+
456
+ it 'should allow PRIORITY, RST_STREAM to be sent' do
457
+ expect { @stream.send PRIORITY.dup }.to_not raise_error
458
+ expect { @stream.send RST_STREAM.dup }.to_not raise_error
459
+ end
460
+
461
+ it 'should allow PRIORITY, RST_STREAM to be received' do
462
+ expect { @stream.receive PRIORITY }.to_not raise_error
463
+ expect { @stream.receive RST_STREAM }.to_not raise_error
464
+ end
465
+
466
+ it 'should reprioritize stream on sent PRIORITY' do
467
+ expect { @stream.send PRIORITY.dup }.to_not raise_error
468
+ expect(@stream.weight).to eq 20
469
+ end
470
+ it 'should reprioritize stream on received PRIORITY' do
471
+ expect { @stream.receive PRIORITY }.to_not raise_error
472
+ expect(@stream.weight).to eq 20
473
+ end
474
+
475
+ it 'should ignore received WINDOW_UPDATE frames' do
476
+ expect { @stream.receive WINDOW_UPDATE }.to_not raise_error
477
+ expect(@stream.state).to eq :closed
478
+ end
479
+ end
480
+
481
+ context 'local closed via RST_STREAM frame' do
482
+ before(:each) do
483
+ @stream.send HEADERS.deep_dup # open
484
+ @stream.send RST_STREAM.deep_dup # closed by local
485
+ end
486
+
487
+ it 'should ignore received frames' do
488
+ (FRAME_TYPES - [PUSH_PROMISE]).each do |frame|
489
+ expect do
490
+ cb = []
491
+ @stream.on(:data) { cb << :data }
492
+ @stream.on(:headers) { cb << :headers }
493
+ @stream.dup.receive frame.dup
494
+ expect(cb).to be_empty
495
+ end.to_not raise_error
496
+ end
497
+ end
498
+
499
+ # it "should transition to reserved remote on PUSH_PROMISE" do
500
+ # An endpoint might receive a PUSH_PROMISE frame after it sends
501
+ # RST_STREAM. PUSH_PROMISE causes a stream to become "reserved".
502
+ # ...
503
+ # We're auto RST'ing PUSH streams in connection class, hence
504
+ # skipping this transition for now.
505
+ # end
506
+ end
507
+
508
+ # FIXME: Isn't this test same as "half closed (local)"?
509
+ # context "local closed via END_STREAM flag" do
510
+ # before(:each) do
511
+ # @stream.send HEADERS # open
512
+ # @stream.send DATA # contains end_stream flag
513
+ # end
514
+
515
+ # it "should ignore received frames" do
516
+ # FRAME_TYPES.each do |frame|
517
+ # expect { @stream.dup.receive frame }.to_not raise_error
518
+ # end
519
+ # end
520
+ # end
521
+ end
522
+ end # end stream states
523
+
524
+ # TODO: add test cases to ensure on(:priority) emitted after close
525
+
526
+ context 'flow control' do
527
+ it 'should initialize to default flow control window' do
528
+ expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW
529
+ end
530
+
531
+ it 'should update window size on DATA frames only' do
532
+ @stream.send HEADERS.deep_dup # go to open
533
+ expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW
534
+
535
+ (FRAME_TYPES - [DATA, PING, GOAWAY, SETTINGS]).each do |frame|
536
+ s = @stream.dup
537
+ s.send frame.deep_dup
538
+ expect(s.remote_window).to eq DEFAULT_FLOW_WINDOW
539
+ end
540
+
541
+ @stream.send DATA.dup
542
+ expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW - DATA[:payload].bytesize
543
+ end
544
+
545
+ it 'should update window size on receipt of WINDOW_UPDATE' do
546
+ @stream.send HEADERS.deep_dup
547
+ @stream.send DATA.dup
548
+ @stream.receive WINDOW_UPDATE
549
+
550
+ expect(@stream.remote_window).to eq(
551
+ DEFAULT_FLOW_WINDOW - DATA[:payload].bytesize + WINDOW_UPDATE[:increment],
552
+ )
553
+ end
554
+
555
+ it 'should observe session flow control' do
556
+ settings, data = SETTINGS.dup, DATA.dup
557
+ settings[:payload] = [[:settings_initial_window_size, 1000]]
558
+ settings[:stream] = 0
559
+
560
+ framer = Framer.new
561
+ @client << framer.generate(settings)
562
+
563
+ s1 = @client.new_stream
564
+ s1.send HEADERS.deep_dup
565
+ s1.send data.merge(payload: 'x' * 900, flags: [])
566
+ expect(s1.remote_window).to eq 100
567
+
568
+ s1.send data.merge(payload: 'x' * 200)
569
+ expect(s1.remote_window).to eq 0
570
+ expect(s1.buffered_amount).to eq 100
571
+
572
+ @client << framer.generate(WINDOW_UPDATE.merge(stream: s1.id, increment: 1000))
573
+ expect(s1.buffered_amount).to eq 0
574
+ expect(s1.remote_window).to eq 900
575
+ end
576
+ end
577
+
578
+ context 'client API' do
579
+ it '.reprioritize should emit PRIORITY frame' do
580
+ expect(@stream).to receive(:send) do |frame|
581
+ expect(frame[:type]).to eq :priority
582
+ expect(frame[:weight]).to eq 30
583
+ end
584
+
585
+ @stream.reprioritize weight: 30
586
+ end
587
+
588
+ it '.reprioritize should raise error if invoked by server' do
589
+ srv = Server.new
590
+ stream = srv.new_stream
591
+
592
+ expect { stream.reprioritize(weight: 10) }.to raise_error(InternalError)
593
+ end
594
+
595
+ it '.headers should emit HEADERS frames' do
596
+ payload = {
597
+ ':method' => 'GET',
598
+ ':scheme' => 'http',
599
+ ':host' => 'www.example.org',
600
+ ':path' => '/resource',
601
+ 'custom' => 'value',
602
+ }
603
+
604
+ expect(@stream).to receive(:send) do |frame|
605
+ expect(frame[:type]).to eq :headers
606
+ expect(frame[:payload]).to eq payload.to_a
607
+ expect(frame[:flags]).to eq [:end_headers]
608
+ end
609
+
610
+ @stream.headers(payload, end_stream: false, end_headers: true)
611
+ end
612
+
613
+ it '.data should emit DATA frames' do
614
+ expect(@stream).to receive(:send) do |frame|
615
+ expect(frame[:type]).to eq :data
616
+ expect(frame[:payload]).to eq 'text'
617
+ expect(frame[:flags]).to be_empty
618
+ end
619
+ @stream.data('text', end_stream: false)
620
+
621
+ expect(@stream).to receive(:send) do |frame|
622
+ expect(frame[:flags]).to eq [:end_stream]
623
+ end
624
+ @stream.data('text')
625
+ end
626
+
627
+ it '.data should split large DATA frames' do
628
+ data = 'x' * 16_384 * 2
629
+
630
+ want = [
631
+ { type: :data, flags: [], length: 16_384 },
632
+ { type: :data, flags: [], length: 16_384 },
633
+ { type: :data, flags: [:end_stream], length: 1 },
634
+ ]
635
+ want.each do |w|
636
+ expect(@stream).to receive(:send) do |frame|
637
+ expect(frame[:type]).to eq w[:type]
638
+ expect(frame[:flags]).to eq w[:flags]
639
+ expect(frame[:payload].length).to eq w[:length]
640
+ end
641
+ end
642
+
643
+ @stream.data(data + 'x')
644
+ end
645
+
646
+ it '.cancel should reset stream with cancel error code' do
647
+ expect(@stream).to receive(:send) do |frame|
648
+ expect(frame[:type]).to eq :rst_stream
649
+ expect(frame[:error]).to eq :cancel
650
+ end
651
+
652
+ @stream.cancel
653
+ end
654
+
655
+ it '.refuse should reset stream with refused stream error code' do
656
+ expect(@stream).to receive(:send) do |frame|
657
+ expect(frame[:type]).to eq :rst_stream
658
+ expect(frame[:error]).to eq :refused_stream
659
+ end
660
+
661
+ @stream.refuse
662
+ end
663
+ end
664
+
665
+ context 'server API' do
666
+ before(:each) do
667
+ @srv = Server.new
668
+ @frm = Framer.new
669
+
670
+ @client.on(:frame) { |bytes| @srv << bytes }
671
+ @client_stream = @client.new_stream
672
+ end
673
+
674
+ it 'should emit received headers via on(:headers)' do
675
+ headers, recv = [%w(header value)], nil
676
+ @srv.on(:stream) do |stream|
677
+ stream.on(:headers) { |h| recv = h }
678
+ end
679
+
680
+ @client_stream.headers(headers)
681
+ expect(recv).to eq headers
682
+ end
683
+
684
+ it 'should emit received payload via on(:data)' do
685
+ payload = 'some-payload'
686
+ @srv.on(:stream) do |stream|
687
+ stream.on(:data) do |recv|
688
+ expect(recv).to eq payload
689
+ end
690
+ end
691
+
692
+ @client_stream.headers('key' => 'value')
693
+ @client_stream.data(payload)
694
+ end
695
+
696
+ it 'should emit received priority parameters via on(:priority)' do
697
+ new_weight, new_dependency = 15, @client_stream.id + 2
698
+ callback_called = false
699
+ @srv.on(:stream) do |stream|
700
+ stream.on(:priority) do |pri|
701
+ callback_called = true
702
+ expect(pri.is_a?(Hash)).to be
703
+ expect(pri[:weight]).to eq new_weight
704
+ expect(pri[:dependency]).to eq new_dependency
705
+ end
706
+ end
707
+
708
+ @client_stream.headers('key' => 'value')
709
+ @client_stream.reprioritize(weight: new_weight, dependency: new_dependency)
710
+ expect(callback_called).to be
711
+ end
712
+
713
+ context 'push' do
714
+ before(:each) do
715
+ @srv.on(:frame) { |bytes| @client << bytes }
716
+ @srv.on(:stream) do |stream|
717
+ @server_stream = stream
718
+ end
719
+
720
+ @client_stream.headers('key' => 'value')
721
+ end
722
+
723
+ it '.promise should emit server initiated stream' do
724
+ push = nil
725
+ @server_stream.promise('key' => 'val') { |pstream| push = pstream }
726
+ expect(push.id).to eq 2
727
+ end
728
+
729
+ it '.promise push stream should have parent stream' do
730
+ push = nil
731
+ @server_stream.promise('key' => 'val') { |pstream| push = pstream }
732
+
733
+ expect(push.state).to eq :reserved_local
734
+ expect(push.parent.id).to eq @server_stream.id
735
+ end
736
+
737
+ context 'stream states' do
738
+ it 'server: active > half close > close' do
739
+ order = []
740
+ @server_stream.promise('key' => 'val') do |push|
741
+ stream = push
742
+
743
+ expect(push.state).to eq :reserved_local
744
+ order << :reserved
745
+
746
+ push.on(:active) { order << :active }
747
+ push.on(:half_close) { order << :half_close }
748
+ push.on(:close) { order << :close }
749
+
750
+ push.headers('key2' => 'val2')
751
+ push.send DATA.merge(stream: stream.id)
752
+ end
753
+
754
+ expect(order).to eq [:reserved, :active, :half_close, :close]
755
+ end
756
+
757
+ it 'client: headers > active > headers > .. > data > close' do
758
+ order, headers = [], []
759
+ @client.on(:promise) do |push|
760
+ order << :reserved
761
+
762
+ push.on(:active) { order << :active }
763
+ push.on(:data) { order << :data }
764
+ push.on(:half_close) { order << :half_close }
765
+ push.on(:close) { order << :close }
766
+
767
+ push.on(:headers) do |h|
768
+ order << :headers
769
+ headers += h
770
+ end
771
+
772
+ expect(push.id).to be_even
773
+ end
774
+
775
+ @server_stream.promise('key' => 'val') do |push|
776
+ push.headers('key2' => 'val2')
777
+ push.data('somedata')
778
+ end
779
+
780
+ expect(headers).to eq([%w(key val), %w(key2 val2)])
781
+ expect(order).to eq [
782
+ :reserved,
783
+ :headers,
784
+ :active,
785
+ :headers,
786
+ :half_close,
787
+ :data,
788
+ :close,
789
+ ]
790
+ end
791
+ end
792
+ end
793
+ end
794
+ end