protocol-hpack 1.4.3 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/protocol/hpack/compressor.rb +9 -11
- data/lib/protocol/hpack/context.rb +47 -19
- data/lib/protocol/hpack/decompressor.rb +5 -3
- data/lib/protocol/hpack/huffman/generator.rb +186 -0
- data/lib/protocol/hpack/huffman/machine.rb +254 -254
- data/lib/protocol/hpack/huffman.rb +14 -11
- data/lib/protocol/hpack/version.rb +1 -1
- data/lib/protocol/hpack.rb +2 -2
- data/license.md +2 -0
- data/readme.md +8 -8
- data.tar.gz.sig +0 -0
- metadata +8 -6
- metadata.gz.sig +0 -0
- data/tasks/huffman.rake +0 -10
- data/tasks/huffman.rb +0 -173
@@ -6,9 +6,10 @@
|
|
6
6
|
# Copyright, 2015, by Tamir Duberstein.
|
7
7
|
# Copyright, 2018-2024, by Samuel Williams.
|
8
8
|
# Copyright, 2022, by Daniel Morrison.
|
9
|
+
# Copyright, 2024, by Nathan Froyd.
|
9
10
|
|
10
|
-
require_relative
|
11
|
-
require_relative
|
11
|
+
require_relative "huffman/machine"
|
12
|
+
require_relative "error"
|
12
13
|
|
13
14
|
module Protocol
|
14
15
|
module HPACK
|
@@ -22,10 +23,10 @@ module Protocol
|
|
22
23
|
#
|
23
24
|
# @param str [String]
|
24
25
|
# @return [String] binary string
|
25
|
-
def encode(str)
|
26
|
+
def self.encode(str)
|
26
27
|
bitstring = str.each_byte.map {|chr| ENCODE_TABLE[chr]}.join
|
27
|
-
bitstring <<
|
28
|
-
[bitstring].pack(
|
28
|
+
bitstring << "1" * ((8 - bitstring.size) % 8)
|
29
|
+
[bitstring].pack("B*")
|
29
30
|
end
|
30
31
|
|
31
32
|
# Decodes provided Huffman coded string.
|
@@ -33,14 +34,15 @@ module Protocol
|
|
33
34
|
# @param buf [Buffer]
|
34
35
|
# @return [String] binary string
|
35
36
|
# @raise [CompressionError] when Huffman coded string is malformed
|
36
|
-
def decode(buffer)
|
37
|
+
def self.decode(buffer)
|
37
38
|
emit = String.new.b
|
38
39
|
state = 0 # start state
|
39
40
|
|
40
41
|
mask = (1 << BITS_AT_ONCE) - 1
|
41
42
|
buffer.each_byte do |c|
|
42
|
-
|
43
|
-
|
43
|
+
shift = BITS_AT_ONCE
|
44
|
+
while shift >= 0
|
45
|
+
branch = (c >> shift) & mask
|
44
46
|
|
45
47
|
# MACHINE[state] = [final, [transitions]]
|
46
48
|
# [final] unfinished bits so far are prefix of the EOS code.
|
@@ -49,14 +51,15 @@ module Protocol
|
|
49
51
|
# [next] next state number.
|
50
52
|
value, state = MACHINE[state][branch]
|
51
53
|
|
52
|
-
raise CompressionError,
|
54
|
+
raise CompressionError, "Huffman decode error (EOS found)" if value == EOS
|
53
55
|
|
54
56
|
emit << value.chr if value
|
57
|
+
shift -= BITS_AT_ONCE
|
55
58
|
end
|
56
59
|
end
|
57
60
|
# Check whether partial input is correctly filled
|
58
61
|
unless state <= MAX_FINAL_STATE
|
59
|
-
raise CompressionError,
|
62
|
+
raise CompressionError, "Huffman decode error (EOS invalid)"
|
60
63
|
end
|
61
64
|
emit.force_encoding(Encoding::BINARY)
|
62
65
|
end
|
@@ -322,7 +325,7 @@ module Protocol
|
|
322
325
|
[0x3fffffff, 30],
|
323
326
|
].each(&:freeze).freeze
|
324
327
|
|
325
|
-
ENCODE_TABLE = CODES.map {|c, l| [c].pack(
|
328
|
+
ENCODE_TABLE = CODES.map {|c, l| [c].pack("N").unpack1("B*")[-l..-1]}.each(&:freeze).freeze
|
326
329
|
end
|
327
330
|
end
|
328
331
|
end
|
data/lib/protocol/hpack.rb
CHANGED
data/license.md
CHANGED
@@ -15,6 +15,8 @@ Copyright, 2020, by Olle Jonsson.
|
|
15
15
|
Copyright, 2020, by Justin Mazzocchi.
|
16
16
|
Copyright, 2022, by Daniel Morrison.
|
17
17
|
Copyright, 2022, by Felix Yan.
|
18
|
+
Copyright, 2024, by Maruth Goyal.
|
19
|
+
Copyright, 2024, by Nathan Froyd.
|
18
20
|
|
19
21
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
20
22
|
of this software and associated documentation files (the "Software"), to deal
|
data/readme.md
CHANGED
@@ -10,6 +10,11 @@ Please see the [project documentation](https://socketry.github.io/protocol-hpack
|
|
10
10
|
|
11
11
|
- [Getting Started](https://socketry.github.io/protocol-hpack/guides/getting-started/index) - This guide explains how to use the `protocol-hpack` gem to compress and decompress HTTP/2 headers using HPACK.
|
12
12
|
|
13
|
+
## See Also
|
14
|
+
|
15
|
+
- [protocol-http2](https://github.com/socketry/protocol-http2) - Provides an HTTP/2 client and server implementation.
|
16
|
+
- [async-http](https://github.com/socketry/async-http) - Provides a complete HTTP client and server implementation on top of Async.
|
17
|
+
|
13
18
|
## Contributing
|
14
19
|
|
15
20
|
We welcome contributions to this project.
|
@@ -22,13 +27,8 @@ We welcome contributions to this project.
|
|
22
27
|
|
23
28
|
### Developer Certificate of Origin
|
24
29
|
|
25
|
-
|
30
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
26
31
|
|
27
|
-
###
|
28
|
-
|
29
|
-
This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
|
30
|
-
|
31
|
-
## See Also
|
32
|
+
### Community Guidelines
|
32
33
|
|
33
|
-
|
34
|
-
- [async-http](https://github.com/socketry/async-http) - Provides a complete HTTP client and server implementation on top of Async.
|
34
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protocol-hpack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
- Ilya Grigorik
|
9
9
|
- Tamir Duberstein
|
10
|
+
- Nathan Froyd
|
10
11
|
- Kaoru Maeda
|
11
12
|
- Tiago Cardoso
|
12
13
|
- Byron Formwalt
|
@@ -18,6 +19,7 @@ authors:
|
|
18
19
|
- Justin Mazzocchi
|
19
20
|
- Kenichi Nakamura
|
20
21
|
- Kien Nguyen Trung
|
22
|
+
- Maruth Goyal
|
21
23
|
- Olle Jonsson
|
22
24
|
autorequire:
|
23
25
|
bindir: bin
|
@@ -51,7 +53,7 @@ cert_chain:
|
|
51
53
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
52
54
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
53
55
|
-----END CERTIFICATE-----
|
54
|
-
date: 2024-
|
56
|
+
date: 2024-09-13 00:00:00.000000000 Z
|
55
57
|
dependencies: []
|
56
58
|
description:
|
57
59
|
email:
|
@@ -65,17 +67,17 @@ files:
|
|
65
67
|
- lib/protocol/hpack/decompressor.rb
|
66
68
|
- lib/protocol/hpack/error.rb
|
67
69
|
- lib/protocol/hpack/huffman.rb
|
70
|
+
- lib/protocol/hpack/huffman/generator.rb
|
68
71
|
- lib/protocol/hpack/huffman/machine.rb
|
69
72
|
- lib/protocol/hpack/version.rb
|
70
73
|
- license.md
|
71
74
|
- readme.md
|
72
|
-
- tasks/huffman.rake
|
73
|
-
- tasks/huffman.rb
|
74
75
|
homepage: https://github.com/socketry/http-hpack
|
75
76
|
licenses:
|
76
77
|
- MIT
|
77
78
|
metadata:
|
78
79
|
documentation_uri: https://socketry.github.io/protocol-hpack/
|
80
|
+
source_code_uri: https://github.com/socketry/http-hpack.git
|
79
81
|
post_install_message:
|
80
82
|
rdoc_options: []
|
81
83
|
require_paths:
|
@@ -84,14 +86,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
86
|
requirements:
|
85
87
|
- - ">="
|
86
88
|
- !ruby/object:Gem::Version
|
87
|
-
version: '3.
|
89
|
+
version: '3.1'
|
88
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
91
|
requirements:
|
90
92
|
- - ">="
|
91
93
|
- !ruby/object:Gem::Version
|
92
94
|
version: '0'
|
93
95
|
requirements: []
|
94
|
-
rubygems_version: 3.5.
|
96
|
+
rubygems_version: 3.5.11
|
95
97
|
signing_key:
|
96
98
|
specification_version: 4
|
97
99
|
summary: A compresssor and decompressor for HTTP/2's HPACK format.
|
metadata.gz.sig
CHANGED
Binary file
|
data/tasks/huffman.rake
DELETED
data/tasks/huffman.rb
DELETED
@@ -1,173 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2014, by Kaoru Maeda.
|
5
|
-
# Copyright, 2015, by Tamir Duberstein.
|
6
|
-
# Copyright, 2015, by Ilya Grigorik.
|
7
|
-
# Copyright, 2016, by George Ulmer.
|
8
|
-
# Copyright, 2018-2024, by Samuel Williams.
|
9
|
-
|
10
|
-
require_relative '../lib/http/hpack/huffman'
|
11
|
-
|
12
|
-
require 'set'
|
13
|
-
|
14
|
-
module Huffman
|
15
|
-
BITS_AT_ONCE = HTTP::HPACK::Huffman::BITS_AT_ONCE
|
16
|
-
EOS = 256
|
17
|
-
|
18
|
-
class Node
|
19
|
-
attr_accessor :next, :emit, :final, :depth
|
20
|
-
attr_accessor :transitions
|
21
|
-
attr_accessor :id
|
22
|
-
@@id = 0 # rubocop:disable Style/ClassVars
|
23
|
-
def initialize(depth)
|
24
|
-
@next = [nil, nil]
|
25
|
-
@id = @@id
|
26
|
-
@@id += 1 # rubocop:disable Style/ClassVars
|
27
|
-
@final = false
|
28
|
-
@depth = depth
|
29
|
-
end
|
30
|
-
|
31
|
-
def add(code, len, chr)
|
32
|
-
self.final = true if chr == EOS && @depth <= 7
|
33
|
-
if len.zero?
|
34
|
-
@emit = chr
|
35
|
-
else
|
36
|
-
bit = (code & (1 << (len - 1))).zero? ? 0 : 1
|
37
|
-
node = @next[bit] ||= Node.new(@depth + 1)
|
38
|
-
node.add(code, len - 1, chr)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
class Transition
|
43
|
-
attr_accessor :emit, :node
|
44
|
-
def initialize(emit, node)
|
45
|
-
@emit = emit
|
46
|
-
@node = node
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.generate_tree
|
51
|
-
@root = new(0)
|
52
|
-
HTTP::HPACK::Huffman::CODES.each_with_index do |c, chr|
|
53
|
-
code, len = c
|
54
|
-
@root.add(code, len, chr)
|
55
|
-
end
|
56
|
-
puts "#{@@id} nodes"
|
57
|
-
@root
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.generate_machine
|
61
|
-
generate_tree
|
62
|
-
togo = Set[@root]
|
63
|
-
@states = Set[@root]
|
64
|
-
|
65
|
-
until togo.empty?
|
66
|
-
node = togo.first
|
67
|
-
togo.delete(node)
|
68
|
-
|
69
|
-
next if node.transitions
|
70
|
-
node.transitions = Array[1 << BITS_AT_ONCE]
|
71
|
-
|
72
|
-
(1 << BITS_AT_ONCE).times do |input|
|
73
|
-
n = node
|
74
|
-
emit = ''
|
75
|
-
(BITS_AT_ONCE - 1).downto(0) do |i|
|
76
|
-
bit = (input & (1 << i)).zero? ? 0 : 1
|
77
|
-
n = n.next[bit]
|
78
|
-
next unless n.emit
|
79
|
-
if n.emit == EOS
|
80
|
-
emit = EOS # cause error on decoding
|
81
|
-
else
|
82
|
-
emit << n.emit.chr(Encoding::BINARY) unless emit == EOS
|
83
|
-
end
|
84
|
-
n = @root
|
85
|
-
end
|
86
|
-
node.transitions[input] = Transition.new(emit, n)
|
87
|
-
togo << n
|
88
|
-
@states << n
|
89
|
-
end
|
90
|
-
end
|
91
|
-
puts "#{@states.size} states"
|
92
|
-
@root
|
93
|
-
end
|
94
|
-
|
95
|
-
def self.generate_state_table
|
96
|
-
generate_machine
|
97
|
-
state_id = {}
|
98
|
-
id_state = {}
|
99
|
-
state_id[@root] = 0
|
100
|
-
id_state[0] = @root
|
101
|
-
max_final = 0
|
102
|
-
id = 1
|
103
|
-
(@states - [@root]).sort_by {|s| s.final ? 0 : 1}.each do |s|
|
104
|
-
state_id[s] = id
|
105
|
-
id_state[id] = s
|
106
|
-
max_final = id if s.final
|
107
|
-
id += 1
|
108
|
-
end
|
109
|
-
|
110
|
-
File.open(File.expand_path('../lib/http/hpack/huffman/machine.rb', File.dirname(__FILE__)), 'w') do |f|
|
111
|
-
f.print <<HEADER
|
112
|
-
# frozen_string_literal: true
|
113
|
-
|
114
|
-
# Released under the MIT License.
|
115
|
-
# Copyright, 2018-2024, by Samuel Williams.
|
116
|
-
|
117
|
-
# Machine generated Huffman decoder state machine.
|
118
|
-
# DO NOT EDIT THIS FILE.
|
119
|
-
|
120
|
-
module Protocol
|
121
|
-
module HPACK
|
122
|
-
class Huffman
|
123
|
-
# :nodoc:
|
124
|
-
MAX_FINAL_STATE = #{max_final}
|
125
|
-
MACHINE = [
|
126
|
-
HEADER
|
127
|
-
id.times do |i|
|
128
|
-
n = id_state[i]
|
129
|
-
f.print "\t\t\t\t["
|
130
|
-
string = (1 << BITS_AT_ONCE).times.map do |t|
|
131
|
-
transition = n.transitions.fetch(t)
|
132
|
-
emit = transition.emit
|
133
|
-
unless emit == EOS
|
134
|
-
bytes = emit.bytes
|
135
|
-
fail ArgumentError if bytes.size > 1
|
136
|
-
emit = bytes.first
|
137
|
-
end
|
138
|
-
"[#{emit.inspect}, #{state_id.fetch(transition.node)}]"
|
139
|
-
end.join(', ')
|
140
|
-
f.print(string)
|
141
|
-
f.print "],\n"
|
142
|
-
end
|
143
|
-
f.print <<TAILER
|
144
|
-
].each {|arr| arr.each {|subarr| subarr.each(&:freeze)}.freeze}.freeze
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
TAILER
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
class << self
|
153
|
-
attr_reader :root
|
154
|
-
end
|
155
|
-
|
156
|
-
# Test decoder
|
157
|
-
def self.decode(input)
|
158
|
-
emit = ''
|
159
|
-
n = root
|
160
|
-
nibbles = input.unpack('C*').flat_map {|b| [((b & 0xf0) >> 4), b & 0xf]}
|
161
|
-
until nibbles.empty?
|
162
|
-
nb = nibbles.shift
|
163
|
-
t = n.transitions[nb]
|
164
|
-
emit << t.emit
|
165
|
-
n = t.node
|
166
|
-
end
|
167
|
-
unless n.final && nibbles.all? {|x| x == 0xf}
|
168
|
-
puts "len = #{emit.size} n.final = #{n.final} nibbles = #{nibbles}"
|
169
|
-
end
|
170
|
-
emit
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|