http-2-next 0.2.6 → 0.3.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/README.md +1 -1
- data/lib/http/2/next.rb +1 -3
- data/lib/http/2/next/buffer.rb +7 -1
- data/lib/http/2/next/connection.rb +27 -16
- data/lib/http/2/next/emitter.rb +2 -3
- data/lib/http/2/next/error.rb +1 -0
- data/lib/http/2/next/extensions.rb +11 -1
- data/lib/http/2/next/flow_buffer.rb +1 -0
- data/lib/http/2/next/framer.rb +12 -10
- data/lib/http/2/next/header.rb +35 -0
- data/lib/http/2/next/header/compressor.rb +137 -0
- data/lib/http/2/next/header/decompressor.rb +141 -0
- data/lib/http/2/next/{compressor.rb → header/encoding_context.rb} +7 -298
- data/lib/http/2/next/{huffman.rb → header/huffman.rb} +5 -2
- data/lib/http/2/next/{huffman_statemachine.rb → header/huffman_statemachine.rb} +0 -0
- data/lib/http/2/next/server.rb +6 -4
- data/lib/http/2/next/stream.rb +3 -4
- data/lib/http/2/next/version.rb +1 -1
- data/lib/tasks/generate_huffman_table.rb +4 -4
- data/sig/buffer.rbs +21 -0
- data/sig/client.rbs +9 -0
- data/sig/connection.rbs +73 -0
- data/sig/emitter.rbs +13 -0
- data/sig/flow_buffer.rbs +23 -0
- data/sig/frame_buffer.rbs +13 -0
- data/sig/framer.rbs +25 -0
- data/sig/header.rbs +15 -0
- data/sig/header/compressor.rbs +23 -0
- data/sig/header/decompressor.rbs +22 -0
- data/sig/header/encoding_context.rbs +34 -0
- data/sig/header/huffman.rbs +9 -0
- data/sig/next.rbs +56 -0
- data/sig/server.rbs +12 -0
- data/sig/stream.rbs +77 -0
- metadata +27 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4ce4e3302e32d2c860cf38d25f82186a233ae9bb446bde932d185ca398d4eec
|
4
|
+
data.tar.gz: c2428f11638405fa7c92390745f4c21022627500fe54d564a46f06e83967d126
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a10531d34c3d37989a6de029b4bfdca1b9742b7614a7785179bea4d8794568b58bd88641c259721520f803b1f6577c2e41ddb49bff75564edfbdf9d422124fc
|
7
|
+
data.tar.gz: 8d9d7e74f52d079a7f9021bcd7679bd7544b1e30d17cdd5f42de24326812c46f4c3f8208f3132ba0e9d0df46b6c947238781a48abbf6d1cacc95da69b3227293
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](http://rubygems.org/gems/http-2-next)
|
4
4
|
[](https://gitlab.com/honeyryderchuck/http-2-next/commits/master)
|
5
|
-
[](https://honeyryderchuck.gitlab.io/http-2-next/coverage/#_AllFiles)
|
5
|
+
[](https://honeyryderchuck.gitlab.io/http-2-next/coverage/#_AllFiles)
|
6
6
|
|
7
7
|
**Attention!** This is a fork of the [http-2](https://github.com/igrigorik/http-2) gem.
|
8
8
|
|
data/lib/http/2/next.rb
CHANGED
@@ -6,9 +6,7 @@ require "http/2/next/error"
|
|
6
6
|
require "http/2/next/emitter"
|
7
7
|
require "http/2/next/buffer"
|
8
8
|
require "http/2/next/flow_buffer"
|
9
|
-
require "http/2/next/
|
10
|
-
require "http/2/next/huffman_statemachine"
|
11
|
-
require "http/2/next/compressor"
|
9
|
+
require "http/2/next/header"
|
12
10
|
require "http/2/next/framer"
|
13
11
|
require "http/2/next/connection"
|
14
12
|
require "http/2/next/client"
|
data/lib/http/2/next/buffer.rb
CHANGED
@@ -8,6 +8,8 @@ module HTTP2Next
|
|
8
8
|
class Buffer
|
9
9
|
extend Forwardable
|
10
10
|
|
11
|
+
using StringExtensions
|
12
|
+
|
11
13
|
def_delegators :@buffer, :ord, :encoding, :setbyte, :unpack,
|
12
14
|
:size, :each_byte, :to_str, :to_s, :length, :inspect,
|
13
15
|
:[], :[]=, :empty?, :bytesize, :include?
|
@@ -28,6 +30,10 @@ module HTTP2Next
|
|
28
30
|
Buffer.new(@buffer.slice!(0, n))
|
29
31
|
end
|
30
32
|
|
33
|
+
def unpack1(*arg)
|
34
|
+
@buffer.unpack1(*arg)
|
35
|
+
end
|
36
|
+
|
31
37
|
# Emulate StringIO#getbyte: slice first byte from buffer.
|
32
38
|
def getbyte
|
33
39
|
read(1).ord
|
@@ -61,7 +67,7 @@ module HTTP2Next
|
|
61
67
|
# Slice unsigned 32-bit integer from buffer.
|
62
68
|
# @return [Integer]
|
63
69
|
def read_uint32
|
64
|
-
read(4).
|
70
|
+
read(4).unpack1(UINT32)
|
65
71
|
end
|
66
72
|
|
67
73
|
# Ensures that data that is added is binary encoded as well,
|
@@ -57,12 +57,11 @@ module HTTP2Next
|
|
57
57
|
# Size of current connection flow control window (by default, set to
|
58
58
|
# infinity, but is automatically updated on receipt of peer settings).
|
59
59
|
attr_reader :local_window
|
60
|
-
attr_reader :remote_window
|
60
|
+
attr_reader :remote_window, :remote_settings
|
61
61
|
alias window local_window
|
62
62
|
|
63
63
|
# Current settings value for local and peer
|
64
64
|
attr_reader :local_settings
|
65
|
-
attr_reader :remote_settings
|
66
65
|
|
67
66
|
# Pending settings value
|
68
67
|
# Sent but not ack'ed settings
|
@@ -105,6 +104,7 @@ module HTTP2Next
|
|
105
104
|
|
106
105
|
@h2c_upgrade = nil
|
107
106
|
@closed_since = nil
|
107
|
+
@received_frame = false
|
108
108
|
end
|
109
109
|
|
110
110
|
def closed?
|
@@ -176,7 +176,7 @@ module HTTP2Next
|
|
176
176
|
# @param settings [Array or Hash]
|
177
177
|
def settings(payload)
|
178
178
|
payload = payload.to_a
|
179
|
-
|
179
|
+
validate_settings(@local_role, payload)
|
180
180
|
@pending_settings << payload
|
181
181
|
send(type: :settings, stream: 0, payload: payload)
|
182
182
|
@pending_settings << payload
|
@@ -213,6 +213,11 @@ module HTTP2Next
|
|
213
213
|
end
|
214
214
|
|
215
215
|
while (frame = @framer.parse(@recv_buffer))
|
216
|
+
if is_a?(Client) && !@received_frame
|
217
|
+
connection_error(:protocol_error, msg: "didn't receive settings") if frame[:type] != :settings
|
218
|
+
@received_frame = true
|
219
|
+
end
|
220
|
+
|
216
221
|
# Implementations MUST discard frames
|
217
222
|
# that have unknown or unsupported types.
|
218
223
|
if frame[:type].nil?
|
@@ -521,8 +526,6 @@ module HTTP2Next
|
|
521
526
|
def validate_settings(role, settings)
|
522
527
|
settings.each do |key, v|
|
523
528
|
case key
|
524
|
-
when :settings_header_table_size
|
525
|
-
# Any value is valid
|
526
529
|
when :settings_enable_push
|
527
530
|
case role
|
528
531
|
when :server
|
@@ -530,32 +533,41 @@ module HTTP2Next
|
|
530
533
|
# Clients MUST reject any attempt to change the
|
531
534
|
# SETTINGS_ENABLE_PUSH setting to a value other than 0 by treating the
|
532
535
|
# message as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
533
|
-
|
536
|
+
next if v.zero?
|
537
|
+
|
538
|
+
connection_error(:protocol_error, msg: "invalid #{key} value")
|
534
539
|
when :client
|
535
540
|
# Any value other than 0 or 1 MUST be treated as a
|
536
541
|
# connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
537
|
-
|
542
|
+
next if v.zero? || v == 1
|
543
|
+
|
544
|
+
connection_error(:protocol_error, msg: "invalid #{key} value")
|
538
545
|
end
|
539
|
-
when :settings_max_concurrent_streams
|
540
|
-
# Any value is valid
|
541
546
|
when :settings_initial_window_size
|
542
547
|
# Values above the maximum flow control window size of 2^31-1 MUST
|
543
548
|
# be treated as a connection error (Section 5.4.1) of type
|
544
549
|
# FLOW_CONTROL_ERROR.
|
545
|
-
|
550
|
+
next if v <= 0x7fffffff
|
551
|
+
|
552
|
+
connection_error(:flow_control_error, msg: "invalid #{key} value")
|
546
553
|
when :settings_max_frame_size
|
547
554
|
# The initial value is 2^14 (16,384) octets. The value advertised
|
548
555
|
# by an endpoint MUST be between this initial value and the maximum
|
549
556
|
# allowed frame size (2^24-1 or 16,777,215 octets), inclusive.
|
550
557
|
# Values outside this range MUST be treated as a connection error
|
551
558
|
# (Section 5.4.1) of type PROTOCOL_ERROR.
|
552
|
-
|
553
|
-
|
559
|
+
next if v >= 16_384 && v <= 16_777_215
|
560
|
+
|
561
|
+
connection_error(:protocol_error, msg: "invalid #{key} value")
|
562
|
+
# when :settings_max_concurrent_streams
|
563
|
+
# Any value is valid
|
564
|
+
# when :settings_header_table_size
|
565
|
+
# Any value is valid
|
566
|
+
# when :settings_max_header_list_size
|
554
567
|
# Any value is valid
|
555
568
|
# else # ignore unknown settings
|
556
569
|
end
|
557
570
|
end
|
558
|
-
nil
|
559
571
|
end
|
560
572
|
|
561
573
|
# Update connection settings based on parameters set by the peer.
|
@@ -572,8 +584,7 @@ module HTTP2Next
|
|
572
584
|
# Process pending settings we have sent.
|
573
585
|
[@pending_settings.shift, :local]
|
574
586
|
else
|
575
|
-
|
576
|
-
connection_error(check) if check
|
587
|
+
validate_settings(@remote_role, frame[:payload])
|
577
588
|
[frame[:payload], :remote]
|
578
589
|
end
|
579
590
|
|
@@ -707,7 +718,7 @@ module HTTP2Next
|
|
707
718
|
# @param priority [Integer]
|
708
719
|
# @param window [Integer]
|
709
720
|
# @param parent [Stream]
|
710
|
-
def activate_stream(id
|
721
|
+
def activate_stream(id:, **args)
|
711
722
|
connection_error(msg: "Stream ID already exists") if @streams.key?(id)
|
712
723
|
|
713
724
|
raise StreamLimitExceeded if @active_stream_count >= (@max_streams || @local_settings[:settings_max_concurrent_streams])
|
data/lib/http/2/next/emitter.rb
CHANGED
@@ -9,19 +9,18 @@ module HTTP2Next
|
|
9
9
|
#
|
10
10
|
# @param event [Symbol]
|
11
11
|
# @param block [Proc] callback function
|
12
|
-
def
|
12
|
+
def on(event, &block)
|
13
13
|
raise ArgumentError, "must provide callback" unless block_given?
|
14
14
|
|
15
15
|
listeners(event.to_sym).push block
|
16
16
|
end
|
17
|
-
alias on add_listener
|
18
17
|
|
19
18
|
# Subscribe to next event (at most once) for specified type.
|
20
19
|
#
|
21
20
|
# @param event [Symbol]
|
22
21
|
# @param block [Proc] callback function
|
23
22
|
def once(event, &block)
|
24
|
-
|
23
|
+
on(event) do |*args, &callback|
|
25
24
|
block.call(*args, &callback)
|
26
25
|
:delete
|
27
26
|
end
|
data/lib/http/2/next/error.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HTTP2Next
|
4
|
-
module
|
4
|
+
module RegexpExtensions
|
5
5
|
unless Regexp.method_defined?(:match?)
|
6
6
|
refine Regexp do
|
7
7
|
def match?(*args)
|
@@ -10,4 +10,14 @@ module HTTP2Next
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
13
|
+
|
14
|
+
module StringExtensions
|
15
|
+
unless String.method_defined?(:unpack1)
|
16
|
+
refine String do
|
17
|
+
def unpack1(format)
|
18
|
+
unpack(format).first
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
13
23
|
end
|
data/lib/http/2/next/framer.rb
CHANGED
@@ -4,6 +4,8 @@ module HTTP2Next
|
|
4
4
|
# Performs encoding, decoding, and validation of binary HTTP/2 frames.
|
5
5
|
#
|
6
6
|
class Framer
|
7
|
+
using StringExtensions
|
8
|
+
|
7
9
|
include Error
|
8
10
|
|
9
11
|
# Default value of max frame size (16384 bytes)
|
@@ -154,6 +156,7 @@ module HTTP2Next
|
|
154
156
|
# Decodes common 9-byte header.
|
155
157
|
#
|
156
158
|
# @param buf [Buffer]
|
159
|
+
# @return [Hash] the corresponding frame
|
157
160
|
def read_common_header(buf)
|
158
161
|
frame = {}
|
159
162
|
len_hi, len_lo, type, flags, stream = buf.slice(0, 9).unpack(HEADERPACK)
|
@@ -221,7 +224,7 @@ module HTTP2Next
|
|
221
224
|
raise CompressionError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
222
225
|
|
223
226
|
frame[:payload].each do |(k, v)|
|
224
|
-
if k.is_a? Integer
|
227
|
+
if k.is_a? Integer # rubocop:disable Style/GuardClause
|
225
228
|
DEFINED_SETTINGS.value?(k) || next
|
226
229
|
else
|
227
230
|
k = DEFINED_SETTINGS[k]
|
@@ -333,10 +336,10 @@ module HTTP2Next
|
|
333
336
|
#
|
334
337
|
# @param buf [Buffer]
|
335
338
|
def parse(buf)
|
336
|
-
return
|
339
|
+
return if buf.size < 9
|
337
340
|
|
338
341
|
frame = read_common_header(buf)
|
339
|
-
return
|
342
|
+
return if buf.size < 9 + frame[:length]
|
340
343
|
|
341
344
|
raise ProtocolError, "payload too large" if frame[:length] > @local_max_frame_size
|
342
345
|
|
@@ -353,7 +356,7 @@ module HTTP2Next
|
|
353
356
|
if FRAME_TYPES_WITH_PADDING.include?(frame[:type])
|
354
357
|
padded = frame[:flags].include?(:padded)
|
355
358
|
if padded
|
356
|
-
padlen = payload.read(1).
|
359
|
+
padlen = payload.read(1).unpack1(UINT8)
|
357
360
|
frame[:padding] = padlen + 1
|
358
361
|
raise ProtocolError, "padding too long" if padlen > payload.bytesize
|
359
362
|
|
@@ -395,7 +398,7 @@ module HTTP2Next
|
|
395
398
|
raise ProtocolError, "Invalid stream ID (#{frame[:stream]})" if (frame[:stream]).nonzero?
|
396
399
|
|
397
400
|
(frame[:length] / 6).times do
|
398
|
-
id = payload.read(2).
|
401
|
+
id = payload.read(2).unpack1(UINT16)
|
399
402
|
val = payload.read_uint32
|
400
403
|
|
401
404
|
# Unsupported or unrecognized settings MUST be ignored.
|
@@ -437,7 +440,7 @@ module HTTP2Next
|
|
437
440
|
origins = []
|
438
441
|
|
439
442
|
until payload.empty?
|
440
|
-
len = payload.read(2).
|
443
|
+
len = payload.read(2).unpack1(UINT16)
|
441
444
|
origins << payload.read(len)
|
442
445
|
end
|
443
446
|
|
@@ -452,17 +455,16 @@ module HTTP2Next
|
|
452
455
|
|
453
456
|
def pack_error(e)
|
454
457
|
unless e.is_a? Integer
|
455
|
-
raise CompressionError, "Unknown error ID for #{e}" if DEFINED_ERRORS[e].nil?
|
456
|
-
|
457
458
|
e = DEFINED_ERRORS[e]
|
459
|
+
|
460
|
+
raise CompressionError, "Unknown error ID for #{e}" unless e
|
458
461
|
end
|
459
462
|
|
460
463
|
[e].pack(UINT32)
|
461
464
|
end
|
462
465
|
|
463
466
|
def unpack_error(error)
|
464
|
-
|
465
|
-
name || error
|
467
|
+
DEFINED_ERRORS.key(error) || error
|
466
468
|
end
|
467
469
|
end
|
468
470
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTP2Next
|
4
|
+
# Implementation of header compression for HTTP 2.0 (HPACK) format adapted
|
5
|
+
# to efficiently represent HTTP headers in the context of HTTP 2.0.
|
6
|
+
#
|
7
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10
|
8
|
+
module Header
|
9
|
+
# Header representation as defined by the spec.
|
10
|
+
HEADREP = {
|
11
|
+
indexed: { prefix: 7, pattern: 0x80 },
|
12
|
+
incremental: { prefix: 6, pattern: 0x40 },
|
13
|
+
noindex: { prefix: 4, pattern: 0x00 },
|
14
|
+
neverindexed: { prefix: 4, pattern: 0x10 },
|
15
|
+
changetablesize: { prefix: 5, pattern: 0x20 }
|
16
|
+
}.each_value(&:freeze).freeze
|
17
|
+
|
18
|
+
# Predefined options set for Compressor
|
19
|
+
# http://mew.org/~kazu/material/2014-hpack.pdf
|
20
|
+
NAIVE = { index: :never, huffman: :never }.freeze
|
21
|
+
LINEAR = { index: :all, huffman: :never }.freeze
|
22
|
+
STATIC = { index: :static, huffman: :never }.freeze
|
23
|
+
SHORTER = { index: :all, huffman: :never }.freeze
|
24
|
+
NAIVEH = { index: :never, huffman: :always }.freeze
|
25
|
+
LINEARH = { index: :all, huffman: :always }.freeze
|
26
|
+
STATICH = { index: :static, huffman: :always }.freeze
|
27
|
+
SHORTERH = { index: :all, huffman: :shorter }.freeze
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require "http/2/next/header/huffman"
|
32
|
+
require "http/2/next/header/huffman_statemachine"
|
33
|
+
require "http/2/next/header/encoding_context"
|
34
|
+
require "http/2/next/header/compressor"
|
35
|
+
require "http/2/next/header/decompressor"
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTP2Next
|
4
|
+
module Header
|
5
|
+
# Responsible for encoding header key-value pairs using HPACK algorithm.
|
6
|
+
class Compressor
|
7
|
+
# @param options [Hash] encoding options
|
8
|
+
def initialize(options = {})
|
9
|
+
@cc = EncodingContext.new(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Set dynamic table size in EncodingContext
|
13
|
+
# @param size [Integer] new dynamic table size
|
14
|
+
def table_size=(size)
|
15
|
+
@cc.table_size = size
|
16
|
+
end
|
17
|
+
|
18
|
+
# Encodes provided value via integer representation.
|
19
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#section-5.1
|
20
|
+
#
|
21
|
+
# If I < 2^N - 1, encode I on N bits
|
22
|
+
# Else
|
23
|
+
# encode 2^N - 1 on N bits
|
24
|
+
# I = I - (2^N - 1)
|
25
|
+
# While I >= 128
|
26
|
+
# Encode (I % 128 + 128) on 8 bits
|
27
|
+
# I = I / 128
|
28
|
+
# encode (I) on 8 bits
|
29
|
+
#
|
30
|
+
# @param i [Integer] value to encode
|
31
|
+
# @param n [Integer] number of available bits
|
32
|
+
# @return [String] binary string
|
33
|
+
def integer(i, n)
|
34
|
+
limit = 2**n - 1
|
35
|
+
return [i].pack("C") if i < limit
|
36
|
+
|
37
|
+
bytes = []
|
38
|
+
bytes.push limit unless n.zero?
|
39
|
+
|
40
|
+
i -= limit
|
41
|
+
while i >= 128
|
42
|
+
bytes.push((i % 128) + 128)
|
43
|
+
i /= 128
|
44
|
+
end
|
45
|
+
|
46
|
+
bytes.push i
|
47
|
+
bytes.pack("C*")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Encodes provided value via string literal representation.
|
51
|
+
# - http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-10#section-5.2
|
52
|
+
#
|
53
|
+
# * The string length, defined as the number of bytes needed to store
|
54
|
+
# its UTF-8 representation, is represented as an integer with a seven
|
55
|
+
# bits prefix. If the string length is strictly less than 127, it is
|
56
|
+
# represented as one byte.
|
57
|
+
# * If the bit 7 of the first byte is 1, the string value is represented
|
58
|
+
# as a list of Huffman encoded octets
|
59
|
+
# (padded with bit 1's until next octet boundary).
|
60
|
+
# * If the bit 7 of the first byte is 0, the string value is
|
61
|
+
# represented as a list of UTF-8 encoded octets.
|
62
|
+
#
|
63
|
+
# +@options [:huffman]+ controls whether to use Huffman encoding:
|
64
|
+
# :never Do not use Huffman encoding
|
65
|
+
# :always Always use Huffman encoding
|
66
|
+
# :shorter Use Huffman when the result is strictly shorter
|
67
|
+
#
|
68
|
+
# @param str [String]
|
69
|
+
# @return [String] binary string
|
70
|
+
def string(str)
|
71
|
+
plain = nil
|
72
|
+
huffman = nil
|
73
|
+
plain = integer(str.bytesize, 7) << str.dup.force_encoding(Encoding::BINARY) unless @cc.options[:huffman] == :always
|
74
|
+
unless @cc.options[:huffman] == :never
|
75
|
+
huffman = Huffman.new.encode(str)
|
76
|
+
huffman = integer(huffman.bytesize, 7) << huffman
|
77
|
+
huffman.setbyte(0, huffman.ord | 0x80)
|
78
|
+
end
|
79
|
+
case @cc.options[:huffman]
|
80
|
+
when :always
|
81
|
+
huffman
|
82
|
+
when :never
|
83
|
+
plain
|
84
|
+
else
|
85
|
+
huffman.bytesize < plain.bytesize ? huffman : plain
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Encodes header command with appropriate header representation.
|
90
|
+
#
|
91
|
+
# @param h [Hash] header command
|
92
|
+
# @param buffer [String]
|
93
|
+
# @return [Buffer]
|
94
|
+
def header(h, buffer = Buffer.new)
|
95
|
+
rep = HEADREP[h[:type]]
|
96
|
+
|
97
|
+
case h[:type]
|
98
|
+
when :indexed
|
99
|
+
buffer << integer(h[:name] + 1, rep[:prefix])
|
100
|
+
when :changetablesize
|
101
|
+
buffer << integer(h[:value], rep[:prefix])
|
102
|
+
else
|
103
|
+
if h[:name].is_a? Integer
|
104
|
+
buffer << integer(h[:name] + 1, rep[:prefix])
|
105
|
+
else
|
106
|
+
buffer << integer(0, rep[:prefix])
|
107
|
+
buffer << string(h[:name])
|
108
|
+
end
|
109
|
+
|
110
|
+
buffer << string(h[:value])
|
111
|
+
end
|
112
|
+
|
113
|
+
# set header representation pattern on first byte
|
114
|
+
fb = buffer.ord | rep[:pattern]
|
115
|
+
buffer.setbyte(0, fb)
|
116
|
+
|
117
|
+
buffer
|
118
|
+
end
|
119
|
+
|
120
|
+
# Encodes provided list of HTTP headers.
|
121
|
+
#
|
122
|
+
# @param headers [Array] +[[name, value], ...]+
|
123
|
+
# @return [Buffer]
|
124
|
+
def encode(headers)
|
125
|
+
buffer = Buffer.new
|
126
|
+
pseudo_headers, regular_headers = headers.partition { |f, _| f.start_with? ":" }
|
127
|
+
headers = [*pseudo_headers, *regular_headers]
|
128
|
+
commands = @cc.encode(headers)
|
129
|
+
commands.each do |cmd|
|
130
|
+
buffer << header(cmd)
|
131
|
+
end
|
132
|
+
|
133
|
+
buffer
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|