http-2-next 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTP2Next
4
+ VERSION = "0.1.0"
5
+ end
@@ -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: []