http-2 0.8.0 → 0.8.1
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/.travis.yml +1 -0
- data/Gemfile +1 -1
- data/example/client.rb +2 -1
- data/example/server.rb +3 -2
- data/example/upgrade_server.rb +22 -20
- data/http-2.gemspec +1 -1
- data/lib/http/2/buffer.rb +14 -0
- data/lib/http/2/connection.rb +6 -5
- data/lib/http/2/server.rb +5 -4
- data/lib/http/2/version.rb +1 -1
- data/spec/buffer_spec.rb +7 -0
- data/spec/helper.rb +1 -0
- data/spec/hpack_test_spec.rb +84 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e4d8a543b24380552462863450caaa90dcc5d1f
|
4
|
+
data.tar.gz: 5948ffc6c3aa27f7d81bd86cd0ef62ad163e412f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32a27c2213a6c8b18b56f752495a938d65260c63644be63af58fd0aaf133795bf9e2748d23fe8b8c49bf96500e30a551ac0001001a1fe46a2082f06d8420dcc8
|
7
|
+
data.tar.gz: 2fdc75f407ecb70fb601a04d9c8fe5b76e23bebafc495e48bf2a1e3d96ada6d1784ed29a9c352cfa04027ebd5dace72779fc009b1cd0f87366cfab035c72f66a
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/example/client.rb
CHANGED
@@ -110,7 +110,8 @@ while !sock.closed? && !sock.eof?
|
|
110
110
|
begin
|
111
111
|
conn << data
|
112
112
|
rescue => e
|
113
|
-
puts "
|
113
|
+
puts "#{e.class} exception: #{e.message} - closing socket."
|
114
|
+
e.backtrace.each { |l| puts "\t" + l }
|
114
115
|
sock.close
|
115
116
|
end
|
116
117
|
end
|
data/example/server.rb
CHANGED
@@ -45,7 +45,7 @@ loop do
|
|
45
45
|
log = Logger.new(stream.id)
|
46
46
|
req, buffer = {}, ''
|
47
47
|
|
48
|
-
stream.on(:active) { log.info '
|
48
|
+
stream.on(:active) { log.info 'client opened new stream' }
|
49
49
|
stream.on(:close) { log.info 'stream closed' }
|
50
50
|
|
51
51
|
stream.on(:headers) do |h|
|
@@ -89,7 +89,8 @@ loop do
|
|
89
89
|
begin
|
90
90
|
conn << data
|
91
91
|
rescue => e
|
92
|
-
puts "
|
92
|
+
puts "#{e.class} exception: #{e.message} - closing socket."
|
93
|
+
e.backtrace.each { |l| puts "\t" + l }
|
93
94
|
sock.close
|
94
95
|
end
|
95
96
|
end
|
data/example/upgrade_server.rb
CHANGED
@@ -28,15 +28,17 @@ if options[:secure]
|
|
28
28
|
end
|
29
29
|
|
30
30
|
class UpgradeHandler
|
31
|
+
VALID_UPGRADE_METHODS = %w(GET OPTIONS)
|
32
|
+
UPGRADE_RESPONSE = <<-RESP
|
33
|
+
HTTP/1.1 101 Switching Protocols
|
34
|
+
Connection: Upgrade
|
35
|
+
Upgrade: h2c
|
31
36
|
|
32
|
-
|
33
|
-
UPGRADE_RESPONSE = ("HTTP/1.1 101 Switching Protocols\n" +
|
34
|
-
"Connection: Upgrade\n" +
|
35
|
-
"Upgrade: h2c\n\n").freeze
|
37
|
+
RESP
|
36
38
|
|
37
39
|
attr_reader :complete, :headers, :body, :parsing
|
38
40
|
|
39
|
-
def initialize
|
41
|
+
def initialize(conn, sock)
|
40
42
|
@conn, @sock = conn, sock
|
41
43
|
@complete, @parsing = false, false
|
42
44
|
@body = ''
|
@@ -46,23 +48,24 @@ class UpgradeHandler
|
|
46
48
|
def <<(data)
|
47
49
|
@parsing ||= true
|
48
50
|
@parser << data
|
49
|
-
|
51
|
+
return unless complete
|
50
52
|
|
51
|
-
|
53
|
+
@sock.write UPGRADE_RESPONSE
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
settings = headers['http2-settings']
|
56
|
+
request = {
|
57
|
+
':scheme' => 'http',
|
58
|
+
':method' => @parser.http_method,
|
59
|
+
':authority' => headers['Host'],
|
60
|
+
':path' => @parser.request_url,
|
61
|
+
}.merge(headers)
|
60
62
|
|
61
|
-
|
62
|
-
end
|
63
|
+
@conn.upgrade(settings, request, @body)
|
63
64
|
end
|
64
65
|
|
65
|
-
def complete
|
66
|
+
def complete!
|
67
|
+
@complete = true
|
68
|
+
end
|
66
69
|
|
67
70
|
def on_headers_complete(headers)
|
68
71
|
@headers = headers
|
@@ -73,11 +76,10 @@ class UpgradeHandler
|
|
73
76
|
end
|
74
77
|
|
75
78
|
def on_message_complete
|
76
|
-
|
79
|
+
fail unless VALID_UPGRADE_METHODS.include?(@parser.http_method)
|
77
80
|
@parsing = false
|
78
81
|
complete!
|
79
82
|
end
|
80
|
-
|
81
83
|
end
|
82
84
|
|
83
85
|
loop do
|
@@ -122,7 +124,7 @@ loop do
|
|
122
124
|
log.info "Processing h2c Upgrade request: #{req}"
|
123
125
|
|
124
126
|
# Don't respond to OPTIONS...
|
125
|
-
if req[':method'] !=
|
127
|
+
if req[':method'] != 'OPTIONS'
|
126
128
|
response = 'Hello h2c world!'
|
127
129
|
stream.headers({
|
128
130
|
':status' => '200',
|
data/http-2.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.summary = spec.description
|
13
13
|
spec.homepage = 'https://github.com/igrigorik/http-2'
|
14
14
|
spec.license = 'MIT'
|
15
|
-
spec.required_ruby_version = '>=2.
|
15
|
+
spec.required_ruby_version = '>=2.1.0'
|
16
16
|
|
17
17
|
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
18
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
data/lib/http/2/buffer.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
module HTTP2
|
2
2
|
# Simple binary buffer backed by string.
|
3
3
|
#
|
4
|
+
# TODO: Refactor, it would be better if Buffer were not a String subclass,
|
5
|
+
# but rather wrap a string and only expose the mutating API needed so that
|
6
|
+
# the possible surface for things to go wrong stays controllable.
|
7
|
+
# - https://github.com/igrigorik/http-2/pull/46
|
8
|
+
#
|
4
9
|
class Buffer < String
|
5
10
|
UINT32 = 'N'.freeze
|
6
11
|
private_constant :UINT32
|
@@ -30,5 +35,14 @@ module HTTP2
|
|
30
35
|
def read_uint32
|
31
36
|
read(4).unpack(UINT32).first
|
32
37
|
end
|
38
|
+
|
39
|
+
# Ensures that data that is added is binary encoded as well,
|
40
|
+
# otherwise this could lead to the Buffer instance changing its encoding.
|
41
|
+
[:<<, :prepend].each do |mutating_method|
|
42
|
+
define_method(mutating_method) do |string|
|
43
|
+
string = string.dup if string.frozen?
|
44
|
+
super(string.force_encoding(Encoding::BINARY))
|
45
|
+
end
|
46
|
+
end
|
33
47
|
end
|
34
48
|
end
|
data/lib/http/2/connection.rb
CHANGED
@@ -321,7 +321,7 @@ module HTTP2
|
|
321
321
|
|
322
322
|
rescue => e
|
323
323
|
raise if e.is_a?(Error::Error)
|
324
|
-
connection_error
|
324
|
+
connection_error(e: e)
|
325
325
|
end
|
326
326
|
alias_method :<<, :receive
|
327
327
|
|
@@ -574,7 +574,7 @@ module HTTP2
|
|
574
574
|
end
|
575
575
|
|
576
576
|
rescue => e
|
577
|
-
connection_error(:compression_error,
|
577
|
+
connection_error(:compression_error, e: e)
|
578
578
|
end
|
579
579
|
|
580
580
|
# Encode headers payload and update connection compressor state.
|
@@ -605,7 +605,8 @@ module HTTP2
|
|
605
605
|
frames
|
606
606
|
|
607
607
|
rescue => e
|
608
|
-
|
608
|
+
connection_error(:compression_error, e: e)
|
609
|
+
nil
|
609
610
|
end
|
610
611
|
|
611
612
|
# Activates new incoming or outgoing stream and registers appropriate
|
@@ -642,12 +643,12 @@ module HTTP2
|
|
642
643
|
# @option error [Symbol] :frame_too_large
|
643
644
|
# @option error [Symbol] :compression_error
|
644
645
|
# @param msg [String]
|
645
|
-
def connection_error(error = :protocol_error, msg: nil)
|
646
|
+
def connection_error(error = :protocol_error, msg: nil, e: nil)
|
646
647
|
goaway(error) unless @state == :closed || @state == :new
|
647
648
|
|
648
649
|
@state, @error = :closed, error
|
649
650
|
klass = error.to_s.split('_').map(&:capitalize).join
|
650
|
-
fail Error.const_get(klass), msg
|
651
|
+
fail Error.const_get(klass), msg || e.message, e.backtrace || []
|
651
652
|
end
|
652
653
|
end
|
653
654
|
end
|
data/lib/http/2/server.rb
CHANGED
@@ -72,12 +72,13 @@ module HTTP2
|
|
72
72
|
|
73
73
|
# Process received HTTP2-Settings payload
|
74
74
|
buf = HTTP2::Buffer.new Base64.urlsafe_decode64(settings)
|
75
|
-
|
75
|
+
header = @framer.common_header(
|
76
76
|
length: buf.bytesize,
|
77
77
|
type: :settings,
|
78
78
|
stream: 0,
|
79
|
-
flags: []
|
80
|
-
)
|
79
|
+
flags: [],
|
80
|
+
)
|
81
|
+
buf.prepend(header)
|
81
82
|
receive(buf)
|
82
83
|
|
83
84
|
# Activate stream (id: 1) with on HTTP/1.1 request parameters
|
@@ -98,7 +99,7 @@ module HTTP2
|
|
98
99
|
stream << headers_frame
|
99
100
|
else
|
100
101
|
stream << headers_frame
|
101
|
-
stream << {type: :data, stream: 1, payload: body, flags: [:end_stream]}
|
102
|
+
stream << { type: :data, stream: 1, payload: body, flags: [:end_stream] }
|
102
103
|
end
|
103
104
|
|
104
105
|
# Mark h2c upgrade as finished
|
data/lib/http/2/version.rb
CHANGED
data/spec/buffer_spec.rb
CHANGED
@@ -7,6 +7,13 @@ RSpec.describe HTTP2::Buffer do
|
|
7
7
|
expect(b.encoding.to_s).to eq 'ASCII-8BIT'
|
8
8
|
end
|
9
9
|
|
10
|
+
it 'should force 8-bit encoding when adding data' do
|
11
|
+
b << 'émalgré'
|
12
|
+
expect(b.encoding.to_s).to eq 'ASCII-8BIT'
|
13
|
+
b.prepend('émalgré')
|
14
|
+
expect(b.encoding.to_s).to eq 'ASCII-8BIT'
|
15
|
+
end
|
16
|
+
|
10
17
|
it 'should return bytesize of the buffer' do
|
11
18
|
expect(b.size).to eq 9
|
12
19
|
end
|
data/spec/helper.rb
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
RSpec.describe HTTP2::Header do
|
5
|
+
folders = %w(
|
6
|
+
go-hpack
|
7
|
+
haskell-http2-diff
|
8
|
+
haskell-http2-diff-huffman
|
9
|
+
haskell-http2-linear
|
10
|
+
haskell-http2-linear-huffman
|
11
|
+
haskell-http2-naive
|
12
|
+
haskell-http2-naive-huffman
|
13
|
+
haskell-http2-static
|
14
|
+
haskell-http2-static-huffman
|
15
|
+
#hyper-hpack
|
16
|
+
nghttp2
|
17
|
+
nghttp2-16384-4096
|
18
|
+
nghttp2-change-table-size
|
19
|
+
node-http2-hpack
|
20
|
+
)
|
21
|
+
|
22
|
+
context 'Decompressor' do
|
23
|
+
folders.each do |folder|
|
24
|
+
next if folder =~ /#/
|
25
|
+
path = File.expand_path("hpack-test-case/#{folder}", File.dirname(__FILE__))
|
26
|
+
next unless Dir.exist?(path)
|
27
|
+
context "#{folder}" do
|
28
|
+
Dir.foreach(path) do |file|
|
29
|
+
next if file !~ /\.json/
|
30
|
+
it "should decode #{file}" do
|
31
|
+
story = JSON.parse(File.read("#{path}/#{file}"))
|
32
|
+
cases = story['cases']
|
33
|
+
table_size = cases[0]['header_table_size'] || 4096
|
34
|
+
@dc = Decompressor.new(table_size: table_size)
|
35
|
+
cases.each do |c|
|
36
|
+
wire = [c['wire']].pack('H*').force_encoding(Encoding::BINARY)
|
37
|
+
@emitted = @dc.decode(HTTP2::Buffer.new(wire))
|
38
|
+
headers = c['headers'].flat_map(&:to_a)
|
39
|
+
expect(@emitted).to eq headers
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'Compressor' do
|
48
|
+
%w(
|
49
|
+
LINEAR
|
50
|
+
NAIVE
|
51
|
+
SHORTER
|
52
|
+
STATIC
|
53
|
+
).each do |mode|
|
54
|
+
next if mode =~ /#/
|
55
|
+
['', 'H'].each do |huffman|
|
56
|
+
encoding_mode = "#{mode}#{huffman}".to_sym
|
57
|
+
encoding_options = HTTP2::Header::EncodingContext.const_get(encoding_mode)
|
58
|
+
[4096, 512].each do |table_size|
|
59
|
+
options = { table_size: table_size }
|
60
|
+
options.update(encoding_options)
|
61
|
+
|
62
|
+
context "with #{mode}#{huffman} mode and table_size #{table_size}" do
|
63
|
+
path = File.expand_path('hpack-test-case/raw-data', File.dirname(__FILE__))
|
64
|
+
Dir.foreach(path) do |file|
|
65
|
+
next if file !~ /\.json/
|
66
|
+
it "should encode #{file}" do
|
67
|
+
story = JSON.parse(File.read("#{path}/#{file}"))
|
68
|
+
cases = story['cases']
|
69
|
+
@cc = Compressor .new(options)
|
70
|
+
@dc = Decompressor.new(options)
|
71
|
+
cases.each do |c|
|
72
|
+
headers = c['headers'].flat_map(&:to_a)
|
73
|
+
wire = @cc.encode(headers)
|
74
|
+
decoded = @dc.decode(HTTP2::Buffer.new(wire))
|
75
|
+
expect(decoded).to eq headers
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http-2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Grigorik
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-04-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -73,6 +73,7 @@ files:
|
|
73
73
|
- spec/emitter_spec.rb
|
74
74
|
- spec/framer_spec.rb
|
75
75
|
- spec/helper.rb
|
76
|
+
- spec/hpack_test_spec.rb
|
76
77
|
- spec/huffman_spec.rb
|
77
78
|
- spec/server_spec.rb
|
78
79
|
- spec/stream_spec.rb
|
@@ -88,7 +89,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
88
89
|
requirements:
|
89
90
|
- - ">="
|
90
91
|
- !ruby/object:Gem::Version
|
91
|
-
version: 2.
|
92
|
+
version: 2.1.0
|
92
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
94
|
requirements:
|
94
95
|
- - ">="
|
@@ -108,6 +109,7 @@ test_files:
|
|
108
109
|
- spec/emitter_spec.rb
|
109
110
|
- spec/framer_spec.rb
|
110
111
|
- spec/helper.rb
|
112
|
+
- spec/hpack_test_spec.rb
|
111
113
|
- spec/huffman_spec.rb
|
112
114
|
- spec/server_spec.rb
|
113
115
|
- spec/stream_spec.rb
|