mirror of
https://github.com/seemueller-io/hyper-custom-cert.git
synced 2025-09-08 22:46:45 +00:00
run cargo fmt
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
use axum::{
|
||||
Router,
|
||||
extract::{Path, Query},
|
||||
response::Json,
|
||||
routing::{get, post, put, delete},
|
||||
Router,
|
||||
routing::{delete, get, post, put},
|
||||
};
|
||||
use hyper_custom_cert::HttpClient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{json, Value};
|
||||
use serde_json::{Value, json};
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -38,39 +38,35 @@ async fn main() {
|
||||
let app = Router::new()
|
||||
// Root endpoint with API overview
|
||||
.route("/", get(api_overview))
|
||||
|
||||
// Basic HTTP client tests
|
||||
.route("/test/client/default", get(test_default_client))
|
||||
.route("/test/client/builder", get(test_builder_client))
|
||||
.route("/test/client/timeout", get(test_timeout_client))
|
||||
.route("/test/client/headers", get(test_headers_client))
|
||||
.route("/test/client/combined", get(test_combined_config))
|
||||
|
||||
// Feature-specific tests
|
||||
.route("/test/features/native-tls", get(test_native_tls_feature))
|
||||
.route("/test/features/rustls", get(test_rustls_feature))
|
||||
.route("/test/features/insecure", get(test_insecure_feature))
|
||||
|
||||
// HTTP method tests
|
||||
.route("/test/methods/get", get(test_get_method))
|
||||
.route("/test/methods/post", post(test_post_method))
|
||||
.route("/test/methods/put", put(test_put_method))
|
||||
.route("/test/methods/delete", delete(test_delete_method))
|
||||
|
||||
// Certificate and TLS tests
|
||||
.route("/test/tls/custom-ca", get(test_custom_ca))
|
||||
.route("/test/tls/cert-pinning", get(test_cert_pinning))
|
||||
.route("/test/tls/self-signed", get(test_self_signed))
|
||||
|
||||
// Configuration tests
|
||||
.route("/test/config/timeout/{seconds}", get(test_custom_timeout))
|
||||
.route("/test/config/headers/{header_count}", get(test_custom_headers))
|
||||
|
||||
.route(
|
||||
"/test/config/headers/{header_count}",
|
||||
get(test_custom_headers),
|
||||
)
|
||||
// Error simulation tests
|
||||
.route("/test/errors/timeout", get(test_timeout_error))
|
||||
.route("/test/errors/invalid-url", get(test_invalid_url))
|
||||
.route("/test/errors/connection", get(test_connection_error))
|
||||
|
||||
// Health and status endpoints
|
||||
.route("/health", get(health_check))
|
||||
.route("/status", get(status_check));
|
||||
@@ -185,7 +181,10 @@ async fn test_timeout_client(Query(params): Query<TimeoutQuery>) -> Json<TestRes
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/client/timeout".to_string(),
|
||||
status: "success".to_string(),
|
||||
message: format!("HttpClient with {}s timeout configured successfully", timeout_secs),
|
||||
message: format!(
|
||||
"HttpClient with {}s timeout configured successfully",
|
||||
timeout_secs
|
||||
),
|
||||
features_tested: vec!["timeout-config".to_string()],
|
||||
error: match result {
|
||||
Ok(_) => None,
|
||||
@@ -197,13 +196,14 @@ async fn test_timeout_client(Query(params): Query<TimeoutQuery>) -> Json<TestRes
|
||||
/// Test custom headers configuration
|
||||
async fn test_headers_client() -> Json<TestResponse> {
|
||||
let mut headers = HashMap::new();
|
||||
headers.insert("User-Agent".to_string(), "hyper-custom-cert-test/1.0".to_string());
|
||||
headers.insert(
|
||||
"User-Agent".to_string(),
|
||||
"hyper-custom-cert-test/1.0".to_string(),
|
||||
);
|
||||
headers.insert("X-Test-Header".to_string(), "test-value".to_string());
|
||||
headers.insert("Accept".to_string(), "application/json".to_string());
|
||||
|
||||
let client = HttpClient::builder()
|
||||
.with_default_headers(headers)
|
||||
.build();
|
||||
let client = HttpClient::builder().with_default_headers(headers).build();
|
||||
let result = client.request("https://httpbin.org/get").await;
|
||||
|
||||
Json(TestResponse {
|
||||
@@ -221,7 +221,10 @@ async fn test_headers_client() -> Json<TestResponse> {
|
||||
/// Test combined configuration options
|
||||
async fn test_combined_config() -> Json<TestResponse> {
|
||||
let mut headers = HashMap::new();
|
||||
headers.insert("User-Agent".to_string(), "hyper-custom-cert-combined/1.0".to_string());
|
||||
headers.insert(
|
||||
"User-Agent".to_string(),
|
||||
"hyper-custom-cert-combined/1.0".to_string(),
|
||||
);
|
||||
headers.insert("X-Combined-Test".to_string(), "true".to_string());
|
||||
|
||||
let client = HttpClient::builder()
|
||||
@@ -233,7 +236,8 @@ async fn test_combined_config() -> Json<TestResponse> {
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/client/combined".to_string(),
|
||||
status: "success".to_string(),
|
||||
message: "HttpClient with combined configuration (timeout + headers) works correctly".to_string(),
|
||||
message: "HttpClient with combined configuration (timeout + headers) works correctly"
|
||||
.to_string(),
|
||||
features_tested: vec!["timeout-config".to_string(), "custom-headers".to_string()],
|
||||
error: match result {
|
||||
Ok(_) => None,
|
||||
@@ -331,7 +335,8 @@ async fn test_insecure_feature() -> Json<TestResponse> {
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/features/insecure".to_string(),
|
||||
status: "success".to_string(),
|
||||
message: "insecure-dangerous feature is working (DO NOT USE IN PRODUCTION!)".to_string(),
|
||||
message: "insecure-dangerous feature is working (DO NOT USE IN PRODUCTION!)"
|
||||
.to_string(),
|
||||
features_tested: vec!["insecure-dangerous".to_string()],
|
||||
error: match (result, result2) {
|
||||
(Ok(_), Ok(_)) => None,
|
||||
@@ -345,7 +350,8 @@ async fn test_insecure_feature() -> Json<TestResponse> {
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/features/insecure".to_string(),
|
||||
status: "skipped".to_string(),
|
||||
message: "insecure-dangerous feature is not enabled (this is good for security!)".to_string(),
|
||||
message: "insecure-dangerous feature is not enabled (this is good for security!)"
|
||||
.to_string(),
|
||||
features_tested: vec![],
|
||||
error: Some("Feature not enabled".to_string()),
|
||||
})
|
||||
@@ -382,7 +388,10 @@ async fn test_post_method(Json(payload): Json<PostData>) -> Json<TestResponse> {
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/methods/post".to_string(),
|
||||
status: "success".to_string(),
|
||||
message: format!("HTTP POST method test completed with data: {}", payload.data),
|
||||
message: format!(
|
||||
"HTTP POST method test completed with data: {}",
|
||||
payload.data
|
||||
),
|
||||
features_tested: vec!["post-request".to_string()],
|
||||
error: match result {
|
||||
Ok(_) => None,
|
||||
@@ -400,7 +409,10 @@ async fn test_put_method(Json(payload): Json<PostData>) -> Json<TestResponse> {
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/methods/put".to_string(),
|
||||
status: "success".to_string(),
|
||||
message: format!("HTTP PUT method test completed (simulated via POST) with data: {}", payload.data),
|
||||
message: format!(
|
||||
"HTTP PUT method test completed (simulated via POST) with data: {}",
|
||||
payload.data
|
||||
),
|
||||
features_tested: vec!["put-request-simulation".to_string()],
|
||||
error: match result {
|
||||
Ok(_) => None,
|
||||
@@ -472,12 +484,11 @@ async fn test_cert_pinning() -> Json<TestResponse> {
|
||||
#[cfg(feature = "rustls")]
|
||||
{
|
||||
// Example SHA256 fingerprints (these are demo values)
|
||||
let pins = vec![
|
||||
[0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f,
|
||||
0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff, 0x0f,
|
||||
0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f,
|
||||
0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef, 0xff, 0x0f],
|
||||
];
|
||||
let pins = vec![[
|
||||
0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf, 0xbf, 0xcf, 0xdf, 0xef,
|
||||
0xff, 0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf, 0xbf, 0xcf,
|
||||
0xdf, 0xef, 0xff, 0x0f,
|
||||
]];
|
||||
|
||||
let client = HttpClient::builder()
|
||||
.with_timeout(Duration::from_secs(10))
|
||||
@@ -535,7 +546,8 @@ async fn test_self_signed() -> Json<TestResponse> {
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/tls/self-signed".to_string(),
|
||||
status: "skipped".to_string(),
|
||||
message: "Self-signed test requires insecure-dangerous feature (good for security!)".to_string(),
|
||||
message: "Self-signed test requires insecure-dangerous feature (good for security!)"
|
||||
.to_string(),
|
||||
features_tested: vec![],
|
||||
error: Some("insecure-dangerous feature not enabled".to_string()),
|
||||
})
|
||||
@@ -549,9 +561,7 @@ async fn test_self_signed() -> Json<TestResponse> {
|
||||
/// Test custom timeout configuration
|
||||
async fn test_custom_timeout(Path(seconds): Path<u64>) -> Json<TestResponse> {
|
||||
let timeout_duration = Duration::from_secs(seconds);
|
||||
let client = HttpClient::builder()
|
||||
.with_timeout(timeout_duration)
|
||||
.build();
|
||||
let client = HttpClient::builder().with_timeout(timeout_duration).build();
|
||||
let result = client.request("https://httpbin.org/delay/1");
|
||||
|
||||
Json(TestResponse {
|
||||
@@ -571,14 +581,14 @@ async fn test_custom_headers(Path(header_count): Path<usize>) -> Json<TestRespon
|
||||
let mut headers = HashMap::new();
|
||||
|
||||
for i in 0..header_count {
|
||||
headers.insert(
|
||||
format!("X-Test-Header-{}", i),
|
||||
format!("test-value-{}", i),
|
||||
);
|
||||
headers.insert(format!("X-Test-Header-{}", i), format!("test-value-{}", i));
|
||||
}
|
||||
|
||||
// Add some standard headers
|
||||
headers.insert("User-Agent".to_string(), "hyper-custom-cert-headers-test/1.0".to_string());
|
||||
headers.insert(
|
||||
"User-Agent".to_string(),
|
||||
"hyper-custom-cert-headers-test/1.0".to_string(),
|
||||
);
|
||||
headers.insert("Accept".to_string(), "application/json".to_string());
|
||||
|
||||
let client = HttpClient::builder()
|
||||
@@ -590,7 +600,10 @@ async fn test_custom_headers(Path(header_count): Path<usize>) -> Json<TestRespon
|
||||
Json(TestResponse {
|
||||
endpoint: format!("/test/config/headers/{}", header_count),
|
||||
status: "success".to_string(),
|
||||
message: format!("Custom headers test with {} headers completed", header_count + 2),
|
||||
message: format!(
|
||||
"Custom headers test with {} headers completed",
|
||||
header_count + 2
|
||||
),
|
||||
features_tested: vec!["custom-headers".to_string()],
|
||||
error: match result.await {
|
||||
Ok(_) => None,
|
||||
@@ -614,7 +627,12 @@ async fn test_timeout_error() -> Json<TestResponse> {
|
||||
let awaited = result.await;
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/errors/timeout".to_string(),
|
||||
status: if awaited.is_err() { "success" } else { "unexpected" }.to_string(),
|
||||
status: if awaited.is_err() {
|
||||
"success"
|
||||
} else {
|
||||
"unexpected"
|
||||
}
|
||||
.to_string(),
|
||||
message: "Timeout error simulation test completed".to_string(),
|
||||
features_tested: vec!["timeout-error-handling".to_string()],
|
||||
error: match awaited {
|
||||
@@ -633,7 +651,12 @@ async fn test_invalid_url() -> Json<TestResponse> {
|
||||
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/errors/invalid-url".to_string(),
|
||||
status: if awaited.is_err() { "success" } else { "unexpected" }.to_string(),
|
||||
status: if awaited.is_err() {
|
||||
"success"
|
||||
} else {
|
||||
"unexpected"
|
||||
}
|
||||
.to_string(),
|
||||
message: "Invalid URL error simulation test completed".to_string(),
|
||||
features_tested: vec!["url-validation".to_string()],
|
||||
error: match awaited {
|
||||
@@ -654,7 +677,12 @@ async fn test_connection_error() -> Json<TestResponse> {
|
||||
|
||||
Json(TestResponse {
|
||||
endpoint: "/test/errors/connection".to_string(),
|
||||
status: if awaited.is_err() { "success" } else { "unexpected" }.to_string(),
|
||||
status: if awaited.is_err() {
|
||||
"success"
|
||||
} else {
|
||||
"unexpected"
|
||||
}
|
||||
.to_string(),
|
||||
message: "Connection error simulation test completed".to_string(),
|
||||
features_tested: vec!["connection-error-handling".to_string()],
|
||||
error: match awaited {
|
||||
@@ -695,7 +723,7 @@ async fn status_check() -> Json<Value> {
|
||||
// Test basic client creation to verify library is working
|
||||
let client_test = match HttpClient::new().request("https://httpbin.org/get").await {
|
||||
Ok(_) => "operational",
|
||||
Err(_) => "degraded"
|
||||
Err(_) => "degraded",
|
||||
};
|
||||
|
||||
Json(json!({
|
||||
|
@@ -36,10 +36,10 @@ use std::path::Path;
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::Bytes;
|
||||
use hyper::{body::Incoming, Request, Response, StatusCode, Uri, Method};
|
||||
use http_body_util::BodyExt;
|
||||
use hyper::{Method, Request, Response, StatusCode, Uri, body::Incoming};
|
||||
use hyper_util::client::legacy::Client;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
use http_body_util::BodyExt;
|
||||
|
||||
/// Options for controlling HTTP requests.
|
||||
///
|
||||
@@ -349,12 +349,14 @@ impl HttpClient {
|
||||
/// # Ok::<(), hyper_custom_cert::ClientError>(())
|
||||
/// # };
|
||||
/// ```
|
||||
pub async fn request_with_options(&self, url: &str, options: Option<RequestOptions>) -> Result<HttpResponse, ClientError> {
|
||||
pub async fn request_with_options(
|
||||
&self,
|
||||
url: &str,
|
||||
options: Option<RequestOptions>,
|
||||
) -> Result<HttpResponse, ClientError> {
|
||||
let uri: Uri = url.parse()?;
|
||||
|
||||
let req = Request::builder()
|
||||
.method(Method::GET)
|
||||
.uri(uri);
|
||||
let req = Request::builder().method(Method::GET).uri(uri);
|
||||
|
||||
// Add default headers to the request. This ensures that any headers
|
||||
// set during the client's construction (e.g., API keys, User-Agent)
|
||||
@@ -439,8 +441,15 @@ impl HttpClient {
|
||||
/// # Ok::<(), hyper_custom_cert::ClientError>(())
|
||||
/// # };
|
||||
/// ```
|
||||
#[deprecated(since = "0.4.0", note = "Use post_with_options(url, body, Some(options)) instead")]
|
||||
pub async fn post<B: AsRef<[u8]>>(&self, url: &str, body: B) -> Result<HttpResponse, ClientError> {
|
||||
#[deprecated(
|
||||
since = "0.4.0",
|
||||
note = "Use post_with_options(url, body, Some(options)) instead"
|
||||
)]
|
||||
pub async fn post<B: AsRef<[u8]>>(
|
||||
&self,
|
||||
url: &str,
|
||||
body: B,
|
||||
) -> Result<HttpResponse, ClientError> {
|
||||
self.post_with_options(url, body, None).await
|
||||
}
|
||||
|
||||
@@ -479,12 +488,15 @@ impl HttpClient {
|
||||
/// # Ok::<(), hyper_custom_cert::ClientError>(())
|
||||
/// # };
|
||||
/// ```
|
||||
pub async fn post_with_options<B: AsRef<[u8]>>(&self, url: &str, body: B, options: Option<RequestOptions>) -> Result<HttpResponse, ClientError> {
|
||||
pub async fn post_with_options<B: AsRef<[u8]>>(
|
||||
&self,
|
||||
url: &str,
|
||||
body: B,
|
||||
options: Option<RequestOptions>,
|
||||
) -> Result<HttpResponse, ClientError> {
|
||||
let uri: Uri = url.parse()?;
|
||||
|
||||
let req = Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(uri);
|
||||
let req = Request::builder().method(Method::POST).uri(uri);
|
||||
|
||||
// Add default headers to the request for consistency across client operations.
|
||||
let mut req = req;
|
||||
@@ -562,8 +574,9 @@ impl HttpClient {
|
||||
// Create a TLS connector that accepts invalid certificates
|
||||
let mut tls_builder = native_tls::TlsConnector::builder();
|
||||
tls_builder.danger_accept_invalid_certs(true);
|
||||
let tls_connector = tls_builder.build()
|
||||
.map_err(|e| ClientError::TlsError(format!("Failed to build TLS connector: {}", e)))?;
|
||||
let tls_connector = tls_builder.build().map_err(|e| {
|
||||
ClientError::TlsError(format!("Failed to build TLS connector: {}", e))
|
||||
})?;
|
||||
|
||||
// Create the tokio-native-tls connector
|
||||
let tokio_connector = tokio_native_tls::TlsConnector::from(tls_connector);
|
||||
@@ -571,12 +584,10 @@ impl HttpClient {
|
||||
// Create the HTTPS connector using the HTTP and TLS connectors
|
||||
let connector = hyper_tls::HttpsConnector::from((http_connector, tokio_connector));
|
||||
|
||||
let client = Client::builder(TokioExecutor::new())
|
||||
.build(connector);
|
||||
let client = Client::builder(TokioExecutor::new()).build(connector);
|
||||
let resp = tokio::time::timeout(self.timeout, client.request(req))
|
||||
.await
|
||||
.map_err(|_| ClientError::TlsError("Request timed out".to_string()))?
|
||||
?;
|
||||
.map_err(|_| ClientError::TlsError("Request timed out".to_string()))??;
|
||||
return self.build_response(resp).await;
|
||||
}
|
||||
|
||||
@@ -585,8 +596,7 @@ impl HttpClient {
|
||||
let client = Client::builder(TokioExecutor::new()).build(connector);
|
||||
let resp = tokio::time::timeout(self.timeout, client.request(req))
|
||||
.await
|
||||
.map_err(|_| ClientError::TlsError("Request timed out".to_string()))?
|
||||
?;
|
||||
.map_err(|_| ClientError::TlsError("Request timed out".to_string()))??;
|
||||
self.build_response(resp).await
|
||||
}
|
||||
#[cfg(all(feature = "rustls", not(feature = "native-tls")))]
|
||||
@@ -605,7 +615,10 @@ impl HttpClient {
|
||||
// Add each cert to the root store
|
||||
for cert in &native_certs.certs {
|
||||
if let Err(e) = root_cert_store.add(cert.clone()) {
|
||||
return Err(ClientError::TlsError(format!("Failed to add native cert to root store: {}", e)));
|
||||
return Err(ClientError::TlsError(format!(
|
||||
"Failed to add native cert to root store: {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,17 +628,26 @@ impl HttpClient {
|
||||
for cert_result in rustls_pemfile::certs(&mut reader) {
|
||||
match cert_result {
|
||||
Ok(cert) => {
|
||||
root_cert_store.add(cert)
|
||||
.map_err(|e| ClientError::TlsError(format!("Failed to add custom cert to root store: {}", e)))?;
|
||||
},
|
||||
Err(e) => return Err(ClientError::TlsError(format!("Failed to parse PEM cert: {}", e))),
|
||||
root_cert_store.add(cert).map_err(|e| {
|
||||
ClientError::TlsError(format!(
|
||||
"Failed to add custom cert to root store: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(ClientError::TlsError(format!(
|
||||
"Failed to parse PEM cert: {}",
|
||||
e
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure rustls
|
||||
let mut config_builder = rustls::ClientConfig::builder()
|
||||
.with_root_certificates(root_cert_store);
|
||||
let mut config_builder =
|
||||
rustls::ClientConfig::builder().with_root_certificates(root_cert_store);
|
||||
|
||||
let rustls_config = config_builder.with_no_client_auth();
|
||||
|
||||
@@ -636,11 +658,11 @@ impl HttpClient {
|
||||
// and NEVER in production environments. This creates a vulnerability to
|
||||
// man-in-the-middle attacks and is extremely dangerous.
|
||||
|
||||
use std::sync::Arc;
|
||||
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified};
|
||||
use rustls::DigitallySignedStruct;
|
||||
use rustls::SignatureScheme;
|
||||
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified};
|
||||
use rustls::pki_types::UnixTime;
|
||||
use std::sync::Arc;
|
||||
|
||||
// Override the certificate verifier with a no-op verifier that accepts all certificates
|
||||
#[derive(Debug)]
|
||||
@@ -701,7 +723,9 @@ impl HttpClient {
|
||||
|
||||
// Set up the dangerous configuration with no certificate verification
|
||||
let mut config = rustls_config.clone();
|
||||
config.dangerous().set_certificate_verifier(Arc::new(NoCertificateVerification {}));
|
||||
config
|
||||
.dangerous()
|
||||
.set_certificate_verifier(Arc::new(NoCertificateVerification {}));
|
||||
config
|
||||
} else {
|
||||
rustls_config
|
||||
@@ -711,11 +735,13 @@ impl HttpClient {
|
||||
#[cfg(feature = "rustls")]
|
||||
let rustls_config = if let Some(ref pins) = self.pinned_cert_sha256 {
|
||||
// Implement certificate pinning by creating a custom certificate verifier
|
||||
use std::sync::Arc;
|
||||
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
|
||||
use rustls::DigitallySignedStruct;
|
||||
use rustls::SignatureScheme;
|
||||
use rustls::client::danger::{
|
||||
HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
|
||||
};
|
||||
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
|
||||
use std::sync::Arc;
|
||||
|
||||
// Create a custom certificate verifier that checks certificate pins
|
||||
struct CertificatePinner {
|
||||
@@ -733,10 +759,16 @@ impl HttpClient {
|
||||
now: UnixTime,
|
||||
) -> Result<ServerCertVerified, rustls::Error> {
|
||||
// First, use the inner verifier to do standard verification
|
||||
self.inner.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now)?;
|
||||
self.inner.verify_server_cert(
|
||||
end_entity,
|
||||
intermediates,
|
||||
server_name,
|
||||
ocsp_response,
|
||||
now,
|
||||
)?;
|
||||
|
||||
// Then verify the pin
|
||||
use sha2::{Sha256, Digest};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(end_entity.as_ref());
|
||||
@@ -750,7 +782,9 @@ impl HttpClient {
|
||||
}
|
||||
|
||||
// If we got here, none of the pins matched
|
||||
Err(rustls::Error::General("Certificate pin verification failed".into()))
|
||||
Err(rustls::Error::General(
|
||||
"Certificate pin verification failed".into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn verify_tls12_signature(
|
||||
@@ -783,7 +817,12 @@ impl HttpClient {
|
||||
let default_verifier = rustls::client::WebPkiServerVerifier::builder()
|
||||
.with_root_certificates(root_cert_store.clone())
|
||||
.build()
|
||||
.map_err(|e| ClientError::TlsError(format!("Failed to build certificate verifier: {}", e)))?;
|
||||
.map_err(|e| {
|
||||
ClientError::TlsError(format!(
|
||||
"Failed to build certificate verifier: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
let cert_pinner = Arc::new(CertificatePinner {
|
||||
pins: pins.clone(),
|
||||
@@ -810,8 +849,7 @@ impl HttpClient {
|
||||
let client = Client::builder(TokioExecutor::new()).build(https_connector);
|
||||
let resp = tokio::time::timeout(self.timeout, client.request(req))
|
||||
.await
|
||||
.map_err(|_| ClientError::TlsError("Request timed out".to_string()))?
|
||||
?;
|
||||
.map_err(|_| ClientError::TlsError("Request timed out".to_string()))??;
|
||||
self.build_response(resp).await
|
||||
}
|
||||
#[cfg(not(any(feature = "native-tls", feature = "rustls")))]
|
||||
@@ -824,8 +862,7 @@ impl HttpClient {
|
||||
let client = Client::builder(TokioExecutor::new()).build(connector);
|
||||
let resp = tokio::time::timeout(self.timeout, client.request(req))
|
||||
.await
|
||||
.map_err(|_| ClientError::TlsError("Request timed out".to_string()))?
|
||||
?;
|
||||
.map_err(|_| ClientError::TlsError("Request timed out".to_string()))??;
|
||||
self.build_response(resp).await
|
||||
}
|
||||
}
|
||||
@@ -865,7 +902,10 @@ impl HttpClient {
|
||||
/// On wasm32 targets, runtime methods are stubbed and return
|
||||
/// `ClientError::WasmNotImplemented` because browsers do not allow
|
||||
/// programmatic installation/trust of custom CAs.
|
||||
#[deprecated(since = "0.4.0", note = "Use request_with_options(url, Some(options)) instead")]
|
||||
#[deprecated(
|
||||
since = "0.4.0",
|
||||
note = "Use request_with_options(url, Some(options)) instead"
|
||||
)]
|
||||
pub fn request(&self, _url: &str) -> Result<(), ClientError> {
|
||||
Err(ClientError::WasmNotImplemented)
|
||||
}
|
||||
@@ -873,18 +913,30 @@ impl HttpClient {
|
||||
/// On wasm32 targets, runtime methods are stubbed and return
|
||||
/// `ClientError::WasmNotImplemented` because browsers do not allow
|
||||
/// programmatic installation/trust of custom CAs.
|
||||
pub fn request_with_options(&self, _url: &str, _options: Option<RequestOptions>) -> Result<(), ClientError> {
|
||||
pub fn request_with_options(
|
||||
&self,
|
||||
_url: &str,
|
||||
_options: Option<RequestOptions>,
|
||||
) -> Result<(), ClientError> {
|
||||
Err(ClientError::WasmNotImplemented)
|
||||
}
|
||||
|
||||
/// POST is also not implemented on wasm32 targets for the same reason.
|
||||
#[deprecated(since = "0.4.0", note = "Use post_with_options(url, body, Some(options)) instead")]
|
||||
#[deprecated(
|
||||
since = "0.4.0",
|
||||
note = "Use post_with_options(url, body, Some(options)) instead"
|
||||
)]
|
||||
pub fn post<B: AsRef<[u8]>>(&self, _url: &str, _body: B) -> Result<(), ClientError> {
|
||||
Err(ClientError::WasmNotImplemented)
|
||||
}
|
||||
|
||||
/// POST is also not implemented on wasm32 targets for the same reason.
|
||||
pub fn post_with_options<B: AsRef<[u8]>>(&self, _url: &str, _body: B, _options: Option<RequestOptions>) -> Result<(), ClientError> {
|
||||
pub fn post_with_options<B: AsRef<[u8]>>(
|
||||
&self,
|
||||
_url: &str,
|
||||
_body: B,
|
||||
_options: Option<RequestOptions>,
|
||||
) -> Result<(), ClientError> {
|
||||
Err(ClientError::WasmNotImplemented)
|
||||
}
|
||||
}
|
||||
|
@@ -97,7 +97,6 @@ fn default_client_static_method() {
|
||||
let _client = HttpClient::default();
|
||||
}
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn post_smoke_default() {
|
||||
// Smoke test for POST support with default features
|
||||
|
@@ -55,13 +55,14 @@ fn test_timeout_configuration_for_example_server() {
|
||||
fn test_headers_configuration_for_example_server() {
|
||||
// Test custom headers configuration
|
||||
let mut headers = HashMap::new();
|
||||
headers.insert("User-Agent".to_string(), "hyper-custom-cert-integration-test/1.0".to_string());
|
||||
headers.insert(
|
||||
"User-Agent".to_string(),
|
||||
"hyper-custom-cert-integration-test/1.0".to_string(),
|
||||
);
|
||||
headers.insert("X-Test-Client".to_string(), "integration".to_string());
|
||||
headers.insert("Accept".to_string(), "application/json".to_string());
|
||||
|
||||
let client = HttpClient::builder()
|
||||
.with_default_headers(headers)
|
||||
.build();
|
||||
let client = HttpClient::builder().with_default_headers(headers).build();
|
||||
|
||||
// Smoke test - verify header configuration compiles
|
||||
let _ = client;
|
||||
@@ -71,7 +72,10 @@ fn test_headers_configuration_for_example_server() {
|
||||
fn test_combined_configuration_for_example_server() {
|
||||
// Test combining multiple configuration options
|
||||
let mut headers = HashMap::new();
|
||||
headers.insert("User-Agent".to_string(), "hyper-custom-cert-combined-test/1.0".to_string());
|
||||
headers.insert(
|
||||
"User-Agent".to_string(),
|
||||
"hyper-custom-cert-combined-test/1.0".to_string(),
|
||||
);
|
||||
|
||||
let client = HttpClient::builder()
|
||||
.with_timeout(Duration::from_secs(30))
|
||||
@@ -132,9 +136,7 @@ fn test_rustls_cert_pinning_configuration() {
|
||||
let dummy_pin = [0u8; 32];
|
||||
let pins = vec![dummy_pin];
|
||||
|
||||
let client = HttpClient::builder()
|
||||
.with_pinned_cert_sha256(pins)
|
||||
.build();
|
||||
let client = HttpClient::builder().with_pinned_cert_sha256(pins).build();
|
||||
|
||||
// Smoke test - verify cert pinning compiles
|
||||
let _ = client;
|
||||
|
Reference in New Issue
Block a user