http-2-next 0.1.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 +7 -0
- data/README.md +302 -0
- data/lib/http/2/next.rb +15 -0
- data/lib/http/2/next/buffer.rb +78 -0
- data/lib/http/2/next/client.rb +72 -0
- data/lib/http/2/next/compressor.rb +619 -0
- data/lib/http/2/next/connection.rb +757 -0
- data/lib/http/2/next/emitter.rb +48 -0
- data/lib/http/2/next/error.rb +59 -0
- data/lib/http/2/next/flow_buffer.rb +119 -0
- data/lib/http/2/next/framer.rb +445 -0
- data/lib/http/2/next/huffman.rb +325 -0
- data/lib/http/2/next/huffman_statemachine.rb +274 -0
- data/lib/http/2/next/server.rb +135 -0
- data/lib/http/2/next/stream.rb +692 -0
- data/lib/http/2/next/version.rb +5 -0
- data/lib/tasks/generate_huffman_table.rb +169 -0
- metadata +61 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
desc "Generate Huffman precompiled table in huffman_statemachine.rb"
|
4
|
+
task :generate_table do
|
5
|
+
HuffmanTable::Node.generate_state_table
|
6
|
+
end
|
7
|
+
|
8
|
+
require_relative "../http/2/next/huffman"
|
9
|
+
|
10
|
+
# @private
|
11
|
+
module HuffmanTable
|
12
|
+
BITS_AT_ONCE = HTTP2Next::Header::Huffman::BITS_AT_ONCE
|
13
|
+
EOS = 256
|
14
|
+
|
15
|
+
class Node
|
16
|
+
attr_accessor :next, :emit, :final, :depth
|
17
|
+
attr_accessor :transitions
|
18
|
+
attr_accessor :id
|
19
|
+
@@id = 0 # rubocop:disable Style/ClassVars
|
20
|
+
def initialize(depth)
|
21
|
+
@next = [nil, nil]
|
22
|
+
@id = @@id
|
23
|
+
@@id += 1 # rubocop:disable Style/ClassVars
|
24
|
+
@final = false
|
25
|
+
@depth = depth
|
26
|
+
end
|
27
|
+
|
28
|
+
def add(code, len, chr)
|
29
|
+
self.final = true if chr == EOS && @depth <= 7
|
30
|
+
if len.zero?
|
31
|
+
@emit = chr
|
32
|
+
else
|
33
|
+
bit = (code & (1 << (len - 1))).zero? ? 0 : 1
|
34
|
+
node = @next[bit] ||= Node.new(@depth + 1)
|
35
|
+
node.add(code, len - 1, chr)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Transition
|
40
|
+
attr_accessor :emit, :node
|
41
|
+
def initialize(emit, node)
|
42
|
+
@emit = emit
|
43
|
+
@node = node
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.generate_tree
|
48
|
+
@root = new(0)
|
49
|
+
HTTP2Next::Header::Huffman::CODES.each_with_index do |c, chr|
|
50
|
+
code, len = c
|
51
|
+
@root.add(code, len, chr)
|
52
|
+
end
|
53
|
+
puts "#{@@id} nodes"
|
54
|
+
@root
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.generate_machine
|
58
|
+
generate_tree
|
59
|
+
togo = Set[@root]
|
60
|
+
@states = Set[@root]
|
61
|
+
|
62
|
+
until togo.empty?
|
63
|
+
node = togo.first
|
64
|
+
togo.delete(node)
|
65
|
+
|
66
|
+
next if node.transitions
|
67
|
+
|
68
|
+
node.transitions = Array[1 << BITS_AT_ONCE]
|
69
|
+
|
70
|
+
(1 << BITS_AT_ONCE).times do |input|
|
71
|
+
n = node
|
72
|
+
emit = ""
|
73
|
+
(BITS_AT_ONCE - 1).downto(0) do |i|
|
74
|
+
bit = (input & (1 << i)).zero? ? 0 : 1
|
75
|
+
n = n.next[bit]
|
76
|
+
next unless n.emit
|
77
|
+
|
78
|
+
if n.emit == EOS
|
79
|
+
emit = EOS # cause error on decoding
|
80
|
+
else
|
81
|
+
emit << n.emit.chr(Encoding::BINARY) unless emit == EOS
|
82
|
+
end
|
83
|
+
n = @root
|
84
|
+
end
|
85
|
+
node.transitions[input] = Transition.new(emit, n)
|
86
|
+
togo << n
|
87
|
+
@states << n
|
88
|
+
end
|
89
|
+
end
|
90
|
+
puts "#{@states.size} states"
|
91
|
+
@root
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.generate_state_table
|
95
|
+
generate_machine
|
96
|
+
state_id = {}
|
97
|
+
id_state = {}
|
98
|
+
state_id[@root] = 0
|
99
|
+
id_state[0] = @root
|
100
|
+
max_final = 0
|
101
|
+
id = 1
|
102
|
+
(@states - [@root]).sort_by { |s| s.final ? 0 : 1 }.each do |s|
|
103
|
+
state_id[s] = id
|
104
|
+
id_state[id] = s
|
105
|
+
max_final = id if s.final
|
106
|
+
id += 1
|
107
|
+
end
|
108
|
+
|
109
|
+
File.open(File.expand_path("../http/2/huffman_statemachine.rb", File.dirname(__FILE__)), "w") do |f|
|
110
|
+
f.print <<HEADER
|
111
|
+
# Machine generated Huffman decoder state machine.
|
112
|
+
# DO NOT EDIT THIS FILE.
|
113
|
+
|
114
|
+
# The following task generates this file.
|
115
|
+
# rake generate_huffman_table
|
116
|
+
|
117
|
+
module HTTP2Next
|
118
|
+
module Header
|
119
|
+
class Huffman
|
120
|
+
# :nodoc:
|
121
|
+
MAX_FINAL_STATE = #{max_final}
|
122
|
+
MACHINE = [
|
123
|
+
HEADER
|
124
|
+
id.times do |i|
|
125
|
+
n = id_state[i]
|
126
|
+
f.print " ["
|
127
|
+
string = (1 << BITS_AT_ONCE).times.map do |t|
|
128
|
+
transition = n.transitions.fetch(t)
|
129
|
+
emit = transition.emit
|
130
|
+
unless emit == EOS
|
131
|
+
bytes = emit.bytes
|
132
|
+
raise ArgumentError if bytes.size > 1
|
133
|
+
|
134
|
+
emit = bytes.first
|
135
|
+
end
|
136
|
+
"[#{emit.inspect}, #{state_id.fetch(transition.node)}]"
|
137
|
+
end.join(", ")
|
138
|
+
f.print(string)
|
139
|
+
f.print "],\n"
|
140
|
+
end
|
141
|
+
f.print <<TAILER
|
142
|
+
].each { |arr| arr.each { |subarr| subarr.each(&:freeze) }.freeze }.freeze
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
TAILER
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class << self
|
151
|
+
attr_reader :root
|
152
|
+
end
|
153
|
+
|
154
|
+
# Test decoder
|
155
|
+
def self.decode(input)
|
156
|
+
emit = ""
|
157
|
+
n = root
|
158
|
+
nibbles = input.unpack("C*").flat_map { |b| [((b & 0xf0) >> 4), b & 0xf] }
|
159
|
+
until nibbles.empty?
|
160
|
+
nb = nibbles.shift
|
161
|
+
t = n.transitions[nb]
|
162
|
+
emit << t.emit
|
163
|
+
n = t.node
|
164
|
+
end
|
165
|
+
puts "len = #{emit.size} n.final = #{n.final} nibbles = #{nibbles}" unless n.final && nibbles.all? { |x| x == 0xf }
|
166
|
+
emit
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: http-2-next
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tiago Cardoso
|
8
|
+
- Ilya Grigorik
|
9
|
+
- Kaoru Maeda
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2019-11-13 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Pure-ruby HTTP 2.0 protocol implementation
|
16
|
+
email:
|
17
|
+
- cardoso_tiago@hotmail.com
|
18
|
+
executables: []
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- README.md
|
23
|
+
- lib/http/2/next.rb
|
24
|
+
- lib/http/2/next/buffer.rb
|
25
|
+
- lib/http/2/next/client.rb
|
26
|
+
- lib/http/2/next/compressor.rb
|
27
|
+
- lib/http/2/next/connection.rb
|
28
|
+
- lib/http/2/next/emitter.rb
|
29
|
+
- lib/http/2/next/error.rb
|
30
|
+
- lib/http/2/next/flow_buffer.rb
|
31
|
+
- lib/http/2/next/framer.rb
|
32
|
+
- lib/http/2/next/huffman.rb
|
33
|
+
- lib/http/2/next/huffman_statemachine.rb
|
34
|
+
- lib/http/2/next/server.rb
|
35
|
+
- lib/http/2/next/stream.rb
|
36
|
+
- lib/http/2/next/version.rb
|
37
|
+
- lib/tasks/generate_huffman_table.rb
|
38
|
+
homepage: https://gitlab.com/honeyryderchuck/http-2-next
|
39
|
+
licenses:
|
40
|
+
- MIT
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.1.0
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubygems_version: 3.0.6
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: Pure-ruby HTTP 2.0 protocol implementation
|
61
|
+
test_files: []
|