http-2 0.6.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.gitmodules +3 -0
- data/Gemfile +5 -0
- data/README.md +5 -4
- data/Rakefile +1 -0
- data/example/client.rb +20 -5
- data/example/helper.rb +1 -1
- data/example/keys/mycert.pem +21 -22
- data/example/keys/mykey.pem +25 -25
- data/example/server.rb +10 -3
- data/http-2.gemspec +1 -1
- data/lib/http/2.rb +2 -0
- data/lib/http/2/client.rb +16 -10
- data/lib/http/2/compressor.rb +346 -286
- data/lib/http/2/connection.rb +254 -95
- data/lib/http/2/error.rb +0 -6
- data/lib/http/2/flow_buffer.rb +12 -10
- data/lib/http/2/framer.rb +203 -57
- data/lib/http/2/huffman.rb +332 -0
- data/lib/http/2/huffman_statemachine.rb +272 -0
- data/lib/http/2/server.rb +5 -4
- data/lib/http/2/stream.rb +72 -35
- data/lib/http/2/version.rb +1 -1
- data/lib/tasks/generate_huffman_table.rb +160 -0
- data/spec/client_spec.rb +3 -3
- data/spec/compressor_spec.rb +422 -281
- data/spec/connection_spec.rb +236 -56
- data/spec/framer_spec.rb +213 -45
- data/spec/helper.rb +42 -15
- data/spec/hpack_test_spec.rb +83 -0
- data/spec/huffman_spec.rb +68 -0
- data/spec/server_spec.rb +7 -6
- data/spec/stream_spec.rb +81 -54
- metadata +21 -11
data/lib/http/2/connection.rb
CHANGED
@@ -3,11 +3,36 @@ module HTTP2
|
|
3
3
|
# Default connection and stream flow control window (64KB).
|
4
4
|
DEFAULT_FLOW_WINDOW = 65535
|
5
5
|
|
6
|
+
# Default header table size
|
7
|
+
DEFAULT_HEADER_SIZE = 4096
|
8
|
+
|
9
|
+
# Default stream_limit
|
10
|
+
DEFAULT_MAX_CONCURRENT_STREAMS = 100
|
11
|
+
|
12
|
+
# Default values for SETTINGS frame, as defined by the spec.
|
13
|
+
SPEC_DEFAULT_CONNECTION_SETTINGS = {
|
14
|
+
settings_header_table_size: 4096,
|
15
|
+
settings_enable_push: 1, # enabled for servers
|
16
|
+
settings_max_concurrent_streams: Framer::MAX_STREAM_ID, # unlimited
|
17
|
+
settings_initial_window_size: 65535,
|
18
|
+
settings_max_frame_size: 16384,
|
19
|
+
settings_max_header_list_size: 2**31 - 1, # unlimited
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
DEFAULT_CONNECTION_SETTINGS = {
|
23
|
+
settings_header_table_size: 4096,
|
24
|
+
settings_enable_push: 1, # enabled for servers
|
25
|
+
settings_max_concurrent_streams: 100,
|
26
|
+
settings_initial_window_size: 65535, #
|
27
|
+
settings_max_frame_size: 16384,
|
28
|
+
settings_max_header_list_size: 2**31 - 1, # unlimited
|
29
|
+
}.freeze
|
30
|
+
|
6
31
|
# Default stream priority (lower values are higher priority).
|
7
|
-
|
32
|
+
DEFAULT_WEIGHT = 16
|
8
33
|
|
9
34
|
# Default connection "fast-fail" preamble string as defined by the spec.
|
10
|
-
|
35
|
+
CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
11
36
|
|
12
37
|
# Connection encapsulates all of the connection, stream, flow-control,
|
13
38
|
# error management, and other processing logic required for a well-behaved
|
@@ -28,11 +53,17 @@ module HTTP2
|
|
28
53
|
|
29
54
|
# Size of current connection flow control window (by default, set to
|
30
55
|
# infinity, but is automatically updated on receipt of peer settings).
|
31
|
-
attr_reader :
|
56
|
+
attr_reader :local_window
|
57
|
+
attr_reader :remote_window
|
58
|
+
alias :window :local_window
|
59
|
+
|
60
|
+
# Current settings value for local and peer
|
61
|
+
attr_reader :local_settings
|
62
|
+
attr_reader :remote_settings
|
32
63
|
|
33
|
-
#
|
34
|
-
#
|
35
|
-
attr_reader :
|
64
|
+
# Pending settings value
|
65
|
+
# Sent but not ack'ed settings
|
66
|
+
attr_reader :pending_settings
|
36
67
|
|
37
68
|
# Number of active streams between client and server (reserved streams
|
38
69
|
# are not counted towards the stream limit).
|
@@ -40,14 +71,23 @@ module HTTP2
|
|
40
71
|
|
41
72
|
# Initializes new connection object.
|
42
73
|
#
|
43
|
-
def initialize(
|
44
|
-
@
|
74
|
+
def initialize(**settings)
|
75
|
+
@local_settings = DEFAULT_CONNECTION_SETTINGS.merge(settings)
|
76
|
+
@remote_settings = SPEC_DEFAULT_CONNECTION_SETTINGS.dup
|
77
|
+
|
78
|
+
@compressor = Header::Compressor.new(settings)
|
79
|
+
@decompressor = Header::Decompressor.new(settings)
|
80
|
+
|
45
81
|
@active_stream_count = 0
|
46
82
|
@streams = {}
|
83
|
+
@pending_settings = []
|
47
84
|
|
48
85
|
@framer = Framer.new
|
49
|
-
|
50
|
-
@
|
86
|
+
|
87
|
+
@local_window_limit = @local_settings[:settings_initial_window_size]
|
88
|
+
@local_window = @local_window_limit
|
89
|
+
@remote_window_limit = @remote_settings[:settings_initial_window_size]
|
90
|
+
@remote_window = @remote_window_limit
|
51
91
|
|
52
92
|
@recv_buffer = Buffer.new
|
53
93
|
@send_buffer = []
|
@@ -60,11 +100,11 @@ module HTTP2
|
|
60
100
|
# @param priority [Integer]
|
61
101
|
# @param window [Integer]
|
62
102
|
# @param parent [Stream]
|
63
|
-
def new_stream(
|
103
|
+
def new_stream(**args)
|
64
104
|
raise ConnectionClosed.new if @state == :closed
|
65
|
-
raise StreamLimitExceeded.new if @active_stream_count
|
105
|
+
raise StreamLimitExceeded.new if @active_stream_count >= @remote_settings[:settings_max_concurrent_streams]
|
66
106
|
|
67
|
-
stream = activate_stream(@stream_id,
|
107
|
+
stream = activate_stream(id: @stream_id, **args)
|
68
108
|
@stream_id += 2
|
69
109
|
|
70
110
|
stream
|
@@ -76,7 +116,7 @@ module HTTP2
|
|
76
116
|
# @param blk [Proc] callback to execute when PONG is received
|
77
117
|
def ping(payload, &blk)
|
78
118
|
send({type: :ping, stream: 0, payload: payload})
|
79
|
-
once(:
|
119
|
+
once(:ack, &blk) if blk
|
80
120
|
end
|
81
121
|
|
82
122
|
# Sends a GOAWAY frame indicating that the peer should stop creating
|
@@ -97,20 +137,17 @@ module HTTP2
|
|
97
137
|
@state = :closed
|
98
138
|
end
|
99
139
|
|
100
|
-
# Sends a connection SETTINGS frame to the peer.
|
101
|
-
#
|
140
|
+
# Sends a connection SETTINGS frame to the peer.
|
141
|
+
# The values are reflected when the corresponding ACK is received.
|
102
142
|
#
|
103
|
-
# @param
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
else
|
110
|
-
payload[:settings_initial_window_size] = window_limit
|
111
|
-
end
|
112
|
-
|
143
|
+
# @param settings [Array or Hash]
|
144
|
+
def settings(payload)
|
145
|
+
payload = payload.to_a
|
146
|
+
check = validate_settings(@local_role, payload)
|
147
|
+
check and connection_error
|
148
|
+
@pending_settings << payload
|
113
149
|
send({type: :settings, stream: 0, payload: payload})
|
150
|
+
@pending_settings << payload
|
114
151
|
end
|
115
152
|
|
116
153
|
# Decodes incoming bytes into HTTP 2.0 frames and routes them to
|
@@ -128,23 +165,27 @@ module HTTP2
|
|
128
165
|
#
|
129
166
|
# Client connection header is 24 byte connection header followed by
|
130
167
|
# SETTINGS frame. Server connection header is SETTINGS frame only.
|
131
|
-
if @state == :
|
168
|
+
if @state == :waiting_magic
|
132
169
|
if @recv_buffer.size < 24
|
133
|
-
if !
|
170
|
+
if !CONNECTION_PREFACE_MAGIC.start_with? @recv_buffer
|
134
171
|
raise HandshakeError.new
|
135
172
|
else
|
136
|
-
return
|
173
|
+
return # maybe next time
|
137
174
|
end
|
138
175
|
|
139
|
-
elsif @recv_buffer.read(24) !=
|
176
|
+
elsif @recv_buffer.read(24) != CONNECTION_PREFACE_MAGIC
|
140
177
|
raise HandshakeError.new
|
141
178
|
else
|
142
|
-
|
143
|
-
|
179
|
+
# MAGIC is OK. Send our settings
|
180
|
+
@state = :waiting_connection_preface
|
181
|
+
payload = @local_settings.select {|k,v| v != SPEC_DEFAULT_CONNECTION_SETTINGS[k]}
|
182
|
+
settings(payload)
|
144
183
|
end
|
145
184
|
end
|
146
185
|
|
147
186
|
while frame = @framer.parse(@recv_buffer) do
|
187
|
+
emit(:frame_received, frame)
|
188
|
+
|
148
189
|
# Header blocks MUST be transmitted as a contiguous sequence of frames
|
149
190
|
# with no interleaved frames of any other type, or from any other stream.
|
150
191
|
if !@continuation.empty?
|
@@ -156,21 +197,14 @@ module HTTP2
|
|
156
197
|
@continuation << frame
|
157
198
|
return if !frame[:flags].include? :end_headers
|
158
199
|
|
159
|
-
|
160
|
-
decode_headers(chunk)
|
161
|
-
chunk[:payload]
|
162
|
-
end.flatten(1)
|
200
|
+
payload = @continuation.map {|f| f[:payload]}.join
|
163
201
|
|
164
202
|
frame = @continuation.shift
|
165
203
|
@continuation.clear
|
166
204
|
|
167
205
|
frame.delete(:length)
|
168
|
-
frame[:payload] =
|
169
|
-
frame[:flags] <<
|
170
|
-
:end_push_promise
|
171
|
-
else
|
172
|
-
:end_headers
|
173
|
-
end
|
206
|
+
frame[:payload] = Buffer.new(payload)
|
207
|
+
frame[:flags] << :end_headers
|
174
208
|
end
|
175
209
|
|
176
210
|
# SETTINGS frames always apply to a connection, never a single stream.
|
@@ -200,8 +234,10 @@ module HTTP2
|
|
200
234
|
|
201
235
|
stream = @streams[frame[:stream]]
|
202
236
|
if stream.nil?
|
203
|
-
stream = activate_stream(frame[:stream],
|
204
|
-
frame[:
|
237
|
+
stream = activate_stream(id: frame[:stream],
|
238
|
+
weight: frame[:weight] || DEFAULT_WEIGHT,
|
239
|
+
dependency: frame[:dependency] || 0,
|
240
|
+
exclusive: frame[:exclusive] || false)
|
205
241
|
emit(:stream, stream)
|
206
242
|
end
|
207
243
|
|
@@ -209,8 +245,8 @@ module HTTP2
|
|
209
245
|
|
210
246
|
when :push_promise
|
211
247
|
# The last frame in a sequence of PUSH_PROMISE/CONTINUATION
|
212
|
-
# frames MUST have the
|
213
|
-
if !frame[:flags].include? :
|
248
|
+
# frames MUST have the END_HEADERS flag set
|
249
|
+
if !frame[:flags].include? :end_headers
|
214
250
|
@continuation << frame
|
215
251
|
return
|
216
252
|
end
|
@@ -248,7 +284,7 @@ module HTTP2
|
|
248
284
|
end
|
249
285
|
end
|
250
286
|
|
251
|
-
stream = activate_stream(pid,
|
287
|
+
stream = activate_stream(id: pid, parent: parent)
|
252
288
|
emit(:promise, stream)
|
253
289
|
stream << frame
|
254
290
|
else
|
@@ -263,7 +299,7 @@ module HTTP2
|
|
263
299
|
end
|
264
300
|
end
|
265
301
|
|
266
|
-
rescue
|
302
|
+
rescue => e
|
267
303
|
connection_error
|
268
304
|
end
|
269
305
|
alias :<< :receive
|
@@ -277,6 +313,7 @@ module HTTP2
|
|
277
313
|
# @note all frames are currently delivered in FIFO order.
|
278
314
|
# @param frame [Hash]
|
279
315
|
def send(frame)
|
316
|
+
emit(:frame_sent, frame)
|
280
317
|
if frame[:type] == :data
|
281
318
|
send_data(frame, true)
|
282
319
|
|
@@ -288,7 +325,9 @@ module HTTP2
|
|
288
325
|
goaway(frame[:error])
|
289
326
|
end
|
290
327
|
else
|
291
|
-
|
328
|
+
# HEADERS and PUSH_PROMISE may generate CONTINUATION
|
329
|
+
frames = encode(frame)
|
330
|
+
frames.each {|f| emit(:frame, f) }
|
292
331
|
end
|
293
332
|
end
|
294
333
|
end
|
@@ -296,14 +335,18 @@ module HTTP2
|
|
296
335
|
# Applies HTTP 2.0 binary encoding to the frame.
|
297
336
|
#
|
298
337
|
# @param frame [Hash]
|
299
|
-
# @return [Buffer] encoded frame
|
338
|
+
# @return [Array of Buffer] encoded frame
|
300
339
|
def encode(frame)
|
340
|
+
frames = []
|
341
|
+
|
301
342
|
if frame[:type] == :headers ||
|
302
343
|
frame[:type] == :push_promise
|
303
|
-
encode_headers(frame)
|
344
|
+
frames = encode_headers(frame) # HEADERS and PUSH_PROMISE may create more than one frame
|
345
|
+
else
|
346
|
+
frames = [frame] # otherwise one frame
|
304
347
|
end
|
305
348
|
|
306
|
-
@framer.generate(
|
349
|
+
frames.map {|f| @framer.generate(f) }
|
307
350
|
end
|
308
351
|
|
309
352
|
# Check if frame is a connection frame: SETTINGS, PING, GOAWAY, and any
|
@@ -327,26 +370,25 @@ module HTTP2
|
|
327
370
|
# @param frame [Hash]
|
328
371
|
def connection_management(frame)
|
329
372
|
case @state
|
330
|
-
when :
|
331
|
-
#
|
332
|
-
connection_settings(frame)
|
373
|
+
when :waiting_connection_preface
|
374
|
+
# The first frame MUST be a SETTINGS frame at the start of a connection.
|
333
375
|
@state = :connected
|
376
|
+
connection_settings(frame)
|
334
377
|
|
335
378
|
when :connected
|
336
379
|
case frame[:type]
|
337
380
|
when :settings
|
338
381
|
connection_settings(frame)
|
339
382
|
when :window_update
|
340
|
-
|
341
|
-
@window += frame[:increment]
|
383
|
+
@remote_window += frame[:increment]
|
342
384
|
send_data(nil, true)
|
343
385
|
when :ping
|
344
|
-
if frame[:flags].include? :
|
345
|
-
emit(:
|
386
|
+
if frame[:flags].include? :ack
|
387
|
+
emit(:ack, frame[:payload])
|
346
388
|
else
|
347
389
|
send({
|
348
390
|
type: :ping, stream: 0,
|
349
|
-
flags: [:
|
391
|
+
flags: [:ack], payload: frame[:payload]
|
350
392
|
})
|
351
393
|
end
|
352
394
|
when :goaway
|
@@ -355,7 +397,8 @@ module HTTP2
|
|
355
397
|
# for new streams.
|
356
398
|
@state = :closed
|
357
399
|
emit(:goaway, frame[:last_stream], frame[:error], frame[:payload])
|
358
|
-
|
400
|
+
when :altsvc, :blocked
|
401
|
+
emit(frame[:type], frame)
|
359
402
|
else
|
360
403
|
connection_error
|
361
404
|
end
|
@@ -364,7 +407,60 @@ module HTTP2
|
|
364
407
|
end
|
365
408
|
end
|
366
409
|
|
367
|
-
#
|
410
|
+
# Validate settings parameters. See sepc Section 6.5.2.
|
411
|
+
#
|
412
|
+
# @param role [Symbol] The sender's role: :client or :server
|
413
|
+
# @return nil if no error. Exception object in case of any error.
|
414
|
+
def validate_settings(role, settings)
|
415
|
+
settings.each do |key,v|
|
416
|
+
case key
|
417
|
+
when :settings_header_table_size
|
418
|
+
# Any value is valid
|
419
|
+
when :settings_enable_push
|
420
|
+
case role
|
421
|
+
when :server
|
422
|
+
# Section 8.2
|
423
|
+
# Clients MUST reject any attempt to change the
|
424
|
+
# SETTINGS_ENABLE_PUSH setting to a value other than 0 by treating the
|
425
|
+
# message as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
426
|
+
unless v == 0
|
427
|
+
return ProtocolError.new("invalid #{key} value")
|
428
|
+
end
|
429
|
+
when :client
|
430
|
+
# Any value other than 0 or 1 MUST be treated as a
|
431
|
+
# connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
432
|
+
unless v == 0 || v == 1
|
433
|
+
return ProtocolError.new("invalid #{key} value")
|
434
|
+
end
|
435
|
+
end
|
436
|
+
when :settings_max_concurrent_streams
|
437
|
+
# Any value is valid
|
438
|
+
when :settings_initial_window_size
|
439
|
+
# Values above the maximum flow control window size of 2^31-1 MUST
|
440
|
+
# be treated as a connection error (Section 5.4.1) of type
|
441
|
+
# FLOW_CONTROL_ERROR.
|
442
|
+
unless v <= 0x7fffffff
|
443
|
+
return FlowControlError.new("invalid #{key} value")
|
444
|
+
end
|
445
|
+
when :settings_max_frame_size
|
446
|
+
# The initial value is 2^14 (16,384) octets. The value advertised
|
447
|
+
# by an endpoint MUST be between this initial value and the maximum
|
448
|
+
# allowed frame size (2^24-1 or 16,777,215 octets), inclusive.
|
449
|
+
# Values outside this range MUST be treated as a connection error
|
450
|
+
# (Section 5.4.1) of type PROTOCOL_ERROR.
|
451
|
+
unless 16384 <= v && v <= 16777215
|
452
|
+
return ProtocolError.new("invalid #{key} value")
|
453
|
+
end
|
454
|
+
when :settings_max_header_list_size
|
455
|
+
# Any value is valid
|
456
|
+
else
|
457
|
+
# ignore unknown settings
|
458
|
+
end
|
459
|
+
end
|
460
|
+
nil
|
461
|
+
end
|
462
|
+
|
463
|
+
# Update connection settings based on parameters set by the peer.
|
368
464
|
#
|
369
465
|
# @param frame [Hash]
|
370
466
|
def connection_settings(frame)
|
@@ -372,35 +468,85 @@ module HTTP2
|
|
372
468
|
connection_error
|
373
469
|
end
|
374
470
|
|
375
|
-
|
471
|
+
# Apply settings.
|
472
|
+
# side =
|
473
|
+
# local: previously sent and pended our settings should be effective
|
474
|
+
# remote: just received peer settings should immediately be effective
|
475
|
+
settings, side = \
|
476
|
+
if frame[:flags].include?(:ack)
|
477
|
+
# Process pending settings we have sent.
|
478
|
+
[@pending_settings.shift, :local]
|
479
|
+
else
|
480
|
+
check = validate_settings(@remote_role, frame[:payload])
|
481
|
+
check and connection_error(check)
|
482
|
+
[frame[:payload], :remote]
|
483
|
+
end
|
484
|
+
|
485
|
+
settings.each do |key,v|
|
486
|
+
case side
|
487
|
+
when :local
|
488
|
+
@local_settings[key] = v
|
489
|
+
when :remote
|
490
|
+
@remote_settings[key] = v
|
491
|
+
end
|
492
|
+
|
376
493
|
case key
|
377
494
|
when :settings_max_concurrent_streams
|
378
|
-
|
495
|
+
# Do nothing.
|
496
|
+
# The value controls at the next attempt of stream creation.
|
379
497
|
|
380
|
-
# A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the available
|
381
|
-
# space in a flow control window to become negative. A sender MUST
|
382
|
-
# track the negative flow control window, and MUST NOT send new flow
|
383
|
-
# controlled frames until it receives WINDOW_UPDATE frames that cause
|
384
|
-
# the flow control window to become positive.
|
385
498
|
when :settings_initial_window_size
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
499
|
+
# A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the available
|
500
|
+
# space in a flow control window to become negative. A sender MUST
|
501
|
+
# track the negative flow control window, and MUST NOT send new flow
|
502
|
+
# controlled frames until it receives WINDOW_UPDATE frames that cause
|
503
|
+
# the flow control window to become positive.
|
504
|
+
case side
|
505
|
+
when :local
|
506
|
+
@local_window = @local_window - @local_window_limit + v
|
507
|
+
@streams.each do |id, stream|
|
508
|
+
stream.emit(:local_window, stream.local_window - @local_window_limit + v)
|
509
|
+
end
|
391
510
|
|
392
|
-
|
511
|
+
@local_window_limit = v
|
512
|
+
when :remote
|
513
|
+
@remote_window = @remote_window - @remote_window_limit + v
|
514
|
+
@streams.each do |id, stream|
|
515
|
+
# Event name is :window, not :remote_window
|
516
|
+
stream.emit(:window, stream.remote_window - @remote_window_limit + v)
|
517
|
+
end
|
393
518
|
|
394
|
-
|
395
|
-
|
396
|
-
# of flow control. An implementation that does not wish to perform
|
397
|
-
# flow control can use this in the initial SETTINGS exchange.
|
398
|
-
when :settings_flow_control_options
|
399
|
-
flow_control_allowed?
|
519
|
+
@remote_window_limit = v
|
520
|
+
end
|
400
521
|
|
401
|
-
|
402
|
-
|
522
|
+
when :settings_header_table_size
|
523
|
+
# Setting header table size might cause some headers evicted
|
524
|
+
case side
|
525
|
+
when :local
|
526
|
+
@decompressor.set_table_size(v)
|
527
|
+
when :remote
|
528
|
+
@compressor.set_table_size(v)
|
403
529
|
end
|
530
|
+
|
531
|
+
when :settings_enable_push
|
532
|
+
# nothing to do
|
533
|
+
|
534
|
+
when :settings_max_frame_size
|
535
|
+
# nothing to do
|
536
|
+
|
537
|
+
else
|
538
|
+
# ignore unknown settings
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
case side
|
543
|
+
when :local
|
544
|
+
# Received a settings_ack. Notify application layer.
|
545
|
+
emit(:settings_ack, frame, @pending_settings.size)
|
546
|
+
when :remote
|
547
|
+
if @state != :closed
|
548
|
+
# Send ack to peer
|
549
|
+
send({type: :settings, stream: 0, payload: [], flags: [:ack]})
|
404
550
|
end
|
405
551
|
end
|
406
552
|
end
|
@@ -425,21 +571,34 @@ module HTTP2
|
|
425
571
|
# Encode headers payload and update connection compressor state.
|
426
572
|
#
|
427
573
|
# @param frame [Hash]
|
574
|
+
# @return [Array of Frame]
|
428
575
|
def encode_headers(frame)
|
429
|
-
|
430
|
-
|
576
|
+
payload = frame[:payload]
|
577
|
+
unless payload.is_a? String
|
578
|
+
payload = @compressor.encode(payload)
|
431
579
|
end
|
432
580
|
|
433
|
-
|
434
|
-
connection_error(:compression_error, msg: e.message)
|
435
|
-
end
|
581
|
+
frames = []
|
436
582
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
583
|
+
while payload.size > 0
|
584
|
+
cont = frame.dup
|
585
|
+
cont[:type] = :continuation
|
586
|
+
cont[:flags] = []
|
587
|
+
cont[:payload] = payload.slice!(0, @remote_settings[:settings_max_frame_size])
|
588
|
+
frames << cont
|
589
|
+
end
|
590
|
+
if frames.empty?
|
591
|
+
frames = [frame]
|
592
|
+
else
|
593
|
+
frames.first[:type] = frame[:type]
|
594
|
+
frames.first[:flags] = frame[:flags] - [:end_headers]
|
595
|
+
frames.last[:flags] << :end_headers
|
442
596
|
end
|
597
|
+
|
598
|
+
frames
|
599
|
+
|
600
|
+
rescue Exception => e
|
601
|
+
[connection_error(:compression_error, msg: e.message)]
|
443
602
|
end
|
444
603
|
|
445
604
|
# Activates new incoming or outgoing stream and registers appropriate
|
@@ -449,12 +608,12 @@ module HTTP2
|
|
449
608
|
# @param priority [Integer]
|
450
609
|
# @param window [Integer]
|
451
610
|
# @param parent [Stream]
|
452
|
-
def activate_stream(id
|
611
|
+
def activate_stream(id: nil, **args)
|
453
612
|
if @streams.key?(id)
|
454
613
|
connection_error(msg: 'Stream ID already exists')
|
455
614
|
end
|
456
615
|
|
457
|
-
stream = Stream.new(
|
616
|
+
stream = Stream.new({connection: self, id: id}.merge(args))
|
458
617
|
|
459
618
|
# Streams that are in the "open" state, or either of the "half closed"
|
460
619
|
# states count toward the maximum number of streams that an endpoint is
|