http-2 0.11.0 → 1.0.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -9
  3. data/lib/http/2/base64.rb +45 -0
  4. data/lib/http/2/client.rb +19 -6
  5. data/lib/http/2/connection.rb +235 -163
  6. data/lib/http/2/emitter.rb +7 -5
  7. data/lib/http/2/error.rb +24 -1
  8. data/lib/http/2/extensions.rb +53 -0
  9. data/lib/http/2/flow_buffer.rb +91 -33
  10. data/lib/http/2/framer.rb +184 -157
  11. data/lib/http/2/header/compressor.rb +157 -0
  12. data/lib/http/2/header/decompressor.rb +144 -0
  13. data/lib/http/2/header/encoding_context.rb +337 -0
  14. data/lib/http/2/{huffman.rb → header/huffman.rb} +25 -19
  15. data/lib/http/2/{huffman_statemachine.rb → header/huffman_statemachine.rb} +2 -0
  16. data/lib/http/2/header.rb +35 -0
  17. data/lib/http/2/server.rb +47 -20
  18. data/lib/http/2/stream.rb +130 -61
  19. data/lib/http/2/version.rb +3 -1
  20. data/lib/http/2.rb +14 -13
  21. data/sig/client.rbs +9 -0
  22. data/sig/connection.rbs +93 -0
  23. data/sig/emitter.rbs +13 -0
  24. data/sig/error.rbs +35 -0
  25. data/sig/extensions.rbs +5 -0
  26. data/sig/flow_buffer.rbs +21 -0
  27. data/sig/frame_buffer.rbs +13 -0
  28. data/sig/framer.rbs +54 -0
  29. data/sig/header/compressor.rbs +27 -0
  30. data/sig/header/decompressor.rbs +22 -0
  31. data/sig/header/encoding_context.rbs +34 -0
  32. data/sig/header/huffman.rbs +9 -0
  33. data/sig/header.rbs +27 -0
  34. data/sig/next.rbs +101 -0
  35. data/sig/server.rbs +12 -0
  36. data/sig/stream.rbs +91 -0
  37. metadata +38 -79
  38. data/.autotest +0 -20
  39. data/.coveralls.yml +0 -1
  40. data/.gitignore +0 -20
  41. data/.gitmodules +0 -3
  42. data/.rspec +0 -5
  43. data/.rubocop.yml +0 -93
  44. data/.rubocop_todo.yml +0 -131
  45. data/.travis.yml +0 -17
  46. data/Gemfile +0 -16
  47. data/Guardfile +0 -18
  48. data/Guardfile.h2spec +0 -12
  49. data/LICENSE +0 -21
  50. data/Rakefile +0 -49
  51. data/example/Gemfile +0 -3
  52. data/example/README.md +0 -44
  53. data/example/client.rb +0 -122
  54. data/example/helper.rb +0 -19
  55. data/example/keys/server.crt +0 -20
  56. data/example/keys/server.key +0 -27
  57. data/example/server.rb +0 -139
  58. data/example/upgrade_client.rb +0 -153
  59. data/example/upgrade_server.rb +0 -203
  60. data/http-2.gemspec +0 -22
  61. data/lib/http/2/buffer.rb +0 -76
  62. data/lib/http/2/compressor.rb +0 -572
  63. data/lib/tasks/generate_huffman_table.rb +0 -166
  64. data/spec/buffer_spec.rb +0 -28
  65. data/spec/client_spec.rb +0 -188
  66. data/spec/compressor_spec.rb +0 -666
  67. data/spec/connection_spec.rb +0 -681
  68. data/spec/emitter_spec.rb +0 -54
  69. data/spec/framer_spec.rb +0 -487
  70. data/spec/h2spec/h2spec.darwin +0 -0
  71. data/spec/h2spec/output/non_secure.txt +0 -317
  72. data/spec/helper.rb +0 -147
  73. data/spec/hpack_test_spec.rb +0 -84
  74. data/spec/huffman_spec.rb +0 -68
  75. data/spec/server_spec.rb +0 -52
  76. data/spec/stream_spec.rb +0 -878
  77. data/spec/support/deep_dup.rb +0 -55
  78. data/spec/support/duplicable.rb +0 -98
