httpunk 0.4.5__tar.gz → 0.5.0__tar.gz
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.
- {httpunk-0.4.5 → httpunk-0.5.0}/Cargo.lock +47 -22
- {httpunk-0.4.5 → httpunk-0.5.0}/Cargo.toml +14 -12
- {httpunk-0.4.5 → httpunk-0.5.0}/PKG-INFO +2 -3
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/Cargo.toml +2 -2
- httpunk-0.5.0/crates/vendor-h2/UPSTREAM_VERSION +1 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/go_away.rs +11 -1
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/headers.rs +2 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/mod.rs +2 -2
- httpunk-0.5.0/crates/vendor-h2/src/hpack/huffman/decode.rs +349 -0
- httpunk-0.5.0/crates/vendor-h2/src/hpack/huffman/encode.rs +124 -0
- httpunk-0.5.0/crates/vendor-h2/src/hpack/huffman/mod.rs +100 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/hpack/huffman/table.rs +42 -259
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/hpack/table.rs +6 -11
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/lib.rs +9 -10
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/proto/mod.rs +1 -1
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/Cargo.toml +2 -1
- httpunk-0.5.0/crates/vendor-hyper/UPSTREAM_VERSION +1 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/ext/h1_reason_phrase.rs +5 -1
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/ext/informational.rs +1 -1
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/ext/mod.rs +5 -40
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/lib.rs +12 -9
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/proto/h1/decode.rs +12 -9
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/proto/h1/encode.rs +18 -14
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/proto/h1/httpunk.rs +96 -63
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/proto/h1/mod.rs +1 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/proto/h1/role.rs +400 -14
- httpunk-0.5.0/crates/vendor-hyper-util/Cargo.toml +25 -0
- httpunk-0.5.0/crates/vendor-hyper-util/UPSTREAM_VERSION +1 -0
- httpunk-0.5.0/crates/vendor-hyper-util/src/client/mod.rs +32 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper-util/src/client/proxy/matcher.rs +80 -30
- httpunk-0.5.0/crates/vendor-hyper-util/src/client/proxy/mod.rs +3 -0
- httpunk-0.5.0/crates/vendor-hyper-util/src/lib.rs +19 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/_backend/asyncio.py +12 -10
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/_backend/tonio.py +4 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/_common.py +14 -3
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/_httpunk.pyi +55 -14
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/asyncio.py +5 -5
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/exceptions.py +1 -1
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h1/client.py +100 -43
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h1/connection.py +7 -7
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h1/server.py +82 -36
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h1/share.py +3 -3
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h2/client.py +79 -19
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h2/connection.py +34 -19
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h2/server.py +16 -8
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h2/share.py +2 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h2/stream.py +2 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/types.py +2 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/util/auto.py +9 -3
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/util/client.py +1 -1
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/util/graceful.py +2 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/util/pool.py +104 -76
- {httpunk-0.4.5 → httpunk-0.5.0}/pyproject.toml +1 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h1/codec.rs +135 -39
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h1/conn.rs +27 -11
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h1/errors.rs +3 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h2/codec.rs +41 -28
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h2/conn.rs +193 -95
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h2/errors.rs +23 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h2/reason.rs +1 -8
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h2/settings.rs +4 -4
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h2/streams.rs +27 -23
- {httpunk-0.4.5 → httpunk-0.5.0}/src/http/mod.rs +2 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/src/py.rs +3 -5
- httpunk-0.5.0/tests/_net.py +285 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/conftest.py +20 -4
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_asyncio_backend.py +6 -6
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_asyncio_e2e.py +108 -10
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_asyncio_protocol.py +5 -5
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_backend.py +5 -3
- httpunk-0.5.0/tests/test_h1_builder_options.py +163 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h1_codec.py +18 -6
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h1_conn.py +234 -420
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h1_decode.py +2 -2
- httpunk-0.5.0/tests/test_h1_max_header_size.py +75 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h1_server.py +344 -565
- httpunk-0.5.0/tests/test_h2_builder_options.py +200 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h2_codec.py +1 -1
- httpunk-0.5.0/tests/test_h2_conn.py +45 -0
- httpunk-0.5.0/tests/test_h2_errors.py +695 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h2_flow.py +56 -110
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h2_ready.py +2 -2
- httpunk-0.5.0/tests/test_h2_refusal.py +84 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h2_server.py +260 -460
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h2_settings.py +2 -2
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h2_write_batch.py +3 -4
- httpunk-0.5.0/tests/test_handshake_abort.py +134 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_util_auto.py +5 -10
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_util_graceful.py +8 -15
- httpunk-0.5.0/tests/test_util_pool.py +564 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_util_tls.py +12 -28
- httpunk-0.4.5/crates/vendor-h2/UPSTREAM_VERSION +0 -1
- httpunk-0.4.5/crates/vendor-h2/src/hpack/huffman/mod.rs +0 -263
- httpunk-0.4.5/crates/vendor-hyper/UPSTREAM_VERSION +0 -1
- httpunk-0.4.5/crates/vendor-hyper-util/Cargo.toml +0 -22
- httpunk-0.4.5/crates/vendor-hyper-util/UPSTREAM_VERSION +0 -1
- httpunk-0.4.5/crates/vendor-hyper-util/src/client/mod.rs +0 -4
- httpunk-0.4.5/crates/vendor-hyper-util/src/client/proxy/mod.rs +0 -5
- httpunk-0.4.5/crates/vendor-hyper-util/src/lib.rs +0 -18
- httpunk-0.4.5/tests/_client.py +0 -25
- httpunk-0.4.5/tests/test_h2_conn.py +0 -91
- httpunk-0.4.5/tests/test_h2_errors.py +0 -881
- httpunk-0.4.5/tests/test_util_pool.py +0 -402
- {httpunk-0.4.5 → httpunk-0.5.0}/LICENSE +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/README.md +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/LICENSE +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/codec/error.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/codec/mod.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/error.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/ext.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/data.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/head.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/ping.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/priority.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/reason.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/reset.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/settings.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/stream_id.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/util.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/frame/window_update.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/hpack/decoder.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/hpack/encoder.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/hpack/header.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/hpack/mod.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/proto/error.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/proto/streams/flow_control.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/proto/streams/mod.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-h2/src/proto/streams/state.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/LICENSE +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/body/length.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/body/mod.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/cfg.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/common/date.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/common/mod.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/error.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/headers.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/proto/h1/io.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/proto/mod.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper/src/trace.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/crates/vendor-hyper-util/LICENSE +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/__init__.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/_backend/__init__.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h1/__init__.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/h2/__init__.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/http.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/py.typed +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/util/__init__.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/httpunk/util/proxy.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/src/errors.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h1/mod.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/src/h2/mod.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/src/lib.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/src/proxy.rs +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/_transport.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_asyncio_client_protocol.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_error_retention.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h2_flow_control.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_h2_state.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_headermap.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_util_connect.py +0 -0
- {httpunk-0.4.5 → httpunk-0.5.0}/tests/test_util_proxy.py +0 -0
|
@@ -10,9 +10,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
|
|
10
10
|
|
|
11
11
|
[[package]]
|
|
12
12
|
name = "base64"
|
|
13
|
-
version = "0.
|
|
13
|
+
version = "0.23.1"
|
|
14
14
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
-
checksum = "
|
|
15
|
+
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
|
|
16
16
|
|
|
17
17
|
[[package]]
|
|
18
18
|
name = "bytes"
|
|
@@ -71,9 +71,9 @@ checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d"
|
|
|
71
71
|
|
|
72
72
|
[[package]]
|
|
73
73
|
name = "h2"
|
|
74
|
-
version = "0.4.
|
|
74
|
+
version = "0.4.20"
|
|
75
75
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
76
|
-
checksum = "
|
|
76
|
+
checksum = "7d29020232d6aa3fb1daca64c1127cf662cf97f254ae16c18c05b8ab635fc118"
|
|
77
77
|
dependencies = [
|
|
78
78
|
"atomic-waker",
|
|
79
79
|
"bytes",
|
|
@@ -134,12 +134,13 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|
|
134
134
|
|
|
135
135
|
[[package]]
|
|
136
136
|
name = "httpunk"
|
|
137
|
-
version = "0.
|
|
137
|
+
version = "0.5.0"
|
|
138
138
|
dependencies = [
|
|
139
139
|
"bytes",
|
|
140
140
|
"h2",
|
|
141
141
|
"http",
|
|
142
142
|
"hyper",
|
|
143
|
+
"hyper-util",
|
|
143
144
|
"mimalloc",
|
|
144
145
|
"pyo3",
|
|
145
146
|
"vendor-h2",
|
|
@@ -149,13 +150,12 @@ dependencies = [
|
|
|
149
150
|
|
|
150
151
|
[[package]]
|
|
151
152
|
name = "hyper"
|
|
152
|
-
version = "1.
|
|
153
|
+
version = "1.12.0"
|
|
153
154
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
154
|
-
checksum = "
|
|
155
|
+
checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c"
|
|
155
156
|
dependencies = [
|
|
156
157
|
"atomic-waker",
|
|
157
158
|
"bytes",
|
|
158
|
-
"futures-channel",
|
|
159
159
|
"futures-core",
|
|
160
160
|
"http",
|
|
161
161
|
"http-body",
|
|
@@ -168,6 +168,25 @@ dependencies = [
|
|
|
168
168
|
"want",
|
|
169
169
|
]
|
|
170
170
|
|
|
171
|
+
[[package]]
|
|
172
|
+
name = "hyper-util"
|
|
173
|
+
version = "0.1.21"
|
|
174
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
175
|
+
checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff"
|
|
176
|
+
dependencies = [
|
|
177
|
+
"base64",
|
|
178
|
+
"bytes",
|
|
179
|
+
"futures-channel",
|
|
180
|
+
"http",
|
|
181
|
+
"http-body",
|
|
182
|
+
"hyper",
|
|
183
|
+
"ipnet",
|
|
184
|
+
"percent-encoding",
|
|
185
|
+
"pin-project-lite",
|
|
186
|
+
"tower-service",
|
|
187
|
+
"tracing",
|
|
188
|
+
]
|
|
189
|
+
|
|
171
190
|
[[package]]
|
|
172
191
|
name = "indexmap"
|
|
173
192
|
version = "2.14.0"
|
|
@@ -249,9 +268,9 @@ dependencies = [
|
|
|
249
268
|
|
|
250
269
|
[[package]]
|
|
251
270
|
name = "pyo3"
|
|
252
|
-
version = "0.
|
|
271
|
+
version = "0.30.1"
|
|
253
272
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
254
|
-
checksum = "
|
|
273
|
+
checksum = "229ca13b8a15bb0d1487b81ab975bfcd97513416499a8312011b6036e42e0de4"
|
|
255
274
|
dependencies = [
|
|
256
275
|
"libc",
|
|
257
276
|
"once_cell",
|
|
@@ -263,18 +282,18 @@ dependencies = [
|
|
|
263
282
|
|
|
264
283
|
[[package]]
|
|
265
284
|
name = "pyo3-build-config"
|
|
266
|
-
version = "0.
|
|
285
|
+
version = "0.30.1"
|
|
267
286
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
268
|
-
checksum = "
|
|
287
|
+
checksum = "70130e2b6dd1ee589a8788026a429561ca5c40fa7a2fa7914c2282eddae24700"
|
|
269
288
|
dependencies = [
|
|
270
289
|
"target-lexicon",
|
|
271
290
|
]
|
|
272
291
|
|
|
273
292
|
[[package]]
|
|
274
293
|
name = "pyo3-ffi"
|
|
275
|
-
version = "0.
|
|
294
|
+
version = "0.30.1"
|
|
276
295
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
277
|
-
checksum = "
|
|
296
|
+
checksum = "5e7ff73c555c286d212d4ce7e5fbda845a973c4f61bd5f160f5f2c3c2a04d1c4"
|
|
278
297
|
dependencies = [
|
|
279
298
|
"libc",
|
|
280
299
|
"pyo3-build-config",
|
|
@@ -282,9 +301,9 @@ dependencies = [
|
|
|
282
301
|
|
|
283
302
|
[[package]]
|
|
284
303
|
name = "pyo3-macros"
|
|
285
|
-
version = "0.
|
|
304
|
+
version = "0.30.1"
|
|
286
305
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
287
|
-
checksum = "
|
|
306
|
+
checksum = "c46d2e5862c7bb8207e3b639f713dda3c499933444784154c11d013c5a691b60"
|
|
288
307
|
dependencies = [
|
|
289
308
|
"proc-macro2",
|
|
290
309
|
"pyo3-macros-backend",
|
|
@@ -294,9 +313,9 @@ dependencies = [
|
|
|
294
313
|
|
|
295
314
|
[[package]]
|
|
296
315
|
name = "pyo3-macros-backend"
|
|
297
|
-
version = "0.
|
|
316
|
+
version = "0.30.1"
|
|
298
317
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
299
|
-
checksum = "
|
|
318
|
+
checksum = "38b89f066f2ae72a2aef52d1537ca2fcd104440ebfadee06e97aa90738a25b9b"
|
|
300
319
|
dependencies = [
|
|
301
320
|
"heck",
|
|
302
321
|
"proc-macro2",
|
|
@@ -333,9 +352,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
|
|
|
333
352
|
|
|
334
353
|
[[package]]
|
|
335
354
|
name = "syn"
|
|
336
|
-
version = "
|
|
355
|
+
version = "3.0.9"
|
|
337
356
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
338
|
-
checksum = "
|
|
357
|
+
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
|
339
358
|
dependencies = [
|
|
340
359
|
"proc-macro2",
|
|
341
360
|
"quote",
|
|
@@ -372,6 +391,12 @@ dependencies = [
|
|
|
372
391
|
"tokio",
|
|
373
392
|
]
|
|
374
393
|
|
|
394
|
+
[[package]]
|
|
395
|
+
name = "tower-service"
|
|
396
|
+
version = "0.3.3"
|
|
397
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
398
|
+
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
|
399
|
+
|
|
375
400
|
[[package]]
|
|
376
401
|
name = "tracing"
|
|
377
402
|
version = "0.1.44"
|
|
@@ -438,9 +463,9 @@ dependencies = [
|
|
|
438
463
|
|
|
439
464
|
[[package]]
|
|
440
465
|
name = "want"
|
|
441
|
-
version = "0.3.
|
|
466
|
+
version = "0.3.2"
|
|
442
467
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
443
|
-
checksum = "
|
|
468
|
+
checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b"
|
|
444
469
|
dependencies = [
|
|
445
470
|
"try-lock",
|
|
446
471
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "httpunk"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
description = "The Rust HTTP library for Python"
|
|
5
5
|
authors = [
|
|
6
6
|
"Giovanni Barillari <g@baro.dev>",
|
|
@@ -40,25 +40,27 @@ crate-type = ["cdylib"]
|
|
|
40
40
|
|
|
41
41
|
[features]
|
|
42
42
|
mimalloc = ["dep:mimalloc"]
|
|
43
|
-
# Never enabled. Exists only so the optional crates.io `h2`/`hyper`
|
|
44
|
-
# referenced (no "unused optional dependency"), keeping them in Cargo.lock
|
|
45
|
-
# advisory scanning while they stay uncompiled. See the dependency comments.
|
|
46
|
-
_audit = ["dep:h2", "dep:hyper"]
|
|
43
|
+
# Never enabled. Exists only so the optional crates.io `h2`/`hyper`/`hyper-util`
|
|
44
|
+
# deps are referenced (no "unused optional dependency"), keeping them in Cargo.lock
|
|
45
|
+
# for advisory scanning while they stay uncompiled. See the dependency comments.
|
|
46
|
+
_audit = ["dep:h2", "dep:hyper", "dep:hyper-util"]
|
|
47
47
|
|
|
48
48
|
[dependencies]
|
|
49
49
|
bytes = "1"
|
|
50
50
|
http = "1.1"
|
|
51
51
|
mimalloc = { version = "0.1.49", default-features = false, features = ["local_dynamic_tls"], optional = true }
|
|
52
|
-
pyo3 = { version = "=0.
|
|
52
|
+
pyo3 = { version = "=0.30", features = ["extension-module", "generate-import-lib"] }
|
|
53
53
|
vendor-h2 = { path = "crates/vendor-h2" }
|
|
54
54
|
vendor-hyper = { path = "crates/vendor-hyper", features = ["client", "server", "http1"] }
|
|
55
55
|
vendor-hyper-util = { path = "crates/vendor-hyper-util" }
|
|
56
|
-
# NOT compiled: the crates.io `h2`/`hyper
|
|
57
|
-
#
|
|
58
|
-
# the pinned versions land in Cargo.lock
|
|
59
|
-
# coverage without pulling them (and
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
# NOT compiled: the crates.io `h2`/`hyper`/`hyper-util`; their sans-IO code is
|
|
57
|
+
# vendored into crates/vendor-*/ by scripts/vendor-*.sh. Optional + gated behind
|
|
58
|
+
# the never-enabled `_audit` feature so the pinned versions land in Cargo.lock
|
|
59
|
+
# for `cargo audit` / Dependabot advisory coverage without pulling them (and
|
|
60
|
+
# tokio) into the build. hyper-util's client-proxy mirrors the vendored surface.
|
|
61
|
+
h2 = { version = "=0.4.20", optional = true }
|
|
62
|
+
hyper = { version = "=1.12.0", optional = true, default-features = false, features = ["http1", "client", "server"] }
|
|
63
|
+
hyper-util = { version = "=0.1.21", optional = true, default-features = false, features = ["client-proxy"] }
|
|
62
64
|
|
|
63
65
|
[profile.release]
|
|
64
66
|
codegen-units = 1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: httpunk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: BSD License
|
|
@@ -8,7 +8,6 @@ Classifier: Operating System :: MacOS
|
|
|
8
8
|
Classifier: Operating System :: Microsoft :: Windows
|
|
9
9
|
Classifier: Operating System :: POSIX :: Linux
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
@@ -28,7 +27,7 @@ Keywords: http,h10,h11,h2
|
|
|
28
27
|
Home-Page: https://github.com/gi0baro/httpunk
|
|
29
28
|
Author-email: Giovanni Barillari <g@baro.dev>
|
|
30
29
|
License: BSD-3-Clause
|
|
31
|
-
Requires-Python: >=3.
|
|
30
|
+
Requires-Python: >=3.11
|
|
32
31
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
33
32
|
Project-URL: Funding, https://github.com/sponsors/gi0baro
|
|
34
33
|
Project-URL: Homepage, https://github.com/gi0baro/httpunk
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# Vendored hyperium/h2 (frame + hpack + sync proto pieces)
|
|
2
|
-
#
|
|
1
|
+
# Vendored hyperium/h2 (frame + hpack + sync proto pieces); scripts/vendor-h2.sh
|
|
2
|
+
# enumerates every modification to upstream. A workspace member crate so it owns its
|
|
3
3
|
# own crate root (upstream `crate::` paths resolve natively, no rewrite) and is
|
|
4
4
|
# excluded from the main crate's fmt/clippy via `cargo fmt -p`.
|
|
5
5
|
[package]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.4.20
|
|
@@ -40,7 +40,17 @@ impl GoAway {
|
|
|
40
40
|
&self.debug_data
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
pub fn load(payload: &[u8]) -> Result<GoAway, Error> {
|
|
43
|
+
pub fn load(head: Head, payload: &[u8]) -> Result<GoAway, Error> {
|
|
44
|
+
debug_assert_eq!(head.kind(), crate::frame::Kind::GoAway);
|
|
45
|
+
|
|
46
|
+
// The GOAWAY frame applies to the connection, not a specific stream.
|
|
47
|
+
// An endpoint MUST treat a GOAWAY frame with a stream identifier
|
|
48
|
+
// other than 0x00 as a connection error (Section 5.4.1) of type
|
|
49
|
+
// PROTOCOL_ERROR.
|
|
50
|
+
if !head.stream_id().is_zero() {
|
|
51
|
+
return Err(Error::InvalidStreamId);
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
if payload.len() < 8 {
|
|
45
55
|
return Err(Error::BadFrameSize);
|
|
46
56
|
}
|
|
@@ -381,7 +381,7 @@ impl PushPromise {
|
|
|
381
381
|
|
|
382
382
|
// A promised request "that indicates the presence of a request body
|
|
383
383
|
// MUST reset the promised stream with a stream error"
|
|
384
|
-
|
|
384
|
+
for content_length in req.headers().get_all(header::CONTENT_LENGTH) {
|
|
385
385
|
let parsed_length = parse_u64(content_length.as_bytes());
|
|
386
386
|
if parsed_length != Ok(0) {
|
|
387
387
|
return Err(InvalidContentLength(parsed_length));
|
|
@@ -948,7 +948,7 @@ impl HeaderBlock {
|
|
|
948
948
|
}
|
|
949
949
|
if !self.is_over_size {
|
|
950
950
|
self.field_size += header_size;
|
|
951
|
-
if
|
|
951
|
+
if self.fields.try_append(name, value).is_err() {
|
|
952
952
|
// HeaderMap capacity exceeded — treat as over-size
|
|
953
953
|
// so the stream is rejected downstream (RST_STREAM / 431)
|
|
954
954
|
// instead of panicking on the 24,577th unique header.
|
|
@@ -153,8 +153,8 @@ pub enum Error {
|
|
|
153
153
|
|
|
154
154
|
/// An invalid stream identifier was provided.
|
|
155
155
|
///
|
|
156
|
-
/// This is returned if a SETTINGS or
|
|
157
|
-
/// identifier other than zero.
|
|
156
|
+
/// This is returned if a SETTINGS, PING or GOAWAY frame is received with a
|
|
157
|
+
/// stream identifier other than zero.
|
|
158
158
|
InvalidStreamId,
|
|
159
159
|
|
|
160
160
|
/// A request or response is malformed.
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
//! Huffman decoding for HPACK (RFC 7541 §5.2).
|
|
2
|
+
//!
|
|
3
|
+
//! The decoder has a fast path which decodes 12 bits a time. The decoder
|
|
4
|
+
//! looks the next 12 bits up in `FAST_TABLE`, which maps directly to either
|
|
5
|
+
//! one or two symbols to decode. Since ASCII letters and digits have 5-7 bit
|
|
6
|
+
//! codes, one lookup usually decodes two symbols at once.
|
|
7
|
+
//!
|
|
8
|
+
//! Two situations fall off this fast path:
|
|
9
|
+
//!
|
|
10
|
+
//! - Codes longer than 12 bits: control characters and bytes >= 0x80
|
|
11
|
+
//! - The end of the input, when fewer than 12 bits remain.
|
|
12
|
+
//!
|
|
13
|
+
//! These are decoded by walking the generated `DECODE_TABLE` one input byte at a time.
|
|
14
|
+
|
|
15
|
+
use crate::hpack::huffman::table::{DECODE_TABLE, ENCODE_CODES, ENCODE_CODE_LENGTHS};
|
|
16
|
+
use crate::hpack::DecoderError;
|
|
17
|
+
|
|
18
|
+
use bytes::BytesMut;
|
|
19
|
+
|
|
20
|
+
// DECODE_TABLE (in the generated `table.rs`) is a series of 256-entry
|
|
21
|
+
// tables, walked one input byte at a time. A leaf entry (BRANCH bit clear)
|
|
22
|
+
// holds a decoded symbol and the number of bits its code used. A branch
|
|
23
|
+
// entry (BRANCH bit set) holds the index of the table for the next byte.
|
|
24
|
+
// No valid code leads back to table 0, so a branch to 0 means the input is
|
|
25
|
+
// not a valid code.
|
|
26
|
+
const BRANCH: u16 = 0x8000;
|
|
27
|
+
const TABLE_INDEX_MASK: u16 = 0x7f00;
|
|
28
|
+
const TABLE_WIDTH: usize = 256;
|
|
29
|
+
|
|
30
|
+
// Maps each possible 12-bit chunk of input to the symbol(s) it starts
|
|
31
|
+
// with. Entry layout (u32):
|
|
32
|
+
//
|
|
33
|
+
// bits 0..8 first decoded byte
|
|
34
|
+
// bits 8..16 second decoded byte (if any)
|
|
35
|
+
// bits 16..24 number of symbols decoded: 1, 2, or 0, where 0 means the
|
|
36
|
+
// code is longer than 12 bits and the slow path must run
|
|
37
|
+
// bits 24..32 how many of the 12 bits the decoded symbols used
|
|
38
|
+
const FAST_BITS: usize = 12;
|
|
39
|
+
const FAST_DECODE_TABLE: [u32; 1 << FAST_BITS] = build_fast_table();
|
|
40
|
+
|
|
41
|
+
const fn build_fast_table() -> [u32; 1 << FAST_BITS] {
|
|
42
|
+
let mut table = [0; 1 << FAST_BITS];
|
|
43
|
+
|
|
44
|
+
// First fill in every index that starts with one whole code
|
|
45
|
+
let mut a = 0;
|
|
46
|
+
while a < 256 {
|
|
47
|
+
let len1 = ENCODE_CODE_LENGTHS[a] as usize;
|
|
48
|
+
let code1 = ENCODE_CODES[a] as u64;
|
|
49
|
+
if len1 <= FAST_BITS {
|
|
50
|
+
let rem = FAST_BITS - len1;
|
|
51
|
+
let base = (code1 as usize) << rem;
|
|
52
|
+
let single = (1 << 16) | ((len1 as u32) << 24) | a as u32;
|
|
53
|
+
let mut i = 0;
|
|
54
|
+
while i < (1 << rem) {
|
|
55
|
+
table[base + i] = single;
|
|
56
|
+
i += 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
a += 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Overwrite the indices where a second whole code fit right after the first
|
|
63
|
+
let mut a = 0;
|
|
64
|
+
while a < 256 {
|
|
65
|
+
let len1 = ENCODE_CODE_LENGTHS[a] as usize;
|
|
66
|
+
let code1 = ENCODE_CODES[a] as u64;
|
|
67
|
+
// A second code needs at least 5 more bits (the shortest code)
|
|
68
|
+
if len1 + 5 <= FAST_BITS {
|
|
69
|
+
let rem = FAST_BITS - len1;
|
|
70
|
+
let mut b = 0;
|
|
71
|
+
while b < 256 {
|
|
72
|
+
let len2 = ENCODE_CODE_LENGTHS[b] as usize;
|
|
73
|
+
let code2 = ENCODE_CODES[b] as u64;
|
|
74
|
+
if len2 <= rem {
|
|
75
|
+
let rem2 = rem - len2;
|
|
76
|
+
let base = ((code1 as usize) << rem) | ((code2 as usize) << rem2);
|
|
77
|
+
let pair =
|
|
78
|
+
(2 << 16) | (((len1 + len2) as u32) << 24) | ((b as u32) << 8) | a as u32;
|
|
79
|
+
let mut i = 0;
|
|
80
|
+
while i < (1 << rem2) {
|
|
81
|
+
table[base + i] = pair;
|
|
82
|
+
i += 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
b += 1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
a += 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
table
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Decodes one code of any length by walking DECODE_TABLE one input byte at
|
|
95
|
+
// a time, and writes the symbol to `dst[*o]`. Used for codes longer than
|
|
96
|
+
// FAST_BITS bits and for the tail of the input.
|
|
97
|
+
//
|
|
98
|
+
// Inlining avoids a 35% performance regression on ASCII benchmarks.
|
|
99
|
+
#[inline(always)]
|
|
100
|
+
fn decode_code_slow(
|
|
101
|
+
acc: &mut u64,
|
|
102
|
+
bits: &mut usize,
|
|
103
|
+
dst: &mut [u8],
|
|
104
|
+
o: &mut usize,
|
|
105
|
+
) -> Result<(), DecoderError> {
|
|
106
|
+
let mut table = 0;
|
|
107
|
+
loop {
|
|
108
|
+
let e = DECODE_TABLE[table * TABLE_WIDTH + (*acc >> 56) as usize];
|
|
109
|
+
if e & BRANCH == 0 {
|
|
110
|
+
let used = (e >> 8) as usize;
|
|
111
|
+
if used > *bits {
|
|
112
|
+
return Err(DecoderError::InvalidHuffmanCode);
|
|
113
|
+
}
|
|
114
|
+
dst[*o] = e as u8;
|
|
115
|
+
*o += 1;
|
|
116
|
+
*acc <<= used;
|
|
117
|
+
*bits -= used;
|
|
118
|
+
return Ok(());
|
|
119
|
+
}
|
|
120
|
+
if *bits < 8 {
|
|
121
|
+
return Err(DecoderError::InvalidHuffmanCode);
|
|
122
|
+
}
|
|
123
|
+
table = ((e & TABLE_INDEX_MASK) >> 8) as usize;
|
|
124
|
+
if table == 0 {
|
|
125
|
+
return Err(DecoderError::InvalidHuffmanCode);
|
|
126
|
+
}
|
|
127
|
+
*acc <<= 8;
|
|
128
|
+
*bits -= 8;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Decodes a Huffman encoded string into the provided buffer.
|
|
133
|
+
pub fn decode(src: &[u8], buf: &mut BytesMut) -> Result<BytesMut, DecoderError> {
|
|
134
|
+
let len = src.len();
|
|
135
|
+
let base_len = buf.len();
|
|
136
|
+
|
|
137
|
+
// Reserve the worst case output size. Every code is at least 5 bits,
|
|
138
|
+
// so `len` input bytes can't decode to more than `len * 8 / 5` output
|
|
139
|
+
// bytes. One more byte covers the fast path always writing two bytes
|
|
140
|
+
// even when it decoded only one symbol.
|
|
141
|
+
buf.resize(base_len + len * 8 / 5 + 1, 0);
|
|
142
|
+
let dst = &mut buf[base_len..];
|
|
143
|
+
let mut o = 0;
|
|
144
|
+
|
|
145
|
+
let mut acc: u64 = 0;
|
|
146
|
+
let mut bits: usize = 0;
|
|
147
|
+
let mut pos: usize = 0;
|
|
148
|
+
|
|
149
|
+
'outer: loop {
|
|
150
|
+
// Fill the bit buffer. While 8 or more input bytes remain, load
|
|
151
|
+
// the next 8 in one go and count as many whole bytes as fit under
|
|
152
|
+
// the bits already in the buffer (bringing it to 56-63 bits). The
|
|
153
|
+
// bytes that didn't fit still land in the low end of `acc`; the
|
|
154
|
+
// next refill just ORs the same values over them, so no harm done.
|
|
155
|
+
if pos + 8 <= len {
|
|
156
|
+
let word = u64::from_be_bytes(src[pos..pos + 8].try_into().unwrap());
|
|
157
|
+
acc |= word >> bits;
|
|
158
|
+
pos += (63 - bits) >> 3; // bytes now accounted for in `bits`
|
|
159
|
+
bits |= 56; // equals bits + 8 * (bytes added)
|
|
160
|
+
} else {
|
|
161
|
+
while bits <= 56 && pos < len {
|
|
162
|
+
acc |= (src[pos] as u64) << (56 - bits);
|
|
163
|
+
pos += 1;
|
|
164
|
+
bits += 8;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
while bits >= FAST_BITS {
|
|
169
|
+
let entry = FAST_DECODE_TABLE[(acc >> (64 - FAST_BITS)) as usize];
|
|
170
|
+
let count = (entry >> 16) & 0xff;
|
|
171
|
+
if count == 0 {
|
|
172
|
+
// The next code is longer than 12 bits. Refill first if it
|
|
173
|
+
// might not be in the buffer yet (the longest code is 30
|
|
174
|
+
// bits), then decode it by walking DECODE_TABLE.
|
|
175
|
+
if bits < 30 && pos < len {
|
|
176
|
+
continue 'outer;
|
|
177
|
+
}
|
|
178
|
+
decode_code_slow(&mut acc, &mut bits, dst, &mut o)?;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
let consumed = (entry >> 24) as usize;
|
|
182
|
+
dst[o] = entry as u8;
|
|
183
|
+
dst[o + 1] = (entry >> 8) as u8;
|
|
184
|
+
o += count as usize;
|
|
185
|
+
acc <<= consumed;
|
|
186
|
+
bits -= consumed;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if pos >= len {
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Tail: the input is exhausted and fewer than 12 bits remain.
|
|
195
|
+
while bits > 0 {
|
|
196
|
+
// The encoder pads the last byte with up to 7 one-bits, which are
|
|
197
|
+
// valid only between symbols. Anything else must decode.
|
|
198
|
+
if bits < 8 && (acc >> (64 - bits)) == (1 << bits) - 1 {
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
decode_code_slow(&mut acc, &mut bits, dst, &mut o)?;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
buf.truncate(base_len + o);
|
|
205
|
+
Ok(buf.split())
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
#[cfg(test)]
|
|
209
|
+
mod test {
|
|
210
|
+
use super::*;
|
|
211
|
+
|
|
212
|
+
use bytes::BufMut;
|
|
213
|
+
use rand::rngs::StdRng;
|
|
214
|
+
use rand::{Rng, SeedableRng};
|
|
215
|
+
|
|
216
|
+
fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
|
|
217
|
+
let mut buf = BytesMut::new();
|
|
218
|
+
super::decode(src, &mut buf)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// The simplest decoder we can write, used to double-check the real
|
|
222
|
+
// one: turn the input into a literal string of '0'/'1' characters and
|
|
223
|
+
// repeatedly strip off the first code that matches. No code is a
|
|
224
|
+
// prefix of another, so at most one can match. EOS (index 256) is left
|
|
225
|
+
// out on purpose: encoded EOS is an error per RFC 7541 §5.2.
|
|
226
|
+
fn reference_decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
|
|
227
|
+
// Each symbol's code as a string of '0' and '1', e.g. "11111000".
|
|
228
|
+
let codes: Vec<String> = ENCODE_CODES
|
|
229
|
+
.iter()
|
|
230
|
+
.zip(ENCODE_CODE_LENGTHS.iter())
|
|
231
|
+
.map(|(&code, &len)| format!("{code:0len$b}", len = len as usize))
|
|
232
|
+
.collect();
|
|
233
|
+
let bits: String = src.iter().map(|byte| format!("{byte:08b}")).collect();
|
|
234
|
+
|
|
235
|
+
let mut decoded = BytesMut::new();
|
|
236
|
+
let mut remaining_bits: &str = &bits;
|
|
237
|
+
while let Some(sym) = (0..256).find(|&sym| remaining_bits.starts_with(&codes[sym])) {
|
|
238
|
+
decoded.put_u8(sym as u8);
|
|
239
|
+
remaining_bits = &remaining_bits[codes[sym].len()..];
|
|
240
|
+
}
|
|
241
|
+
// We expect anything that cannot be matched to be padding. Padding is
|
|
242
|
+
// fewer than 8 bits, and all ones.
|
|
243
|
+
if remaining_bits.len() >= 8 || remaining_bits.chars().any(|bit| bit == '0') {
|
|
244
|
+
return Err(DecoderError::InvalidHuffmanCode);
|
|
245
|
+
}
|
|
246
|
+
Ok(decoded)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
#[test]
|
|
250
|
+
fn decode_single_byte() {
|
|
251
|
+
assert_eq!("o", decode(&[0b00111111]).unwrap());
|
|
252
|
+
assert_eq!("0", decode(&[7]).unwrap());
|
|
253
|
+
assert_eq!("A", decode(&[(0x21 << 2) + 3]).unwrap());
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#[test]
|
|
257
|
+
fn single_char_multi_byte() {
|
|
258
|
+
assert_eq!("#", decode(&[255, 160 + 15]).unwrap());
|
|
259
|
+
assert_eq!("$", decode(&[255, 200 + 7]).unwrap());
|
|
260
|
+
assert_eq!("\x0a", decode(&[255, 255, 255, 240 + 3]).unwrap());
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#[test]
|
|
264
|
+
fn multi_char() {
|
|
265
|
+
assert_eq!("!0", decode(&[254, 1]).unwrap());
|
|
266
|
+
assert_eq!(" !", decode(&[0b01010011, 0b11111000]).unwrap());
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Random (mostly invalid) byte strings must produce identical results to reference impl
|
|
270
|
+
#[test]
|
|
271
|
+
fn matches_reference_on_arbitrary_bytes() {
|
|
272
|
+
let mut rng = StdRng::seed_from_u64(0xdeadbeefcafe);
|
|
273
|
+
|
|
274
|
+
for _ in 0..10_000 {
|
|
275
|
+
let len = rng.gen_range(0..40);
|
|
276
|
+
let src: Vec<u8> = (0..len).map(|_| rng.gen()).collect();
|
|
277
|
+
assert_eq!(reference_decode(&src), decode(&src), "src={:?}", src);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Bias toward high bytes to exercise long codes and EOS prefixes.
|
|
281
|
+
for _ in 0..10_000 {
|
|
282
|
+
let len = rng.gen_range(0..40);
|
|
283
|
+
let src: Vec<u8> = (0..len).map(|_| rng.gen::<u8>() | 0xe0).collect();
|
|
284
|
+
assert_eq!(reference_decode(&src), decode(&src), "src={:?}", src);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
#[test]
|
|
289
|
+
fn rejects_eos_and_invalid_padding() {
|
|
290
|
+
assert_eq!(decode(&[0xff]), Err(DecoderError::InvalidHuffmanCode));
|
|
291
|
+
assert_eq!(
|
|
292
|
+
decode(&[0xff, 0xff, 0xff, 0xff]),
|
|
293
|
+
Err(DecoderError::InvalidHuffmanCode)
|
|
294
|
+
);
|
|
295
|
+
assert_eq!(decode(&[0]), Err(DecoderError::InvalidHuffmanCode));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/*
|
|
300
|
+
// uncomment to run benchmarks
|
|
301
|
+
#[cfg(test)]
|
|
302
|
+
mod bench {
|
|
303
|
+
extern crate test;
|
|
304
|
+
|
|
305
|
+
use self::test::{black_box, Bencher};
|
|
306
|
+
use super::*;
|
|
307
|
+
|
|
308
|
+
use crate::hpack::huffman::encode;
|
|
309
|
+
|
|
310
|
+
fn decode_input(b: &mut Bencher, input: &[u8]) {
|
|
311
|
+
let mut encoded = BytesMut::new();
|
|
312
|
+
encode(input, &mut encoded);
|
|
313
|
+
|
|
314
|
+
let mut scratch = BytesMut::with_capacity(input.len() * 2);
|
|
315
|
+
b.bytes = encoded.len() as u64;
|
|
316
|
+
b.iter(|| {
|
|
317
|
+
let decoded = decode(black_box(encoded.as_ref()), &mut scratch).unwrap();
|
|
318
|
+
black_box(decoded);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
#[bench]
|
|
323
|
+
fn decode_short_ascii(b: &mut Bencher) {
|
|
324
|
+
decode_input(b, b"www.example.com");
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
#[bench]
|
|
328
|
+
fn decode_header_value(b: &mut Bencher) {
|
|
329
|
+
decode_input(
|
|
330
|
+
b,
|
|
331
|
+
b"text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8",
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
#[bench]
|
|
336
|
+
fn decode_long_ascii(b: &mut Bencher) {
|
|
337
|
+
decode_input(
|
|
338
|
+
b,
|
|
339
|
+
b"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0",
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
#[bench]
|
|
344
|
+
fn decode_all_octets(b: &mut Bencher) {
|
|
345
|
+
let input: Vec<_> = (0..=u8::MAX).collect();
|
|
346
|
+
decode_input(b, &input);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
*/
|