http-2 1.0.2 → 1.1.0
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.
- checksums.yaml +4 -4
- data/lib/http/2/client.rb +1 -0
- data/lib/http/2/connection.rb +116 -96
- data/lib/http/2/emitter.rb +2 -9
- data/lib/http/2/extensions.rb +21 -1
- data/lib/http/2/flow_buffer.rb +45 -35
- data/lib/http/2/framer.rb +113 -85
- data/lib/http/2/header/compressor.rb +47 -31
- data/lib/http/2/header/decompressor.rb +27 -20
- data/lib/http/2/header/encoding_context.rb +77 -80
- data/lib/http/2/header/huffman.rb +14 -10
- data/lib/http/2/header/huffman_statemachine.rb +1 -1
- data/lib/http/2/server.rb +7 -1
- data/lib/http/2/stream.rb +9 -4
- data/lib/http/2/version.rb +1 -1
- data/lib/http/2.rb +5 -0
- data/sig/{next.rbs → 2.rbs} +29 -18
- data/sig/client.rbs +2 -0
- data/sig/connection.rbs +20 -8
- data/sig/emitter.rbs +2 -4
- data/sig/extensions.rbs +3 -1
- data/sig/flow_buffer.rbs +7 -5
- data/sig/frame_buffer.rbs +1 -1
- data/sig/framer.rbs +5 -0
- data/sig/header/compressor.rbs +6 -4
- data/sig/header/decompressor.rbs +3 -2
- data/sig/header/encoding_context.rbs +24 -6
- data/sig/header/huffman.rbs +19 -3
- data/sig/header.rbs +11 -8
- data/sig/stream.rbs +8 -5
- metadata +4 -7
@@ -13,6 +13,8 @@ module HTTP2
|
|
13
13
|
include Error
|
14
14
|
include BufferUtils
|
15
15
|
|
16
|
+
FORBIDDEN_HEADERS = %w[connection te].freeze
|
17
|
+
|
16
18
|
# @param options [Hash] decoding options. Only :table_size is effective.
|
17
19
|
def initialize(options = {})
|
18
20
|
@cc = EncodingContext.new(options)
|
@@ -30,17 +32,23 @@ module HTTP2
|
|
30
32
|
# @param n [Integer] number of available bits
|
31
33
|
# @return [Integer]
|
32
34
|
def integer(buf, n)
|
33
|
-
limit = (
|
35
|
+
limit = (1 << n) - 1
|
34
36
|
i = n.zero? ? 0 : (shift_byte(buf) & limit)
|
35
37
|
|
36
38
|
m = 0
|
37
39
|
if i == limit
|
38
|
-
|
40
|
+
offset = 0
|
41
|
+
|
42
|
+
buf.each_byte.with_index do |byte, idx|
|
43
|
+
offset = idx
|
44
|
+
# while (byte = shift_byte(buf))
|
39
45
|
i += ((byte & 127) << m)
|
40
46
|
m += 7
|
41
47
|
|
42
48
|
break if byte.nobits?(128)
|
43
49
|
end
|
50
|
+
|
51
|
+
read_str(buf, offset + 1)
|
44
52
|
end
|
45
53
|
|
46
54
|
i
|
@@ -59,7 +67,7 @@ module HTTP2
|
|
59
67
|
str = read_str(buf, len)
|
60
68
|
raise CompressionError, "string too short" unless str.bytesize == len
|
61
69
|
|
62
|
-
str = Huffman.
|
70
|
+
str = Huffman.decode(str) if huffman
|
63
71
|
str.force_encoding(Encoding::UTF_8)
|
64
72
|
end
|
65
73
|
|
@@ -70,37 +78,36 @@ module HTTP2
|
|
70
78
|
def header(buf)
|
71
79
|
peek = buf.getbyte(0)
|
72
80
|
|
73
|
-
|
74
|
-
header[:type], type = HEADREP.find do |_t, desc|
|
81
|
+
header_type, type = HEADREP.find do |_, desc|
|
75
82
|
mask = (peek >> desc[:prefix]) << desc[:prefix]
|
76
83
|
mask == desc[:pattern]
|
77
84
|
end
|
78
85
|
|
79
|
-
raise CompressionError unless
|
86
|
+
raise CompressionError unless header_type && type
|
80
87
|
|
81
|
-
|
88
|
+
header_name = integer(buf, type[:prefix])
|
82
89
|
|
83
|
-
case
|
90
|
+
case header_type
|
84
91
|
when :indexed
|
85
|
-
raise CompressionError if
|
92
|
+
raise CompressionError if header_name.zero?
|
93
|
+
|
94
|
+
header_name -= 1
|
86
95
|
|
87
|
-
|
96
|
+
{ type: header_type, name: header_name }
|
88
97
|
when :changetablesize
|
89
|
-
|
98
|
+
{ type: header_type, name: header_name, value: header_name }
|
90
99
|
else
|
91
|
-
if
|
92
|
-
|
100
|
+
if header_name.zero?
|
101
|
+
header_name = string(buf)
|
93
102
|
else
|
94
|
-
|
103
|
+
header_name -= 1
|
95
104
|
end
|
96
|
-
|
97
|
-
end
|
105
|
+
header_value = string(buf)
|
98
106
|
|
99
|
-
|
107
|
+
{ type: header_type, name: header_name, value: header_value }
|
108
|
+
end
|
100
109
|
end
|
101
110
|
|
102
|
-
FORBIDDEN_HEADERS = %w[connection te].freeze
|
103
|
-
|
104
111
|
# Decodes and processes header commands within provided buffer.
|
105
112
|
#
|
106
113
|
# @param buf [Buffer]
|
@@ -114,7 +121,7 @@ module HTTP2
|
|
114
121
|
field, value = @cc.process(header(buf))
|
115
122
|
next if field.nil?
|
116
123
|
|
117
|
-
is_pseudo_header = field.start_with?
|
124
|
+
is_pseudo_header = field.start_with?(":")
|
118
125
|
if !decoding_pseudo_headers && is_pseudo_header
|
119
126
|
raise ProtocolError, "one or more pseudo headers encountered after regular headers"
|
120
127
|
end
|
@@ -86,6 +86,16 @@ module HTTP2
|
|
86
86
|
|
87
87
|
STATIC_TABLE_SIZE = STATIC_TABLE.size
|
88
88
|
|
89
|
+
DEFAULT_OPTIONS = {
|
90
|
+
huffman: :shorter,
|
91
|
+
index: :all,
|
92
|
+
table_size: 4096
|
93
|
+
}.freeze
|
94
|
+
|
95
|
+
STATIC_ALL = %i[all static].freeze
|
96
|
+
|
97
|
+
STATIC_NEVER = %i[never static].freeze
|
98
|
+
|
89
99
|
# Current table of header key-value pairs.
|
90
100
|
attr_reader :table
|
91
101
|
|
@@ -96,6 +106,9 @@ module HTTP2
|
|
96
106
|
# :index Symbol :all, :static, :never
|
97
107
|
attr_reader :options
|
98
108
|
|
109
|
+
# Current table size in octets
|
110
|
+
attr_reader :current_table_size
|
111
|
+
|
99
112
|
# Initializes compression context with appropriate client/server
|
100
113
|
# defaults and maximum size of the dynamic table.
|
101
114
|
#
|
@@ -104,15 +117,11 @@ module HTTP2
|
|
104
117
|
# :huffman Symbol :always, :never, :shorter
|
105
118
|
# :index Symbol :all, :static, :never
|
106
119
|
def initialize(options = {})
|
107
|
-
default_options = {
|
108
|
-
huffman: :shorter,
|
109
|
-
index: :all,
|
110
|
-
table_size: 4096
|
111
|
-
}
|
112
120
|
@table = []
|
113
|
-
@options =
|
121
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
114
122
|
@limit = @options[:table_size]
|
115
123
|
@_table_updated = false
|
124
|
+
@current_table_size = 0
|
116
125
|
end
|
117
126
|
|
118
127
|
# Duplicates current compression context
|
@@ -140,10 +149,13 @@ module HTTP2
|
|
140
149
|
# @return [Array] +[key, value]+
|
141
150
|
def dereference(index)
|
142
151
|
# NOTE: index is zero-based in this module.
|
143
|
-
|
144
|
-
|
152
|
+
return STATIC_TABLE[index] if index < STATIC_TABLE_SIZE
|
153
|
+
|
154
|
+
idx = index - STATIC_TABLE_SIZE
|
155
|
+
|
156
|
+
raise CompressionError, "Index too large" if idx >= @table.size
|
145
157
|
|
146
|
-
|
158
|
+
@table[index - STATIC_TABLE_SIZE]
|
147
159
|
end
|
148
160
|
|
149
161
|
# Header Block Processing
|
@@ -153,9 +165,11 @@ module HTTP2
|
|
153
165
|
# @return [Array, nil] +[name, value]+ header field that is added to the decoded header list,
|
154
166
|
# or nil if +cmd[:type]+ is +:changetablesize+
|
155
167
|
def process(cmd)
|
156
|
-
|
168
|
+
type = cmd[:type]
|
169
|
+
name = cmd[:name]
|
170
|
+
value = cmd[:value]
|
157
171
|
|
158
|
-
case
|
172
|
+
case type
|
159
173
|
when :changetablesize
|
160
174
|
raise CompressionError, "tried to change table size after adding elements to table" if @_table_updated
|
161
175
|
|
@@ -163,21 +177,18 @@ module HTTP2
|
|
163
177
|
# we should blow up if we receive another frame where the new table size is bigger.
|
164
178
|
table_size_updated = @limit != @options[:table_size]
|
165
179
|
|
166
|
-
raise CompressionError, "dynamic table size update exceed limit" if !table_size_updated &&
|
180
|
+
raise CompressionError, "dynamic table size update exceed limit" if !table_size_updated && value > @limit
|
167
181
|
|
168
|
-
self.table_size =
|
182
|
+
self.table_size = value
|
169
183
|
|
184
|
+
nil
|
170
185
|
when :indexed
|
171
186
|
# Indexed Representation
|
172
187
|
# An _indexed representation_ entails the following actions:
|
173
188
|
# o The header field corresponding to the referenced entry in either
|
174
189
|
# the static table or dynamic table is added to the decoded header
|
175
190
|
# list.
|
176
|
-
|
177
|
-
|
178
|
-
k, v = dereference(idx)
|
179
|
-
emit = [k, v]
|
180
|
-
|
191
|
+
dereference(name)
|
181
192
|
when :incremental, :noindex, :neverindexed
|
182
193
|
# A _literal representation_ that is _not added_ to the dynamic table
|
183
194
|
# entails the following action:
|
@@ -188,27 +199,28 @@ module HTTP2
|
|
188
199
|
# o The header field is added to the decoded header list.
|
189
200
|
# o The header field is inserted at the beginning of the dynamic table.
|
190
201
|
|
191
|
-
case
|
202
|
+
case name
|
192
203
|
when Integer
|
193
|
-
|
204
|
+
name, v = dereference(name)
|
194
205
|
|
195
|
-
|
196
|
-
cmd[:index] ||= cmd[:name]
|
197
|
-
cmd[:value] ||= v
|
198
|
-
cmd[:name] = k
|
206
|
+
value ||= v
|
199
207
|
when UPPER
|
200
|
-
raise ProtocolError, "Invalid uppercase key: #{
|
208
|
+
raise ProtocolError, "Invalid uppercase key: #{name}"
|
201
209
|
end
|
202
210
|
|
203
|
-
emit = [
|
211
|
+
emit = [name, value]
|
204
212
|
|
205
|
-
|
213
|
+
# add to table
|
214
|
+
if type == :incremental && size_check(name.bytesize + value.bytesize + 32)
|
215
|
+
@table.unshift(emit)
|
216
|
+
@current_table_size += name.bytesize + value.bytesize + 32
|
217
|
+
@_table_updated = true
|
218
|
+
end
|
206
219
|
|
220
|
+
emit
|
207
221
|
else
|
208
|
-
raise CompressionError, "Invalid type: #{
|
222
|
+
raise CompressionError, "Invalid type: #{type}"
|
209
223
|
end
|
210
|
-
|
211
|
-
emit
|
212
224
|
end
|
213
225
|
|
214
226
|
# Plan header compression according to +@options [:index]+
|
@@ -219,9 +231,9 @@ module HTTP2
|
|
219
231
|
# @param headers [Array] +[[name, value], ...]+
|
220
232
|
# @return [Array] array of commands
|
221
233
|
def encode(headers)
|
222
|
-
commands = []
|
223
234
|
# Literals commands are marked with :noindex when index is not used
|
224
|
-
noindex =
|
235
|
+
noindex = STATIC_NEVER.include?(@options[:index])
|
236
|
+
|
225
237
|
headers.each do |field, value|
|
226
238
|
# Literal header names MUST be translated to lowercase before
|
227
239
|
# encoding and transmission.
|
@@ -229,10 +241,9 @@ module HTTP2
|
|
229
241
|
value = "/" if field == ":path" && value.empty?
|
230
242
|
cmd = addcmd(field, value)
|
231
243
|
cmd[:type] = :noindex if noindex && cmd[:type] == :incremental
|
232
|
-
commands << cmd
|
233
244
|
process(cmd)
|
245
|
+
yield cmd
|
234
246
|
end
|
235
|
-
commands
|
236
247
|
end
|
237
248
|
|
238
249
|
# Emits command for a header.
|
@@ -245,30 +256,36 @@ module HTTP2
|
|
245
256
|
# :static Use static table only.
|
246
257
|
# :all Use all of them.
|
247
258
|
#
|
248
|
-
# @param
|
259
|
+
# @param field [String] the header field
|
260
|
+
# @param value [String] the header value
|
249
261
|
# @return [Hash] command
|
250
|
-
def addcmd(
|
262
|
+
def addcmd(field, value)
|
263
|
+
# @type var exact: Integer?
|
251
264
|
exact = nil
|
265
|
+
# @type var name_only: Integer?
|
252
266
|
name_only = nil
|
253
267
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
268
|
+
index_type = @options[:index]
|
269
|
+
|
270
|
+
if STATIC_ALL.include?(index_type) &&
|
271
|
+
STATIC_TABLE_BY_FIELD.key?(field)
|
272
|
+
STATIC_TABLE_BY_FIELD[field].each do |i, svalue|
|
273
|
+
name_only ||= i
|
274
|
+
if value == svalue
|
275
|
+
exact = i
|
276
|
+
break
|
263
277
|
end
|
264
278
|
end
|
265
279
|
end
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
280
|
+
|
281
|
+
if index_type == :all && !exact
|
282
|
+
@table.each_with_index do |(hfield, hvalue), i|
|
283
|
+
next unless field == hfield
|
284
|
+
|
285
|
+
if value == hvalue
|
286
|
+
exact = i + STATIC_TABLE_SIZE
|
270
287
|
break
|
271
|
-
|
288
|
+
else
|
272
289
|
name_only ||= i + STATIC_TABLE_SIZE
|
273
290
|
end
|
274
291
|
end
|
@@ -276,10 +293,8 @@ module HTTP2
|
|
276
293
|
|
277
294
|
if exact
|
278
295
|
{ name: exact, type: :indexed }
|
279
|
-
elsif name_only
|
280
|
-
{ name: name_only, value: header.last, type: :incremental }
|
281
296
|
else
|
282
|
-
{ name:
|
297
|
+
{ name: name_only || field, value: value, type: :incremental }
|
283
298
|
end
|
284
299
|
end
|
285
300
|
|
@@ -287,13 +302,7 @@ module HTTP2
|
|
287
302
|
# When the size is reduced, some headers might be evicted.
|
288
303
|
def table_size=(size)
|
289
304
|
@limit = size
|
290
|
-
size_check(
|
291
|
-
end
|
292
|
-
|
293
|
-
# Returns current table size in octets
|
294
|
-
# @return [Integer]
|
295
|
-
def current_table_size
|
296
|
-
@table.inject(0) { |r, (k, v)| r + k.bytesize + v.bytesize + 32 }
|
305
|
+
size_check(0)
|
297
306
|
end
|
298
307
|
|
299
308
|
def listen_on_table
|
@@ -304,32 +313,20 @@ module HTTP2
|
|
304
313
|
|
305
314
|
private
|
306
315
|
|
307
|
-
# Add a name-value pair to the dynamic table.
|
308
|
-
# Older entries might have been evicted so that
|
309
|
-
# the new entry fits in the dynamic table.
|
310
|
-
#
|
311
|
-
# @param cmd [Array] +[name, value]+
|
312
|
-
def add_to_table(cmd)
|
313
|
-
return unless size_check(cmd)
|
314
|
-
|
315
|
-
@table.unshift(cmd)
|
316
|
-
@_table_updated = true
|
317
|
-
end
|
318
|
-
|
319
316
|
# To keep the dynamic table size lower than or equal to @limit,
|
320
317
|
# remove one or more entries at the end of the dynamic table.
|
321
318
|
#
|
322
|
-
# @param
|
319
|
+
# @param cmdsize [Integer]
|
323
320
|
# @return [Boolean] whether +cmd+ fits in the dynamic table.
|
324
|
-
def size_check(
|
325
|
-
|
326
|
-
|
321
|
+
def size_check(cmdsize)
|
322
|
+
unless @table.empty?
|
323
|
+
while @current_table_size + cmdsize > @limit
|
327
324
|
|
328
|
-
|
329
|
-
|
325
|
+
name, value = @table.pop
|
326
|
+
@current_table_size -= name.bytesize + value.bytesize + 32
|
327
|
+
break if @table.empty?
|
330
328
|
|
331
|
-
|
332
|
-
cursize -= e[0].bytesize + e[1].bytesize + 32
|
329
|
+
end
|
333
330
|
end
|
334
331
|
|
335
332
|
cmdsize <= @limit
|
@@ -9,9 +9,12 @@ module HTTP2
|
|
9
9
|
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10
|
10
10
|
module Header
|
11
11
|
# Huffman encoder/decoder
|
12
|
-
|
12
|
+
module Huffman
|
13
|
+
module_function
|
14
|
+
|
13
15
|
include Error
|
14
|
-
|
16
|
+
extend PackingExtensions
|
17
|
+
extend BufferUtils
|
15
18
|
|
16
19
|
BITS_AT_ONCE = 4
|
17
20
|
EOS = 256
|
@@ -21,11 +24,13 @@ module HTTP2
|
|
21
24
|
# Length is not encoded in this method.
|
22
25
|
#
|
23
26
|
# @param str [String]
|
27
|
+
# @param buffer [String]
|
24
28
|
# @return [String] binary string
|
25
|
-
def encode(str)
|
26
|
-
bitstring = str.
|
27
|
-
|
28
|
-
|
29
|
+
def encode(str, buffer = "".b)
|
30
|
+
bitstring = String.new("", encoding: Encoding::BINARY, capacity: (str.bytesize * 30) + ((8 - str.size) % 8))
|
31
|
+
str.each_byte { |chr| append_str(bitstring, ENCODE_TABLE[chr]) }
|
32
|
+
append_str(bitstring, ("1" * ((8 - bitstring.size) % 8)))
|
33
|
+
pack([bitstring], "B*", buffer: buffer)
|
29
34
|
end
|
30
35
|
|
31
36
|
# Decodes provided Huffman coded string.
|
@@ -46,11 +51,10 @@ module HTTP2
|
|
46
51
|
# Each transition is [emit, next]
|
47
52
|
# [emit] character to be emitted on this transition, empty string, or EOS.
|
48
53
|
# [next] next state number.
|
49
|
-
|
50
|
-
raise CompressionError, "Huffman decode error (EOS found)" if
|
54
|
+
first, state = MACHINE.dig(state, branch)
|
55
|
+
raise CompressionError, "Huffman decode error (EOS found)" if first == EOS
|
51
56
|
|
52
|
-
emit
|
53
|
-
state = trans.last
|
57
|
+
append_str(emit, first.chr) if first
|
54
58
|
end
|
55
59
|
end
|
56
60
|
# Check whether partial input is correctly filled
|
data/lib/http/2/server.rb
CHANGED
@@ -79,7 +79,7 @@ module HTTP2
|
|
79
79
|
|
80
80
|
# Process received HTTP2-Settings payload
|
81
81
|
buf = "".b
|
82
|
-
buf
|
82
|
+
append_str(buf, Base64.urlsafe_decode64(settings.to_s))
|
83
83
|
@framer.common_header(
|
84
84
|
{
|
85
85
|
length: buf.bytesize,
|
@@ -120,6 +120,12 @@ module HTTP2
|
|
120
120
|
@state = :waiting_magic
|
121
121
|
end
|
122
122
|
|
123
|
+
def activate_stream(**)
|
124
|
+
super.tap do |stream|
|
125
|
+
stream.on(:promise, &method(:promise))
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
123
129
|
def origin_set=(origins)
|
124
130
|
@origin_set = Array(origins).map(&:to_s)
|
125
131
|
@origins_sent = @origin_set.empty?
|
data/lib/http/2/stream.rb
CHANGED
@@ -40,6 +40,8 @@ module HTTP2
|
|
40
40
|
include Emitter
|
41
41
|
include Error
|
42
42
|
|
43
|
+
STREAM_OPEN_STATES = %i[open half_closed_local half_closing closing].freeze
|
44
|
+
|
43
45
|
# Stream ID (odd for client initiated streams, even otherwise).
|
44
46
|
attr_reader :id
|
45
47
|
|
@@ -80,6 +82,11 @@ module HTTP2
|
|
80
82
|
@id = id
|
81
83
|
@weight = weight
|
82
84
|
@dependency = dependency
|
85
|
+
|
86
|
+
# from mixins
|
87
|
+
@listeners = Hash.new { |hash, key| hash[key] = [] }
|
88
|
+
@send_buffer = FrameBuffer.new
|
89
|
+
|
83
90
|
process_priority(weight: weight, dependency: dependency, exclusive: exclusive)
|
84
91
|
@local_window_max_size = connection.local_settings[:settings_initial_window_size]
|
85
92
|
@local_window = connection.local_settings[:settings_initial_window_size]
|
@@ -88,7 +95,7 @@ module HTTP2
|
|
88
95
|
@state = state
|
89
96
|
@error = false
|
90
97
|
@closed = false
|
91
|
-
@_method = @_content_length = @_status_code = nil
|
98
|
+
@_method = @_content_length = @_status_code = @_trailers = nil
|
92
99
|
@_waiting_on_trailers = false
|
93
100
|
@received_data = false
|
94
101
|
@activated = false
|
@@ -113,9 +120,7 @@ module HTTP2
|
|
113
120
|
# If a DATA frame is received whose stream is not in "open" or
|
114
121
|
# "half closed (local)" state, the recipient MUST respond with a
|
115
122
|
# stream error (Section 5.4.2) of type STREAM_CLOSED.
|
116
|
-
stream_error(:stream_closed) unless @state
|
117
|
-
@state == :half_closed_local ||
|
118
|
-
@state == :half_closing || @state == :closing ||
|
123
|
+
stream_error(:stream_closed) unless STREAM_OPEN_STATES.include?(@state) ||
|
119
124
|
(@state == :closed && @closed == :local_rst)
|
120
125
|
@received_data = true
|
121
126
|
calculate_content_length(frame[:length])
|
data/lib/http/2/version.rb
CHANGED
data/lib/http/2.rb
CHANGED
data/sig/{next.rbs → 2.rbs}
RENAMED
@@ -7,7 +7,18 @@ module HTTP2
|
|
7
7
|
|
8
8
|
DEFAULT_MAX_CONCURRENT_STREAMS: Integer
|
9
9
|
|
10
|
-
|
10
|
+
EMPTY: []
|
11
|
+
|
12
|
+
type connection_opts = Hash[Symbol, untyped]
|
13
|
+
|
14
|
+
type settings_hash = {
|
15
|
+
settings_header_table_size: Integer,
|
16
|
+
settings_enable_push: Integer,
|
17
|
+
settings_max_concurrent_streams: Integer,
|
18
|
+
settings_initial_window_size: Integer,
|
19
|
+
settings_max_frame_size: Integer,
|
20
|
+
settings_max_header_list_size: Integer
|
21
|
+
}
|
11
22
|
|
12
23
|
type settings_ary = Array[settings_enum]
|
13
24
|
|
@@ -30,44 +41,44 @@ module HTTP2
|
|
30
41
|
# # FRAMES
|
31
42
|
type frame_control_flags = Array[:end_headers | :end_stream]
|
32
43
|
|
44
|
+
type common_frame = { stream: Integer }
|
45
|
+
|
33
46
|
# # HEADERS
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
47
|
+
type headers_frame = common_frame & {
|
48
|
+
type: :headers, flags: frame_control_flags, payload: Enumerable[header_pair] | String,
|
49
|
+
?method: Symbol, ?trailer: Array[String], ?content_length: Integer, ?padding: Integer
|
50
|
+
}
|
38
51
|
|
39
52
|
# # DATA
|
40
|
-
type data_frame = { type: :data, flags: frame_control_flags,
|
41
|
-
| { type: :data, flags: frame_control_flags, stream: Integer, length: Integer, payload: String }
|
42
|
-
| { type: :data, flags: frame_control_flags, payload: String }
|
53
|
+
type data_frame = { type: :data, flags: frame_control_flags, ?length: Integer, payload: String, ?padding: Integer }
|
43
54
|
|
44
55
|
# # PUSH_PROMISE
|
45
|
-
|
56
|
+
type push_promise_frame = { type: :push_promise, promise_stream: Integer, flags: frame_control_flags, ?method: Symbol, ?trailer: Array[String], ?content_length: Integer, payload: Enumerable[header_pair], ?padding: Integer }
|
46
57
|
|
47
58
|
# # SETTINGS
|
48
|
-
|
59
|
+
type settings_frame = { type: :settings, payload: Array[[Symbol | Integer, Integer]] }
|
49
60
|
|
50
61
|
# # WINDOW_UPDATE
|
51
|
-
|
62
|
+
type window_update_frame = { type: :window_update, increment: Integer }
|
52
63
|
|
53
64
|
# # PRIORITY
|
54
|
-
type priority_frame = {
|
65
|
+
type priority_frame = { dependency: Integer, exclusive: bool, weight: Integer }
|
55
66
|
|
56
67
|
# # ALTSVC
|
57
|
-
|
68
|
+
type altsvc_frame = { type: :altsvc, max_age: Integer, port: Integer, proto: "String", host: String }
|
58
69
|
|
59
70
|
# # ORIGIN
|
60
|
-
|
71
|
+
type origin_frame = { type: :origin, origin: Array[String] }
|
61
72
|
|
62
73
|
# # PING
|
63
|
-
|
74
|
+
type ping_frame = { type: :ping, payload: String, length: Integer }
|
64
75
|
|
65
76
|
# # GOAWAY
|
66
|
-
|
77
|
+
type goaway_frame = { type: :goaway, last_stream: Integer, error: Symbol? }
|
67
78
|
|
68
|
-
# type frame = headers_frame | data_frame | push_promise_frame |
|
79
|
+
# type frame = common_frame & (headers_frame | data_frame | push_promise_frame |
|
69
80
|
# settings_frame | window_update_frame | priority_frame | altsvc_frame |
|
70
|
-
# origin_frame | ping_frame | goaway_frame
|
81
|
+
# origin_frame | ping_frame | goaway_frame)
|
71
82
|
|
72
83
|
type frame_key = :type | :flags | :stream | :padding | :ignore |
|
73
84
|
# headers
|