http-2 0.10.2 → 0.12.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 +0 -2
- data/lib/http/2/buffer.rb +6 -4
- data/lib/http/2/client.rb +5 -1
- data/lib/http/2/compressor.rb +44 -36
- data/lib/http/2/connection.rb +76 -89
- data/lib/http/2/emitter.rb +4 -1
- data/lib/http/2/error.rb +2 -0
- data/lib/http/2/flow_buffer.rb +8 -3
- data/lib/http/2/framer.rb +83 -94
- data/lib/http/2/huffman.rb +19 -17
- data/lib/http/2/server.rb +9 -7
- data/lib/http/2/stream.rb +48 -48
- data/lib/http/2/version.rb +3 -1
- data/lib/http/2.rb +2 -0
- metadata +10 -63
- data/.autotest +0 -20
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -20
- data/.gitmodules +0 -3
- data/.rspec +0 -5
- data/.rubocop.yml +0 -93
- data/.rubocop_todo.yml +0 -122
- data/.travis.yml +0 -14
- data/Gemfile +0 -16
- data/Guardfile +0 -18
- data/Guardfile.h2spec +0 -12
- data/Rakefile +0 -49
- data/example/Gemfile +0 -3
- data/example/README.md +0 -44
- data/example/client.rb +0 -122
- data/example/helper.rb +0 -19
- data/example/keys/server.crt +0 -20
- data/example/keys/server.key +0 -27
- data/example/server.rb +0 -139
- data/example/upgrade_client.rb +0 -153
- data/example/upgrade_server.rb +0 -203
- data/http-2.gemspec +0 -22
- data/lib/tasks/generate_huffman_table.rb +0 -166
- data/spec/buffer_spec.rb +0 -28
- data/spec/client_spec.rb +0 -188
- data/spec/compressor_spec.rb +0 -666
- data/spec/connection_spec.rb +0 -665
- data/spec/emitter_spec.rb +0 -54
- data/spec/framer_spec.rb +0 -487
- data/spec/h2spec/h2spec.darwin +0 -0
- data/spec/h2spec/output/non_secure.txt +0 -317
- data/spec/helper.rb +0 -147
- data/spec/hpack_test_spec.rb +0 -84
- data/spec/huffman_spec.rb +0 -68
- data/spec/server_spec.rb +0 -52
- data/spec/stream_spec.rb +0 -878
- data/spec/support/deep_dup.rb +0 -55
- data/spec/support/duplicable.rb +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1788e5559bb1f36b98896322d1c846ef73b0fba4a7a45b524845735deacb9aa
|
4
|
+
data.tar.gz: 62ea957139ea576aa4553303f6db141cd9297a8ce75624553e79a90a69f74717
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb252eeff936187cf2ebebdf93e69fb6f82dba50a0fece880f40a8235b790165ad5d3f167b0c3948adf040388fc7cd49bc1bd1a24b754447c83dd2020be78eaa
|
7
|
+
data.tar.gz: 0657b1b61f526041167473faf29e50a0cbf4bbfcbac80a458a98a205ab5a01f409da7d62010e5620e5e5fe6cb6e7d1bd5a2ae2a831fcac7da1c1ab2192511c1e
|
data/README.md
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
# HTTP-2
|
2
2
|
|
3
3
|
[](http://rubygems.org/gems/http-2)
|
4
|
-
[](https://travis-ci.org/igrigorik/http-2)
|
5
4
|
[](https://coveralls.io/r/igrigorik/http-2)
|
6
|
-
[](https://github.com/igrigorik/ga-beacon)
|
7
5
|
|
8
6
|
Pure Ruby, framework and transport agnostic, implementation of HTTP/2 protocol and HPACK header compression with support for:
|
9
7
|
|
data/lib/http/2/buffer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'forwardable'
|
2
4
|
|
3
5
|
module HTTP2
|
@@ -6,11 +8,11 @@ module HTTP2
|
|
6
8
|
class Buffer
|
7
9
|
extend Forwardable
|
8
10
|
|
9
|
-
def_delegators :@buffer, :ord, :encoding, :setbyte, :unpack,
|
11
|
+
def_delegators :@buffer, :ord, :encoding, :setbyte, :unpack, :unpack1,
|
10
12
|
:size, :each_byte, :to_str, :to_s, :length, :inspect,
|
11
13
|
:[], :[]=, :empty?, :bytesize, :include?
|
12
14
|
|
13
|
-
UINT32 = 'N'
|
15
|
+
UINT32 = 'N'
|
14
16
|
private_constant :UINT32
|
15
17
|
|
16
18
|
# Forces binary encoding on the string
|
@@ -59,12 +61,12 @@ module HTTP2
|
|
59
61
|
# Slice unsigned 32-bit integer from buffer.
|
60
62
|
# @return [Integer]
|
61
63
|
def read_uint32
|
62
|
-
read(4).
|
64
|
+
read(4).unpack1(UINT32)
|
63
65
|
end
|
64
66
|
|
65
67
|
# Ensures that data that is added is binary encoded as well,
|
66
68
|
# otherwise this could lead to the Buffer instance changing its encoding.
|
67
|
-
[
|
69
|
+
%i[<< prepend].each do |mutating_method|
|
68
70
|
define_method(mutating_method) do |string|
|
69
71
|
string = string.dup if string.frozen?
|
70
72
|
@buffer.send mutating_method, string.force_encoding(Encoding::BINARY)
|
data/lib/http/2/client.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTP2
|
2
4
|
# HTTP 2.0 client connection class that implements appropriate header
|
3
5
|
# compression / decompression algorithms and stream management logic.
|
@@ -45,7 +47,8 @@ module HTTP2
|
|
45
47
|
|
46
48
|
# sends the preface and initializes the first stream in half-closed state
|
47
49
|
def upgrade
|
48
|
-
|
50
|
+
raise ProtocolError unless @stream_id == 1
|
51
|
+
|
49
52
|
send_connection_preface
|
50
53
|
new_stream(state: :half_closed_local)
|
51
54
|
end
|
@@ -53,6 +56,7 @@ module HTTP2
|
|
53
56
|
# Emit the connection preface if not yet
|
54
57
|
def send_connection_preface
|
55
58
|
return unless @state == :waiting_connection_preface
|
59
|
+
|
56
60
|
@state = :connected
|
57
61
|
emit(:frame, CONNECTION_PREFACE_MAGIC)
|
58
62
|
|
data/lib/http/2/compressor.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTP2
|
2
4
|
# Implementation of header compression for HTTP 2.0 (HPACK) format adapted
|
3
5
|
# to efficiently represent HTTP headers in the context of HTTP 2.0.
|
@@ -74,7 +76,7 @@ module HTTP2
|
|
74
76
|
['user-agent', ''],
|
75
77
|
['vary', ''],
|
76
78
|
['via', ''],
|
77
|
-
['www-authenticate', '']
|
79
|
+
['www-authenticate', '']
|
78
80
|
].each { |pair| pair.each(&:freeze).freeze }.freeze
|
79
81
|
|
80
82
|
# Current table of header key-value pairs.
|
@@ -96,9 +98,9 @@ module HTTP2
|
|
96
98
|
# :index Symbol :all, :static, :never
|
97
99
|
def initialize(**options)
|
98
100
|
default_options = {
|
99
|
-
huffman:
|
100
|
-
index:
|
101
|
-
table_size: 4096
|
101
|
+
huffman: :shorter,
|
102
|
+
index: :all,
|
103
|
+
table_size: 4096
|
102
104
|
}
|
103
105
|
@table = []
|
104
106
|
@options = default_options.merge(options)
|
@@ -112,7 +114,7 @@ module HTTP2
|
|
112
114
|
t = @table
|
113
115
|
l = @limit
|
114
116
|
other.instance_eval do
|
115
|
-
@table = t.dup
|
117
|
+
@table = t.dup # shallow copy
|
116
118
|
@limit = l
|
117
119
|
end
|
118
120
|
other
|
@@ -131,7 +133,8 @@ module HTTP2
|
|
131
133
|
def dereference(index)
|
132
134
|
# NOTE: index is zero-based in this module.
|
133
135
|
value = STATIC_TABLE[index] || @table[index - STATIC_TABLE.size]
|
134
|
-
|
136
|
+
raise CompressionError, 'Index too large' unless value
|
137
|
+
|
135
138
|
value
|
136
139
|
end
|
137
140
|
|
@@ -146,9 +149,8 @@ module HTTP2
|
|
146
149
|
|
147
150
|
case cmd[:type]
|
148
151
|
when :changetablesize
|
149
|
-
if cmd[:value] > @limit
|
150
|
-
|
151
|
-
end
|
152
|
+
raise CompressionError, 'dynamic table size update exceed limit' if cmd[:value] > @limit
|
153
|
+
|
152
154
|
self.table_size = cmd[:value]
|
153
155
|
|
154
156
|
when :indexed
|
@@ -186,7 +188,7 @@ module HTTP2
|
|
186
188
|
add_to_table(emit) if cmd[:type] == :incremental
|
187
189
|
|
188
190
|
else
|
189
|
-
|
191
|
+
raise CompressionError, "Invalid type: #{cmd[:type]}"
|
190
192
|
end
|
191
193
|
|
192
194
|
emit
|
@@ -202,7 +204,7 @@ module HTTP2
|
|
202
204
|
def encode(headers)
|
203
205
|
commands = []
|
204
206
|
# Literals commands are marked with :noindex when index is not used
|
205
|
-
noindex = [
|
207
|
+
noindex = %i[static never].include?(@options[:index])
|
206
208
|
headers.each do |field, value|
|
207
209
|
# Literal header names MUST be translated to lowercase before
|
208
210
|
# encoding and transmission.
|
@@ -232,7 +234,7 @@ module HTTP2
|
|
232
234
|
exact = nil
|
233
235
|
name_only = nil
|
234
236
|
|
235
|
-
if [
|
237
|
+
if %i[all static].include?(@options[:index])
|
236
238
|
STATIC_TABLE.each_index do |i|
|
237
239
|
if STATIC_TABLE[i] == header
|
238
240
|
exact ||= i
|
@@ -284,6 +286,7 @@ module HTTP2
|
|
284
286
|
# @param cmd [Array] +[name, value]+
|
285
287
|
def add_to_table(cmd)
|
286
288
|
return unless size_check(cmd)
|
289
|
+
|
287
290
|
@table.unshift(cmd)
|
288
291
|
end
|
289
292
|
|
@@ -309,11 +312,11 @@ module HTTP2
|
|
309
312
|
|
310
313
|
# Header representation as defined by the spec.
|
311
314
|
HEADREP = {
|
312
|
-
indexed:
|
313
|
-
incremental:
|
314
|
-
noindex:
|
315
|
+
indexed: { prefix: 7, pattern: 0x80 },
|
316
|
+
incremental: { prefix: 6, pattern: 0x40 },
|
317
|
+
noindex: { prefix: 4, pattern: 0x00 },
|
315
318
|
neverindexed: { prefix: 4, pattern: 0x10 },
|
316
|
-
changetablesize: { prefix: 5, pattern: 0x20 }
|
319
|
+
changetablesize: { prefix: 5, pattern: 0x20 }
|
317
320
|
}.each_value(&:freeze).freeze
|
318
321
|
|
319
322
|
# Predefined options set for Compressor
|
@@ -330,8 +333,9 @@ module HTTP2
|
|
330
333
|
# Responsible for encoding header key-value pairs using HPACK algorithm.
|
331
334
|
class Compressor
|
332
335
|
# @param options [Hash] encoding options
|
336
|
+
# @see EncodingContext#initialize
|
333
337
|
def initialize(**options)
|
334
|
-
@cc = EncodingContext.new(options)
|
338
|
+
@cc = EncodingContext.new(**options)
|
335
339
|
end
|
336
340
|
|
337
341
|
# Set dynamic table size in EncodingContext
|
@@ -356,14 +360,14 @@ module HTTP2
|
|
356
360
|
# @param n [Integer] number of available bits
|
357
361
|
# @return [String] binary string
|
358
362
|
def integer(i, n)
|
359
|
-
limit = 2**n - 1
|
363
|
+
limit = (2**n) - 1
|
360
364
|
return [i].pack('C') if i < limit
|
361
365
|
|
362
366
|
bytes = []
|
363
367
|
bytes.push limit unless n.zero?
|
364
368
|
|
365
369
|
i -= limit
|
366
|
-
while
|
370
|
+
while i >= 128
|
367
371
|
bytes.push((i % 128) + 128)
|
368
372
|
i /= 128
|
369
373
|
end
|
@@ -393,7 +397,8 @@ module HTTP2
|
|
393
397
|
# @param str [String]
|
394
398
|
# @return [String] binary string
|
395
399
|
def string(str)
|
396
|
-
plain
|
400
|
+
plain = nil
|
401
|
+
huffman = nil
|
397
402
|
unless @cc.options[:huffman] == :always
|
398
403
|
plain = integer(str.bytesize, 7) << str.dup.force_encoding(Encoding::BINARY)
|
399
404
|
end
|
@@ -463,14 +468,11 @@ module HTTP2
|
|
463
468
|
# Responsible for decoding received headers and maintaining compression
|
464
469
|
# context of the opposing peer. Decompressor must be initialized with
|
465
470
|
# appropriate starting context based on local role: client or server.
|
466
|
-
#
|
467
|
-
# @example
|
468
|
-
# server_role = Decompressor.new(:request)
|
469
|
-
# client_role = Decompressor.new(:response)
|
470
471
|
class Decompressor
|
471
472
|
# @param options [Hash] decoding options. Only :table_size is effective.
|
473
|
+
# @see EncodingContext#initialize
|
472
474
|
def initialize(**options)
|
473
|
-
@cc = EncodingContext.new(options)
|
475
|
+
@cc = EncodingContext.new(**options)
|
474
476
|
end
|
475
477
|
|
476
478
|
# Set dynamic table size in EncodingContext
|
@@ -485,16 +487,18 @@ module HTTP2
|
|
485
487
|
# @param n [Integer] number of available bits
|
486
488
|
# @return [Integer]
|
487
489
|
def integer(buf, n)
|
488
|
-
limit = 2**n - 1
|
489
|
-
i =
|
490
|
+
limit = (2**n) - 1
|
491
|
+
i = n.zero? ? 0 : (buf.getbyte & limit)
|
490
492
|
|
491
493
|
m = 0
|
492
|
-
|
493
|
-
|
494
|
-
|
494
|
+
if i == limit
|
495
|
+
while (byte = buf.getbyte)
|
496
|
+
i += ((byte & 127) << m)
|
497
|
+
m += 7
|
495
498
|
|
496
|
-
|
497
|
-
|
499
|
+
break if (byte & 128).zero?
|
500
|
+
end
|
501
|
+
end
|
498
502
|
|
499
503
|
i
|
500
504
|
end
|
@@ -508,7 +512,8 @@ module HTTP2
|
|
508
512
|
huffman = (buf.readbyte(0) & 0x80) == 0x80
|
509
513
|
len = integer(buf, 7)
|
510
514
|
str = buf.read(len)
|
511
|
-
|
515
|
+
raise CompressionError, 'string too short' unless str.bytesize == len
|
516
|
+
|
512
517
|
str = Huffman.new.decode(Buffer.new(str)) if huffman
|
513
518
|
str.force_encoding(Encoding::UTF_8)
|
514
519
|
end
|
@@ -526,13 +531,14 @@ module HTTP2
|
|
526
531
|
mask == desc[:pattern]
|
527
532
|
end
|
528
533
|
|
529
|
-
|
534
|
+
raise CompressionError unless header[:type]
|
530
535
|
|
531
536
|
header[:name] = integer(buf, type[:prefix])
|
532
537
|
|
533
538
|
case header[:type]
|
534
539
|
when :indexed
|
535
|
-
|
540
|
+
raise CompressionError if (header[:name]).zero?
|
541
|
+
|
536
542
|
header[:name] -= 1
|
537
543
|
when :changetablesize
|
538
544
|
header[:value] = header[:name]
|
@@ -558,10 +564,12 @@ module HTTP2
|
|
558
564
|
until buf.empty?
|
559
565
|
next_header = @cc.process(header(buf))
|
560
566
|
next if next_header.nil?
|
567
|
+
|
561
568
|
is_pseudo_header = next_header.first.start_with? ':'
|
562
569
|
if !decoding_pseudo_headers && is_pseudo_header
|
563
|
-
|
570
|
+
raise ProtocolError, 'one or more pseudo headers encountered after regular headers'
|
564
571
|
end
|
572
|
+
|
565
573
|
decoding_pseudo_headers = is_pseudo_header
|
566
574
|
list << next_header
|
567
575
|
end
|