@@ -1,166 +0,0 @@
1
- desc 'Generate Huffman precompiled table in huffman_statemachine.rb'
2
- task :generate_table do
3
- HuffmanTable::Node.generate_state_table
4
- end
5
-
6
- require_relative '../http/2/huffman'
7
-
8
- # @private
9
- module HuffmanTable
10
- BITS_AT_ONCE = HTTP2::Header::Huffman::BITS_AT_ONCE
11
- EOS = 256
12
-
13
- class Node
14
- attr_accessor :next, :emit, :final, :depth
15
- attr_accessor :transitions
16
- attr_accessor :id
17
- @@id = 0 # rubocop:disable Style/ClassVars
18
- def initialize(depth)
19
- @next = [nil, nil]
20
- @id = @@id
21
- @@id += 1 # rubocop:disable Style/ClassVars
22
- @final = false
23
- @depth = depth
24
- end
25
-
26
- def add(code, len, chr)
27
- self.final = true if chr == EOS && @depth <= 7
28
- if len.zero?
29
- @emit = chr
30
- else
31
- bit = (code & (1 << (len - 1))).zero? ? 0 : 1
32
- node = @next[bit] ||= Node.new(@depth + 1)
33
- node.add(code, len - 1, chr)
34
- end
35
- end
36
-
37
- class Transition
38
- attr_accessor :emit, :node
39
- def initialize(emit, node)
40
- @emit = emit
41
- @node = node
42
- end
43
- end
44
-
45
- def self.generate_tree
46
- @root = new(0)
47
- HTTP2::Header::Huffman::CODES.each_with_index do |c, chr|
48
- code, len = c
49
- @root.add(code, len, chr)
50
- end
51
- puts "#{@@id} nodes"
52
- @root
53
- end
54
-
55
- def self.generate_machine
56
- generate_tree
57
- togo = Set[@root]
58
- @states = Set[@root]
59
-
60
- until togo.empty?
61
- node = togo.first
62
- togo.delete(node)
63
-
64
- next if node.transitions
65
- node.transitions = Array[1 << BITS_AT_ONCE]
66
-
67
- (1 << BITS_AT_ONCE).times do |input|
68
- n = node
69
- emit = ''
70
- (BITS_AT_ONCE - 1).downto(0) do |i|
71
- bit = (input & (1 << i)).zero? ? 0 : 1
72
- n = n.next[bit]
73
- next unless n.emit
74
- if n.emit == EOS
75
- emit = EOS # cause error on decoding
76
- else
77
- emit << n.emit.chr(Encoding::BINARY) unless emit == EOS
78
- end
79
- n = @root
80
- end
81
- node.transitions[input] = Transition.new(emit, n)
82
- togo << n
83
- @states << n
84
- end
85
- end
86
- puts "#{@states.size} states"
87
- @root
88
- end
89
-
90
- def self.generate_state_table
91
- generate_machine
92
- state_id = {}
93
- id_state = {}
94
- state_id[@root] = 0
95
- id_state[0] = @root
96
- max_final = 0
97
- id = 1
98
- (@states - [@root]).sort_by { |s| s.final ? 0 : 1 }.each do |s|
99
- state_id[s] = id
100
- id_state[id] = s
101
- max_final = id if s.final
102
- id += 1
103
- end
104
-
105
- File.open(File.expand_path('../http/2/huffman_statemachine.rb', File.dirname(__FILE__)), 'w') do |f|
106
- f.print <<HEADER
107
- # Machine generated Huffman decoder state machine.
108
- # DO NOT EDIT THIS FILE.
109
-
110
- # The following task generates this file.
111
- # rake generate_huffman_table
112
-
113
- module HTTP2
114
- module Header
115
- class Huffman
116
- # :nodoc:
117
- MAX_FINAL_STATE = #{max_final}
118
- MACHINE = [
119
- HEADER
120
- id.times do |i|
121
- n = id_state[i]
122
- f.print ' ['
123
- string = (1 << BITS_AT_ONCE).times.map do |t|
124
- transition = n.transitions.fetch(t)
125
- emit = transition.emit
126
- unless emit == EOS
127
- bytes = emit.bytes
128
- fail ArgumentError if bytes.size > 1
129
- emit = bytes.first
130
- end
131
- "[#{emit.inspect}, #{state_id.fetch(transition.node)}]"
132
- end.join(', ')
133
- f.print(string)
134
- f.print "],\n"
135
- end
136
- f.print <<TAILER
137
- ].each { |arr| arr.each { |subarr| subarr.each(&:freeze) }.freeze }.freeze
138
- end
139
- end
140
- end
141
- TAILER
142
- end
143
- end
144
-
145
- class << self
146
- attr_reader :root
147
- end
148
-
149
- # Test decoder
150
- def self.decode(input)
151
- emit = ''
152
- n = root
153
- nibbles = input.unpack('C*').flat_map { |b| [((b & 0xf0) >> 4), b & 0xf] }
154
- until nibbles.empty?
155
- nb = nibbles.shift
156
- t = n.transitions[nb]
157
- emit << t.emit
158
- n = t.node
159
- end
160
- unless n.final && nibbles.all? { |x| x == 0xf }
161
- puts "len = #{emit.size} n.final = #{n.final} nibbles = #{nibbles}"
162
- end
163
- emit
164
- end
165
- end
166
- end
data/spec/buffer_spec.rb DELETED
@@ -1,28 +0,0 @@
1
- require 'helper'
2
-
3
- RSpec.describe HTTP2::Buffer do
4
- let(:b) { Buffer.new('émalgré') }
5
-
6
- it 'should force 8-bit encoding' do
7
- expect(b.encoding.to_s).to eq 'ASCII-8BIT'
8
- end
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
-
17
- it 'should return bytesize of the buffer' do
18
- expect(b.size).to eq 9
19
- end
20
-
21
- it 'should read single byte at a time' do
22
- 9.times { expect(b.read(1)).not_to be_nil }
23
- end
24
-
25
- it 'should unpack an unsigned 32-bit int' do
26
- expect(Buffer.new([256].pack('N')).read_uint32).to eq 256
27
- end
28
- end
data/spec/client_spec.rb DELETED
@@ -1,188 +0,0 @@
1
- require 'helper'
2
-
3
- RSpec.describe HTTP2::Client do
4
- include FrameHelpers
5
- before(:each) do
6
- @client = Client.new
7
- end
8
-
9
- let(:f) { Framer.new }
10
-
11
- context 'initialization and settings' do
12
- it 'should return odd stream IDs' do
13
- expect(@client.new_stream.id).not_to be_even
14
- end
15
-
16
- it 'should emit connection header and SETTINGS on new client connection' do
17
- frames = []
18
- @client.on(:frame) { |bytes| frames << bytes }
19
- @client.ping('12345678')
20
-
21
- expect(frames[0]).to eq CONNECTION_PREFACE_MAGIC
22
- expect(f.parse(frames[1])[:type]).to eq :settings
23
- end
24
-
25
- it 'should initialize client with custom connection settings' do
26
- frames = []
27
-
28
- @client = Client.new(settings_max_concurrent_streams: 200)
29
- @client.on(:frame) { |bytes| frames << bytes }
30
- @client.ping('12345678')
31
-
32
- frame = f.parse(frames[1])
33
- expect(frame[:type]).to eq :settings
34
- expect(frame[:payload]).to include([:settings_max_concurrent_streams, 200])
35
- end
36
-
37
- it 'should initialize client when receiving server settings before sending ack' do
38
- frames = []
39
- @client.on(:frame) { |bytes| frames << bytes }
40
- @client << f.generate(settings_frame)
41
-
42
- expect(frames[0]).to eq CONNECTION_PREFACE_MAGIC
43
- expect(f.parse(frames[1])[:type]).to eq :settings
44
- ack_frame = f.parse(frames[2])
45
- expect(ack_frame[:type]).to eq :settings
46
- expect(ack_frame[:flags]).to include(:ack)
47
- end
48
- end
49
-
50
- context 'upgrade' do
51
- it 'fails when client has already created streams' do
52
- @client.new_stream
53
- expect { @client.upgrade }.to raise_error(HTTP2::Error::ProtocolError)
54
- end
55
-
56
- it 'sends the preface' do
57
- expect(@client).to receive(:send_connection_preface)
58
- @client.upgrade
59
- end
60
-
61
- it 'initializes the first stream in the half-closed state' do
62
- stream = @client.upgrade
63
- expect(stream.state).to be(:half_closed_local)
64
- end
65
- end
66
-
67
- context 'push' do
68
- it 'should disallow client initiated push' do
69
- expect do
70
- @client.promise({}) {}
71
- end.to raise_error(NoMethodError)
72
- end
73
-
74
- it 'should raise error on PUSH_PROMISE against stream 0' do
75
- expect do
76
- @client << set_stream_id(f.generate(push_promise_frame), 0)
77
- end.to raise_error(ProtocolError)
78
- end
79
-
80
- it 'should raise error on PUSH_PROMISE against bogus stream' do
81
- expect do
82
- @client << set_stream_id(f.generate(push_promise_frame), 31_415)
83
- end.to raise_error(ProtocolError)
84
- end
85
-
86
- it 'should raise error on PUSH_PROMISE against non-idle stream' do
87
- expect do
88
- s = @client.new_stream
89
- s.send headers_frame
90
-
91
- @client << set_stream_id(f.generate(push_promise_frame), s.id)
92
- @client << set_stream_id(f.generate(push_promise_frame), s.id)
93
- end.to raise_error(ProtocolError)
94
- end
95
-
96
- it 'should emit stream object for received PUSH_PROMISE' do
97
- s = @client.new_stream
98
- s.send headers_frame
99
-
100
- promise = nil
101
- @client.on(:promise) { |stream| promise = stream }
102
- @client << set_stream_id(f.generate(push_promise_frame), s.id)
103
-
104
- expect(promise.id).to eq 2
105
- expect(promise.state).to eq :reserved_remote
106
- end
107
-
108
- it 'should emit promise headers for received PUSH_PROMISE' do
109
- header = nil
110
- s = @client.new_stream
111
- s.send headers_frame
112
-
113
- @client.on(:promise) do |stream|
114
- stream.on(:promise_headers) do |h|
115
- header = h
116
- end
117
- end
118
- @client << set_stream_id(f.generate(push_promise_frame), s.id)
119
-
120
- expect(header).to be_a(Array)
121
- # expect(header).to eq([%w(a b)])
122
- end
123
-
124
- it 'should auto RST_STREAM promises against locally-RST stream' do
125
- s = @client.new_stream
126
- s.send headers_frame
127
- s.close
128
-
129
- allow(@client).to receive(:send)
130
- expect(@client).to receive(:send) do |frame|
131
- expect(frame[:type]).to eq :rst_stream
132
- expect(frame[:stream]).to eq 2
133
- end
134
-
135
- @client << set_stream_id(f.generate(push_promise_frame), s.id)
136
- end
137
- end
138
-
139
- context 'alt-svc' do
140
- context 'received in the connection' do
141
- it 'should emit :altsvc when receiving one' do
142
- @client << f.generate(settings_frame)
143
- frame = nil
144
- @client.on(:altsvc) do |f|
145
- frame = f
146
- end
147
- @client << f.generate(altsvc_frame)
148
- expect(frame).to be_a(Hash)
149
- end
150
- it 'should not emit :altsvc when the frame when contains no host' do
151
- @client << f.generate(settings_frame)
152
- frame = nil
153
- @client.on(:altsvc) do |f|
154
- frame = f
155
- end
156
-
157
- @client << f.generate(altsvc_frame.merge(origin: nil))
158
- expect(frame).to be_nil
159
- end
160
- end
161
- context 'received in a stream' do
162
- it 'should emit :altsvc' do
163
- s = @client.new_stream
164
- s.send headers_frame
165
- s.close
166
-
167
- frame = nil
168
- s.on(:altsvc) { |f| frame = f }
169
-
170
- @client << set_stream_id(f.generate(altsvc_frame.merge(origin: nil)), s.id)
171
-
172
- expect(frame).to be_a(Hash)
173
- end
174
- it 'should not emit :alt_svc when the frame when contains a origin' do
175
- s = @client.new_stream
176
- s.send headers_frame
177
- s.close
178
-
179
- frame = nil
180
- s.on(:altsvc) { |f| frame = f }
181
-
182
- @client << set_stream_id(f.generate(altsvc_frame), s.id)
183
-
184
- expect(frame).to be_nil
185
- end
186
- end
187
- end
188
- end