http-2 0.11.0 → 1.0.0

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