mirror of https://github.com/astral-sh/uv
parent
d8323551f8
commit
a01143980a
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
|
|
@ -55,7 +55,7 @@ anyhow = { version = "1.0.80" }
|
||||||
async-channel = { version = "2.2.0" }
|
async-channel = { version = "2.2.0" }
|
||||||
async-compression = { version = "0.4.6" }
|
async-compression = { version = "0.4.6" }
|
||||||
async-trait = { version = "0.1.78" }
|
async-trait = { version = "0.1.78" }
|
||||||
async_http_range_reader = { version = "0.7.0" }
|
async_http_range_reader = { version = "0.7.1" }
|
||||||
async_zip = { git = "https://github.com/charliermarsh/rs-async-zip", rev = "1dcb40cfe1bf5325a6fd4bfcf9894db40241f585", features = ["deflate"] }
|
async_zip = { git = "https://github.com/charliermarsh/rs-async-zip", rev = "1dcb40cfe1bf5325a6fd4bfcf9894db40241f585", features = ["deflate"] }
|
||||||
axoupdater = { version = "0.4.0", default-features = false }
|
axoupdater = { version = "0.4.0", default-features = false }
|
||||||
backoff = { version = "0.4.0" }
|
backoff = { version = "0.4.0" }
|
||||||
|
|
@ -86,7 +86,7 @@ hex = { version = "0.4.3" }
|
||||||
hmac = { version = "0.12.1" }
|
hmac = { version = "0.12.1" }
|
||||||
home = { version = "0.5.9" }
|
home = { version = "0.5.9" }
|
||||||
html-escape = { version = "0.2.13" }
|
html-escape = { version = "0.2.13" }
|
||||||
http = { version = "0.2.12" }
|
http = { version = "1.1.0" }
|
||||||
indexmap = { version = "2.2.5" }
|
indexmap = { version = "2.2.5" }
|
||||||
indicatif = { version = "0.17.7" }
|
indicatif = { version = "0.17.7" }
|
||||||
indoc = { version = "2.0.4" }
|
indoc = { version = "2.0.4" }
|
||||||
|
|
@ -107,9 +107,9 @@ rand = { version = "0.8.5" }
|
||||||
rayon = { version = "1.8.0" }
|
rayon = { version = "1.8.0" }
|
||||||
reflink-copy = { version = "0.1.15" }
|
reflink-copy = { version = "0.1.15" }
|
||||||
regex = { version = "1.10.2" }
|
regex = { version = "1.10.2" }
|
||||||
reqwest = { version = "0.11.23", default-features = false, features = ["json", "gzip", "brotli", "stream", "rustls-tls", "rustls-tls-native-roots"] }
|
reqwest = { version = "0.12.3", default-features = false, features = ["json", "gzip", "brotli", "stream", "rustls-tls", "rustls-tls-native-roots"] }
|
||||||
reqwest-middleware = { version = "0.2.4" }
|
reqwest-middleware = { version = "0.3.0" }
|
||||||
reqwest-retry = { version = "0.3.0" }
|
reqwest-retry = { version = "0.5.0" }
|
||||||
rkyv = { version = "0.7.43", features = ["strict", "validation"] }
|
rkyv = { version = "0.7.43", features = ["strict", "validation"] }
|
||||||
rmp-serde = { version = "1.1.2" }
|
rmp-serde = { version = "1.1.2" }
|
||||||
rust-netrc = { version = "0.1.1" }
|
rust-netrc = { version = "0.1.1" }
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,15 @@ edition = "2021"
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
clap = { workspace = true, features = ["derive", "env"], optional = true }
|
clap = { workspace = true, features = ["derive", "env"], optional = true }
|
||||||
|
http = { workspace = true }
|
||||||
|
once_cell = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
reqwest-middleware = { workspace = true }
|
reqwest-middleware = { workspace = true }
|
||||||
rust-netrc = { workspace = true }
|
rust-netrc = { workspace = true }
|
||||||
task-local-extensions = { workspace = true }
|
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
urlencoding = { workspace = true }
|
urlencoding = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
use http::Extensions;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use netrc::Netrc;
|
use netrc::Netrc;
|
||||||
use reqwest::{header::HeaderValue, Request, Response};
|
use reqwest::{header::HeaderValue, Request, Response};
|
||||||
use reqwest_middleware::{Middleware, Next};
|
use reqwest_middleware::{Middleware, Next};
|
||||||
use task_local_extensions::Extensions;
|
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
||||||
|
|
@ -48,13 +48,11 @@ tracing = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
urlencoding = { workspace = true }
|
urlencoding = { workspace = true }
|
||||||
|
|
||||||
# These must be kept in-sync with those used by `reqwest`.
|
|
||||||
rustls = { version = "0.21.10" }
|
|
||||||
rustls-native-certs = { version = "0.6.3" }
|
|
||||||
webpki-roots = { version = "0.25.4" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
hyper = { version = "0.14.28", features = ["server", "http1"] }
|
http-body-util = { version = "0.1.0" }
|
||||||
|
hyper = { version = "1.2.0", features = ["server", "http1"] }
|
||||||
|
hyper-util = { version = "0.1.3", features = ["tokio"] }
|
||||||
insta = { version = "1.36.1" }
|
insta = { version = "1.36.1" }
|
||||||
|
os_info = { version = "=3.7.0", default-features = false }
|
||||||
tokio = { workspace = true, features = ["fs", "macros"] }
|
tokio = { workspace = true, features = ["fs", "macros"] }
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,7 @@ use uv_warnings::warn_user_once;
|
||||||
|
|
||||||
use crate::linehaul::LineHaul;
|
use crate::linehaul::LineHaul;
|
||||||
use crate::middleware::OfflineMiddleware;
|
use crate::middleware::OfflineMiddleware;
|
||||||
use crate::tls::Roots;
|
use crate::Connectivity;
|
||||||
use crate::{tls, Connectivity};
|
|
||||||
|
|
||||||
/// A builder for an [`BaseClient`].
|
/// A builder for an [`BaseClient`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -140,19 +139,20 @@ impl<'a> BaseClientBuilder<'a> {
|
||||||
}
|
}
|
||||||
path_exists
|
path_exists
|
||||||
});
|
});
|
||||||
// Load the TLS configuration.
|
|
||||||
let tls = tls::load(if self.native_tls || ssl_cert_file_exists {
|
|
||||||
Roots::Native
|
|
||||||
} else {
|
|
||||||
Roots::Webpki
|
|
||||||
})
|
|
||||||
.expect("Failed to load TLS configuration.");
|
|
||||||
|
|
||||||
|
// Configure the builder.
|
||||||
let client_core = ClientBuilder::new()
|
let client_core = ClientBuilder::new()
|
||||||
.user_agent(user_agent_string)
|
.user_agent(user_agent_string)
|
||||||
.pool_max_idle_per_host(20)
|
.pool_max_idle_per_host(20)
|
||||||
.timeout(std::time::Duration::from_secs(timeout))
|
.timeout(std::time::Duration::from_secs(timeout))
|
||||||
.use_preconfigured_tls(tls);
|
.tls_built_in_root_certs(false);
|
||||||
|
|
||||||
|
// Configure TLS.
|
||||||
|
let client_core = if self.native_tls || ssl_cert_file_exists {
|
||||||
|
client_core.tls_built_in_native_certs(true)
|
||||||
|
} else {
|
||||||
|
client_core.tls_built_in_webpki_certs(true)
|
||||||
|
};
|
||||||
|
|
||||||
client_core.build().expect("Failed to build HTTP client.")
|
client_core.build().expect("Failed to build HTTP client.")
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,3 @@ mod middleware;
|
||||||
mod registry_client;
|
mod registry_client;
|
||||||
mod remote_metadata;
|
mod remote_metadata;
|
||||||
mod rkyvutil;
|
mod rkyvutil;
|
||||||
mod tls;
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
use http::Extensions;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use reqwest::{Request, Response};
|
use reqwest::{Request, Response};
|
||||||
use reqwest_middleware::{Middleware, Next};
|
use reqwest_middleware::{Middleware, Next};
|
||||||
use task_local_extensions::Extensions;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
/// A custom error type for the offline middleware.
|
/// A custom error type for the offline middleware.
|
||||||
|
|
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
use rustls::ClientConfig;
|
|
||||||
use tracing::warn;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
|
||||||
pub(crate) enum TlsError {
|
|
||||||
#[error(transparent)]
|
|
||||||
Rustls(#[from] rustls::Error),
|
|
||||||
#[error("zero valid certificates found in native root store")]
|
|
||||||
ZeroCertificates,
|
|
||||||
#[error("failed to load native root certificates")]
|
|
||||||
NativeCertificates(#[source] std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub(crate) enum Roots {
|
|
||||||
/// Use reqwest's `rustls-tls-webpki-roots` behavior for loading root certificates.
|
|
||||||
Webpki,
|
|
||||||
/// Use reqwest's `rustls-tls-native-roots` behavior for loading root certificates.
|
|
||||||
Native,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize a TLS configuration for the client.
|
|
||||||
///
|
|
||||||
/// This is equivalent to the TLS initialization `reqwest` when `rustls-tls` is enabled,
|
|
||||||
/// with two notable changes:
|
|
||||||
///
|
|
||||||
/// 1. It enables _either_ the `webpki-roots` or the `native-certs` feature, but not both.
|
|
||||||
/// 2. It assumes the following builder settings (which match the defaults):
|
|
||||||
/// - `root_certs: vec![]`
|
|
||||||
/// - `min_tls_version: None`
|
|
||||||
/// - `max_tls_version: None`
|
|
||||||
/// - `identity: None`
|
|
||||||
/// - `certs_verification: false`
|
|
||||||
/// - `tls_sni: true`
|
|
||||||
/// - `http_version_pref: HttpVersionPref::All`
|
|
||||||
///
|
|
||||||
/// See: <https://github.com/seanmonstar/reqwest/blob/e3192638518d577759dd89da489175b8f992b12f/src/async_impl/client.rs#L498>
|
|
||||||
pub(crate) fn load(roots: Roots) -> Result<ClientConfig, TlsError> {
|
|
||||||
// Set root certificates.
|
|
||||||
let mut root_cert_store = rustls::RootCertStore::empty();
|
|
||||||
|
|
||||||
match roots {
|
|
||||||
Roots::Webpki => {
|
|
||||||
// Use `rustls-tls-webpki-roots`
|
|
||||||
use rustls::OwnedTrustAnchor;
|
|
||||||
|
|
||||||
let trust_anchors = webpki_roots::TLS_SERVER_ROOTS.iter().map(|trust_anchor| {
|
|
||||||
OwnedTrustAnchor::from_subject_spki_name_constraints(
|
|
||||||
trust_anchor.subject,
|
|
||||||
trust_anchor.spki,
|
|
||||||
trust_anchor.name_constraints,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
root_cert_store.add_trust_anchors(trust_anchors);
|
|
||||||
}
|
|
||||||
Roots::Native => {
|
|
||||||
// Use: `rustls-tls-native-roots`
|
|
||||||
let mut valid_count = 0;
|
|
||||||
let mut invalid_count = 0;
|
|
||||||
for cert in
|
|
||||||
rustls_native_certs::load_native_certs().map_err(TlsError::NativeCertificates)?
|
|
||||||
{
|
|
||||||
let cert = rustls::Certificate(cert.0);
|
|
||||||
// Continue on parsing errors, as native stores often include ancient or syntactically
|
|
||||||
// invalid certificates, like root certificates without any X509 extensions.
|
|
||||||
// Inspiration: https://github.com/rustls/rustls/blob/633bf4ba9d9521a95f68766d04c22e2b01e68318/rustls/src/anchors.rs#L105-L112
|
|
||||||
match root_cert_store.add(&cert) {
|
|
||||||
Ok(_) => valid_count += 1,
|
|
||||||
Err(err) => {
|
|
||||||
invalid_count += 1;
|
|
||||||
warn!(
|
|
||||||
"rustls failed to parse DER certificate {:?} {:?}",
|
|
||||||
&err, &cert
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if valid_count == 0 && invalid_count > 0 {
|
|
||||||
return Err(TlsError::ZeroCertificates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build TLS config
|
|
||||||
let config_builder = ClientConfig::builder()
|
|
||||||
.with_safe_default_cipher_suites()
|
|
||||||
.with_safe_default_kx_groups()
|
|
||||||
.with_protocol_versions(rustls::ALL_VERSIONS)?
|
|
||||||
.with_root_certificates(root_cert_store);
|
|
||||||
|
|
||||||
// Finalize TLS config
|
|
||||||
let mut tls = config_builder.with_no_client_auth();
|
|
||||||
|
|
||||||
// Enable SNI
|
|
||||||
tls.enable_sni = true;
|
|
||||||
|
|
||||||
// ALPN protocol
|
|
||||||
tls.alpn_protocols = vec!["h2".into(), "http/1.1".into()];
|
|
||||||
|
|
||||||
Ok(tls)
|
|
||||||
}
|
|
||||||
|
|
@ -3,10 +3,13 @@ use std::io::Write;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use hyper::header::AUTHORIZATION;
|
use http::header::AUTHORIZATION;
|
||||||
use hyper::server::conn::Http;
|
use http_body_util::Full;
|
||||||
|
use hyper::body::Bytes;
|
||||||
|
use hyper::server::conn::http1;
|
||||||
use hyper::service::service_fn;
|
use hyper::service::service_fn;
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Request, Response};
|
||||||
|
use hyper_util::rt::TokioIo;
|
||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
|
@ -21,7 +24,7 @@ async fn test_client_with_netrc_credentials() -> Result<()> {
|
||||||
|
|
||||||
// Spawn the server loop in a background task
|
// Spawn the server loop in a background task
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let svc = service_fn(move |req: Request<Body>| {
|
let svc = service_fn(move |req: Request<hyper::body::Incoming>| {
|
||||||
// Get User Agent Header and send it back in the response
|
// Get User Agent Header and send it back in the response
|
||||||
let auth = req
|
let auth = req
|
||||||
.headers()
|
.headers()
|
||||||
|
|
@ -29,16 +32,19 @@ async fn test_client_with_netrc_credentials() -> Result<()> {
|
||||||
.and_then(|v| v.to_str().ok())
|
.and_then(|v| v.to_str().ok())
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.unwrap_or_default(); // Empty Default
|
.unwrap_or_default(); // Empty Default
|
||||||
future::ok::<_, hyper::Error>(Response::new(Body::from(auth)))
|
future::ok::<_, hyper::Error>(Response::new(Full::new(Bytes::from(auth))))
|
||||||
});
|
});
|
||||||
// Start Hyper Server
|
// Start Server (not wrapped in loop {} since we want a single response server)
|
||||||
|
// If you want server to accept multiple connections, wrap it in loop {}
|
||||||
let (socket, _) = listener.accept().await.unwrap();
|
let (socket, _) = listener.accept().await.unwrap();
|
||||||
Http::new()
|
let socket = TokioIo::new(socket);
|
||||||
.http1_keep_alive(false)
|
tokio::task::spawn(async move {
|
||||||
.serve_connection(socket, svc)
|
http1::Builder::new()
|
||||||
.with_upgrades()
|
.serve_connection(socket, svc)
|
||||||
.await
|
.with_upgrades()
|
||||||
.expect("Server Started");
|
.await
|
||||||
|
.expect("Server Started");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create a netrc file
|
// Create a netrc file
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use futures::future;
|
use futures::future;
|
||||||
|
use http_body_util::Full;
|
||||||
|
use hyper::body::Bytes;
|
||||||
use hyper::header::USER_AGENT;
|
use hyper::header::USER_AGENT;
|
||||||
use hyper::server::conn::Http;
|
use hyper::server::conn::http1;
|
||||||
use hyper::service::service_fn;
|
use hyper::service::service_fn;
|
||||||
use hyper::{Body, Request, Response};
|
use hyper::{Request, Response};
|
||||||
|
use hyper_util::rt::TokioIo;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
use pep508_rs::{MarkerEnvironment, StringVersion};
|
use pep508_rs::{MarkerEnvironment, StringVersion};
|
||||||
use platform_tags::{Arch, Os, Platform};
|
use platform_tags::{Arch, Os, Platform};
|
||||||
use tokio::net::TcpListener;
|
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::LineHaul;
|
use uv_client::LineHaul;
|
||||||
use uv_client::RegistryClientBuilder;
|
use uv_client::RegistryClientBuilder;
|
||||||
|
|
@ -19,8 +23,8 @@ async fn test_user_agent_has_version() -> Result<()> {
|
||||||
let addr = listener.local_addr()?;
|
let addr = listener.local_addr()?;
|
||||||
|
|
||||||
// Spawn the server loop in a background task
|
// Spawn the server loop in a background task
|
||||||
tokio::spawn(async move {
|
let server_task = tokio::spawn(async move {
|
||||||
let svc = service_fn(move |req: Request<Body>| {
|
let svc = service_fn(move |req: Request<hyper::body::Incoming>| {
|
||||||
// Get User Agent Header and send it back in the response
|
// Get User Agent Header and send it back in the response
|
||||||
let user_agent = req
|
let user_agent = req
|
||||||
.headers()
|
.headers()
|
||||||
|
|
@ -28,16 +32,19 @@ async fn test_user_agent_has_version() -> Result<()> {
|
||||||
.and_then(|v| v.to_str().ok())
|
.and_then(|v| v.to_str().ok())
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.unwrap_or_default(); // Empty Default
|
.unwrap_or_default(); // Empty Default
|
||||||
future::ok::<_, hyper::Error>(Response::new(Body::from(user_agent)))
|
future::ok::<_, hyper::Error>(Response::new(Full::new(Bytes::from(user_agent))))
|
||||||
});
|
});
|
||||||
// Start Hyper Server
|
// Start Server (not wrapped in loop {} since we want a single response server)
|
||||||
|
// If you want server to accept multiple connections, wrap it in loop {}
|
||||||
let (socket, _) = listener.accept().await.unwrap();
|
let (socket, _) = listener.accept().await.unwrap();
|
||||||
Http::new()
|
let socket = TokioIo::new(socket);
|
||||||
.http1_keep_alive(false)
|
tokio::task::spawn(async move {
|
||||||
.serve_connection(socket, svc)
|
http1::Builder::new()
|
||||||
.with_upgrades()
|
.serve_connection(socket, svc)
|
||||||
.await
|
.with_upgrades()
|
||||||
.expect("Server Started");
|
.await
|
||||||
|
.expect("Server Started");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize uv-client
|
// Initialize uv-client
|
||||||
|
|
@ -46,7 +53,8 @@ async fn test_user_agent_has_version() -> Result<()> {
|
||||||
|
|
||||||
// Send request to our dummy server
|
// Send request to our dummy server
|
||||||
let res = client
|
let res = client
|
||||||
.uncached_client()
|
.cached_client()
|
||||||
|
.uncached()
|
||||||
.get(format!("http://{addr}"))
|
.get(format!("http://{addr}"))
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
@ -60,6 +68,9 @@ async fn test_user_agent_has_version() -> Result<()> {
|
||||||
// Verify body matches regex
|
// Verify body matches regex
|
||||||
assert_eq!(body, format!("uv/{}", version()));
|
assert_eq!(body, format!("uv/{}", version()));
|
||||||
|
|
||||||
|
// Wait for the server task to complete, to be a good citizen.
|
||||||
|
server_task.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,8 +81,8 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
||||||
let addr = listener.local_addr()?;
|
let addr = listener.local_addr()?;
|
||||||
|
|
||||||
// Spawn the server loop in a background task
|
// Spawn the server loop in a background task
|
||||||
tokio::spawn(async move {
|
let server_task = tokio::spawn(async move {
|
||||||
let svc = service_fn(move |req: Request<Body>| {
|
let svc = service_fn(move |req: Request<hyper::body::Incoming>| {
|
||||||
// Get User Agent Header and send it back in the response
|
// Get User Agent Header and send it back in the response
|
||||||
let user_agent = req
|
let user_agent = req
|
||||||
.headers()
|
.headers()
|
||||||
|
|
@ -79,16 +90,19 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
||||||
.and_then(|v| v.to_str().ok())
|
.and_then(|v| v.to_str().ok())
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
.unwrap_or_default(); // Empty Default
|
.unwrap_or_default(); // Empty Default
|
||||||
future::ok::<_, hyper::Error>(Response::new(Body::from(user_agent)))
|
future::ok::<_, hyper::Error>(Response::new(Full::new(Bytes::from(user_agent))))
|
||||||
});
|
});
|
||||||
// Start Hyper Server
|
// Start Server (not wrapped in loop {} since we want a single response server)
|
||||||
|
// If you want server to accept multiple connections, wrap it in loop {}
|
||||||
let (socket, _) = listener.accept().await.unwrap();
|
let (socket, _) = listener.accept().await.unwrap();
|
||||||
Http::new()
|
let socket = TokioIo::new(socket);
|
||||||
.http1_keep_alive(false)
|
tokio::task::spawn(async move {
|
||||||
.serve_connection(socket, svc)
|
http1::Builder::new()
|
||||||
.with_upgrades()
|
.serve_connection(socket, svc)
|
||||||
.await
|
.with_upgrades()
|
||||||
.expect("Server Started");
|
.await
|
||||||
|
.expect("Server Started");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add some representative markers for an Ubuntu CI runner
|
// Add some representative markers for an Ubuntu CI runner
|
||||||
|
|
@ -142,7 +156,8 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
||||||
|
|
||||||
// Send request to our dummy server
|
// Send request to our dummy server
|
||||||
let res = client
|
let res = client
|
||||||
.uncached_client()
|
.cached_client()
|
||||||
|
.uncached()
|
||||||
.get(format!("http://{addr}"))
|
.get(format!("http://{addr}"))
|
||||||
.send()
|
.send()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
@ -153,6 +168,9 @@ async fn test_user_agent_has_linehaul() -> Result<()> {
|
||||||
// Check User Agent
|
// Check User Agent
|
||||||
let body = res.text().await?;
|
let body = res.text().await?;
|
||||||
|
|
||||||
|
// Wait for the server task to complete, to be a good citizen.
|
||||||
|
server_task.await?;
|
||||||
|
|
||||||
// Unpack User-Agent with linehaul
|
// Unpack User-Agent with linehaul
|
||||||
let (uv_version, uv_linehaul) = body
|
let (uv_version, uv_linehaul) = body
|
||||||
.split_once(' ')
|
.split_once(' ')
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ indoc = { version = "2.0.4" }
|
||||||
insta = { version = "1.36.1", features = ["filters", "json"] }
|
insta = { version = "1.36.1", features = ["filters", "json"] }
|
||||||
predicates = { version = "3.0.4" }
|
predicates = { version = "3.0.4" }
|
||||||
regex = { version = "1.10.3" }
|
regex = { version = "1.10.3" }
|
||||||
reqwest = { version = "0.11.23", features = ["blocking"], default-features = false }
|
reqwest = { workspace = true, features = ["blocking"], default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["flate2/zlib-ng", "python", "pypi", "git", "maturin", "python-patch"]
|
default = ["flate2/zlib-ng", "python", "pypi", "git", "maturin", "python-patch"]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue