Merge e2a022ac7ba5f60cdbcc6f526297ffe2449e9a07 into af2253ba16dfdc731e787e3a43a6f6a63ea65e0a

This commit is contained in:
arielf212 2025-07-22 19:21:54 +08:00 committed by GitHub
commit 4bdd77db37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 240 additions and 51 deletions

211
Cargo.lock generated
View File

@ -136,6 +136,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
[[package]]
name = "biblatex"
version = "0.10.0"
@ -244,6 +250,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
version = "1.2.11"
@ -471,6 +483,35 @@ dependencies = [
"syn",
]
[[package]]
name = "cookie"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "cookie_store"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
dependencies = [
"cookie",
"document-features",
"idna",
"indexmap 2.7.1",
"log",
"serde",
"serde_derive",
"serde_json",
"time",
"url",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -572,6 +613,16 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]]
name = "der"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"pem-rfc7468",
"zeroize",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -624,6 +675,15 @@ dependencies = [
"syn",
]
[[package]]
name = "document-features"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
dependencies = [
"litrs",
]
[[package]]
name = "downcast-rs"
version = "1.2.1"
@ -677,16 +737,6 @@ dependencies = [
"syn",
]
[[package]]
name = "env_proxy"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a5019be18538406a43b5419a5501461f0c8b49ea7dfda0cfc32f4e51fc44be1"
dependencies = [
"log",
"url",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -1033,6 +1083,23 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "http"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
@ -1566,6 +1633,12 @@ dependencies = [
"serde",
]
[[package]]
name = "litrs"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]]
name = "lock_api"
version = "0.4.12"
@ -1912,6 +1985,15 @@ dependencies = [
"ryu",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -2287,6 +2369,24 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
dependencies = [
"zeroize",
]
[[package]]
name = "rustversion"
version = "1.0.19"
@ -2514,6 +2614,17 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socks"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
dependencies = [
"byteorder",
"libc",
"winapi",
]
[[package]]
name = "spin"
version = "0.9.8"
@ -3065,7 +3176,6 @@ version = "0.13.1"
dependencies = [
"dirs",
"ecow",
"env_proxy",
"fastrand",
"flate2",
"fontdb",
@ -3435,18 +3545,37 @@ checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47"
[[package]]
name = "ureq"
version = "2.12.1"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
checksum = "b7a3e9af6113ecd57b8c63d3cd76a385b2e3881365f1f489e54f49801d0c83ea"
dependencies = [
"base64",
"cookie_store",
"der",
"flate2",
"log",
"native-tls",
"once_cell",
"percent-encoding",
"rustls-pemfile",
"rustls-pki-types",
"serde",
"serde_json",
"url",
"socks",
"ureq-proto",
"utf-8",
"webpki-root-certs 0.26.11",
]
[[package]]
name = "ureq-proto"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36"
dependencies = [
"base64",
"http",
"httparse",
"log",
]
[[package]]
@ -3488,6 +3617,12 @@ dependencies = [
"xmlwriter",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf16_iter"
version = "1.0.5"
@ -3665,12 +3800,46 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-root-certs"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e"
dependencies = [
"webpki-root-certs 1.0.0",
]
[[package]]
name = "webpki-root-certs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "weezl"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.9"
@ -3680,6 +3849,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
@ -3957,6 +4132,12 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
[[package]]
name = "zerotrie"
version = "0.1.3"

View File

@ -54,7 +54,6 @@ csv = "1"
ctrlc = "3.4.1"
dirs = "6"
ecow = { version = "0.2", features = ["serde"] }
env_proxy = "0.4"
fastrand = "2.3"
flate2 = "1"
fontdb = { version = "0.23", default-features = false }
@ -134,7 +133,7 @@ unicode-script = "0.5"
unicode-normalization = "0.1.24"
unicode-segmentation = "1"
unscanny = "0.1"
ureq = { version = "2", default-features = false, features = ["native-tls", "gzip", "json"] }
ureq = { version = "3", default-features = false, features = ["native-tls", "gzip", "json", "socks-proxy"] }
usvg = { version = "0.45", default-features = false, features = ["text"] }
utf8_iter = "1.0.4"
walkdir = "2"

View File

@ -145,10 +145,10 @@ impl Release {
};
match downloader.download(&url) {
Ok(response) => response.into_json().map_err(|err| {
Ok(mut response) => response.body_mut().read_json().map_err(|err| {
eco_format!("failed to parse release information ({err})")
}),
Err(ureq::Error::Status(404, _)) => {
Err(ureq::Error::StatusCode(404)) => {
bail!("release not found (searched at {url})")
}
Err(err) => bail!("failed to download release ({err})"),
@ -175,7 +175,7 @@ impl Release {
&mut PrintDownload("release"),
) {
Ok(data) => data,
Err(ureq::Error::Status(404, _)) => {
Err(ureq::Error::StatusCode(404)) => {
bail!("asset not found (searched for {})", asset.name);
}
Err(err) => bail!("failed to download asset ({err})"),

View File

@ -18,7 +18,6 @@ typst-timing = { workspace = true }
typst-utils = { workspace = true }
dirs = { workspace = true, optional = true }
ecow = { workspace = true }
env_proxy = { workspace = true, optional = true }
fastrand = { workspace = true, optional = true }
flate2 = { workspace = true, optional = true }
fontdb = { workspace = true, optional = true }
@ -41,7 +40,7 @@ default = ["fonts", "packages"]
fonts = ["dep:fontdb", "fontdb/memmap", "fontdb/fontconfig"]
# Add generic downloading utilities
downloads = ["dep:env_proxy", "dep:native-tls", "dep:ureq", "dep:openssl"]
downloads = ["dep:native-tls", "dep:ureq", "dep:openssl"]
# Add package downloading utilities, implies `downloads`
packages = ["downloads", "dep:dirs", "dep:flate2", "dep:tar", "dep:fastrand"]

View File

@ -13,9 +13,8 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use ecow::EcoString;
use native_tls::{Certificate, TlsConnector};
use once_cell::sync::OnceCell;
use ureq::Response;
use ureq::tls::{Certificate, RootCerts, TlsProvider};
/// Manages progress reporting for downloads.
pub trait Progress {
@ -57,7 +56,7 @@ pub struct DownloadState {
pub struct Downloader {
user_agent: EcoString,
cert_path: Option<PathBuf>,
cert: OnceCell<Certificate>,
cert: OnceCell<Certificate<'static>>,
}
impl Downloader {
@ -82,7 +81,10 @@ impl Downloader {
}
/// Crates a new downloader with the given user agent and certificate.
pub fn with_cert(user_agent: impl Into<EcoString>, cert: Certificate) -> Self {
pub fn with_cert(
user_agent: impl Into<EcoString>,
cert: Certificate<'static>,
) -> Self {
Self {
user_agent: user_agent.into(),
cert_path: None,
@ -96,7 +98,7 @@ impl Downloader {
/// - Returns `None` if `--cert` and `TYPST_CERT` are not set.
/// - Returns `Some(Ok(cert))` if the certificate was loaded successfully.
/// - Returns `Some(Err(err))` if an error occurred while loading the certificate.
pub fn cert(&self) -> Option<io::Result<&Certificate>> {
pub fn cert(&self) -> Option<io::Result<&Certificate<'static>>> {
self.cert_path.as_ref().map(|path| {
self.cert.get_or_try_init(|| {
let pem = std::fs::read(path)?;
@ -107,31 +109,34 @@ impl Downloader {
/// Download binary data from the given url.
#[allow(clippy::result_large_err)]
pub fn download(&self, url: &str) -> Result<ureq::Response, ureq::Error> {
let mut builder = ureq::AgentBuilder::new();
let mut tls = TlsConnector::builder();
pub fn download(
&self,
url: &str,
) -> Result<ureq::http::Response<ureq::Body>, ureq::Error> {
let mut builder = ureq::config::Config::builder();
// Set user agent.
builder = builder.user_agent(&self.user_agent);
// Get the network proxy config from the environment and apply it.
if let Some(proxy) = env_proxy::for_url_str(url)
.to_url()
.and_then(|url| ureq::Proxy::new(url).ok())
{
builder = builder.proxy(proxy);
}
// Apply a custom CA certificate if present.
if let Some(cert) = self.cert() {
tls.add_root_certificate(cert?.clone());
}
let maybe_cert = self.cert().transpose()?.cloned().map_or(
RootCerts::PlatformVerifier,
|cert| {
let certs = vec![cert];
RootCerts::Specific(Arc::new(certs))
},
);
// Configure native TLS.
let connector = tls.build().map_err(io::Error::other)?;
builder = builder.tls_connector(Arc::new(connector));
let tls_config = ureq::tls::TlsConfig::builder()
.provider(TlsProvider::NativeTls)
.root_certs(maybe_cert);
builder.build().get(url).call()
builder = builder.tls_config(tls_config.build());
let agent = ureq::Agent::new_with_config(builder.build());
agent.get(url).call()
}
/// Download binary data from the given url and report its progress.
@ -170,7 +175,7 @@ const SAMPLES: usize = 5;
/// over a websocket and reports its progress.
struct RemoteReader<'p> {
/// The reader returned by the ureq::Response.
reader: Box<dyn Read + Send + Sync + 'static>,
reader: ureq::BodyReader<'static>,
/// The download state, holding download metadata for progress reporting.
state: DownloadState,
/// The instant at which progress was last reported.
@ -184,13 +189,18 @@ impl<'p> RemoteReader<'p> {
///
/// The 'Content-Length' header is used as a size hint for read
/// optimization, if present.
fn from_response(response: Response, progress: &'p mut dyn Progress) -> Self {
fn from_response(
response: ureq::http::Response<ureq::Body>,
progress: &'p mut dyn Progress,
) -> Self {
let content_len: Option<usize> = response
.header("Content-Length")
.headers()
.get("Content-Length")
.and_then(|header| header.to_str().ok())
.and_then(|header| header.parse().ok());
Self {
reader: response.into_reader(),
reader: response.into_body().into_reader(),
last_progress: None,
state: DownloadState {
content_len,

View File

@ -150,10 +150,10 @@ impl PackageStorage {
.get_or_try_init(|| {
let url = format!("{DEFAULT_REGISTRY}/{DEFAULT_NAMESPACE}/index.json");
match self.downloader.download(&url) {
Ok(response) => response.into_json().map_err(|err| {
Ok(mut response) => response.body_mut().read_json().map_err(|err| {
eco_format!("failed to parse package index: {err}")
}),
Err(ureq::Error::Status(404, _)) => {
Err(ureq::Error::StatusCode(404)) => {
bail!("failed to fetch package index (not found)")
}
Err(err) => bail!("failed to fetch package index ({err})"),
@ -181,7 +181,7 @@ impl PackageStorage {
let data = match self.downloader.download_with_progress(&url, progress) {
Ok(data) => data,
Err(ureq::Error::Status(404, _)) => {
Err(ureq::Error::StatusCode(404)) => {
if let Ok(version) = self.determine_latest_version(&spec.versionless()) {
return Err(PackageError::VersionNotFound(spec.clone(), version));
} else {