http-2 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|