diff --git a/Cargo.lock b/Cargo.lock index dcd154367..d7eeb0a32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" @@ -970,6 +1020,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" @@ -1507,6 +1574,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" @@ -1853,6 +1926,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" @@ -2228,6 +2310,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" @@ -2455,6 +2555,17 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[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" @@ -3008,7 +3119,6 @@ version = "0.13.1" dependencies = [ "dirs", "ecow", - "env_proxy", "fastrand", "flate2", "fontdb", @@ -3373,18 +3483,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]] @@ -3426,6 +3555,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" @@ -3603,12 +3738,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" @@ -3618,6 +3787,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" @@ -3895,6 +4070,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" diff --git a/Cargo.toml b/Cargo.toml index 63ea32b94..923f18a6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 } @@ -133,7 +132,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" diff --git a/crates/typst-cli/src/update.rs b/crates/typst-cli/src/update.rs index ec8ca71e8..06d1814e9 100644 --- a/crates/typst-cli/src/update.rs +++ b/crates/typst-cli/src/update.rs @@ -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})"), diff --git a/crates/typst-kit/Cargo.toml b/crates/typst-kit/Cargo.toml index e59127d71..ffa7ab465 100644 --- a/crates/typst-kit/Cargo.toml +++ b/crates/typst-kit/Cargo.toml @@ -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"] diff --git a/crates/typst-kit/src/download.rs b/crates/typst-kit/src/download.rs index a4d49b4f3..cc64341ba 100644 --- a/crates/typst-kit/src/download.rs +++ b/crates/typst-kit/src/download.rs @@ -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, - cert: OnceCell, + cert: OnceCell>, } 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, cert: Certificate) -> Self { + pub fn with_cert( + user_agent: impl Into, + 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> { + pub fn cert(&self) -> Option>> { 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 { - let mut builder = ureq::AgentBuilder::new(); - let mut tls = TlsConnector::builder(); + pub fn download( + &self, + url: &str, + ) -> Result, 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, + 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, + progress: &'p mut dyn Progress, + ) -> Self { let content_len: Option = 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, diff --git a/crates/typst-kit/src/package.rs b/crates/typst-kit/src/package.rs index e62e843cd..76c562734 100644 --- a/crates/typst-kit/src/package.rs +++ b/crates/typst-kit/src/package.rs @@ -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 {