feat(http): enable gzip + brotli decompression for reqwest clients (#704)

* feat(http): enable gzip + brotli decompression for reqwest clients

All Rust-side HTTP clients now advertise Accept-Encoding and transparently
decode compressed responses. reqwest auto-decompresses by default once the
features are enabled, so this is a pure dependency-feature change with no
call-site edits.

Added to all five reqwest declarations across the Cargo workspace
(top psysonic crate + psysonic-audio / -analysis / -integration / -syncfs).
The real wire savings land on JSON payloads — Navidrome native /api,
Bandsintown, Radio-Browser, Last.fm — measured at roughly -76% to -93% on
earlier curl tests. Crates that only fetch already-compressed audio bytes
get the features too for consistency: reqwest just advertises the header
there, so there's no runtime cost when the server returns data as-is.

Cargo.lock grows additively (async-compression + compression codecs); no
other crates moved.

* docs(changelog): add entry for HTTP gzip + brotli (#704)
This commit is contained in:
Frank Stellmacher
2026-05-14 22:17:02 +02:00
committed by GitHub
parent 5f0803e98a
commit ac21fc084d
7 changed files with 47 additions and 5 deletions
+7
View File
@@ -30,6 +30,13 @@ Foundational work: faster reviews, narrower diffs, and a safety net under the pa
## Added
### HTTP — gzip + brotli decompression for the Rust-side clients
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#704](https://github.com/Psychotoxical/psysonic/pull/704)**
* Every HTTP client on the Rust side now advertises `Accept-Encoding` and transparently decodes compressed responses. The JSON-heavy endpoints — Navidrome native `/api`, Bandsintown, Radio-Browser, Last.fm — were the gap: the WebView's `axios` calls were already compressed, the Rust clients were not. Earlier curl measurements put the wire savings on those payloads at roughly **7693 %**.
* Implementation is a dependency-feature change only (`gzip` + `brotli` on `reqwest`) — no behaviour change beyond smaller transfers, and no extra cost on the clients that fetch already-compressed audio.
### Queue Toolbar — customizable button order + per-button visibility
**By [@kveld9](https://github.com/kveld9), PR [#534](https://github.com/Psychotoxical/psysonic/pull/534)**
+35
View File
@@ -128,6 +128,18 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "async-compression"
version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac"
dependencies = [
"compression-codecs",
"compression-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "async-executor"
version = "1.14.0"
@@ -671,6 +683,24 @@ dependencies = [
"memchr",
]
[[package]]
name = "compression-codecs"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf"
dependencies = [
"brotli",
"compression-core",
"flate2",
"memchr",
]
[[package]]
name = "compression-core"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@@ -5950,13 +5980,18 @@ version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [
"async-compression",
"bitflags 2.11.1",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-body-util",
"iri-string",
"pin-project-lite",
"tokio",
"tokio-util",
"tower",
"tower-layer",
"tower-service",
+1 -1
View File
@@ -52,7 +52,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
rodio = { version = "0.22", default-features = false, features = ["playback", "symphonia-all"] }
symphonia = { version = "0.5", default-features = false, features = ["flac", "mp3", "pcm", "aac", "alac", "isomp4", "vorbis", "ogg", "wav", "adpcm"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "json", "multipart", "query", "form", "rustls", "blocking"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "json", "multipart", "query", "form", "rustls", "blocking", "gzip", "brotli"] }
futures-util = "0.3"
md5 = "0.8"
tokio = { version = "1", features = ["rt", "time", "sync"] }
@@ -12,7 +12,7 @@ tauri = { version = "2" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt", "time", "sync"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "rustls"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "rustls", "gzip", "brotli"] }
futures-util = "0.3"
ebur128 = "0.1"
md5 = "0.8"
+1 -1
View File
@@ -13,7 +13,7 @@ tauri = { version = "2" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt", "time", "sync"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "json", "rustls", "blocking"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "json", "rustls", "blocking", "gzip", "brotli"] }
futures-util = "0.3"
rodio = { version = "0.22", default-features = false, features = ["playback", "symphonia-all"] }
symphonia = { version = "0.5", default-features = false, features = ["flac", "mp3", "pcm", "aac", "alac", "isomp4", "vorbis", "ogg", "wav", "adpcm"] }
@@ -12,7 +12,7 @@ tauri = { version = "2" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt", "time", "sync"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "json", "rustls", "blocking"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "json", "rustls", "blocking", "gzip", "brotli"] }
futures-util = "0.3"
discord-rich-presence = "1.1"
url = "2"
+1 -1
View File
@@ -16,7 +16,7 @@ tauri-plugin-process = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt", "time", "sync"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "json", "rustls", "blocking"] }
reqwest = { version = "0.13", default-features = false, features = ["stream", "json", "rustls", "blocking", "gzip", "brotli"] }
futures-util = "0.3"
sysinfo = { version = "0.38", default-features = false, features = ["disk"] }
url = "2"