From 408396a7ce1333a3ce500a04bcf0a014d18fc226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=9C=AA=E6=9D=A5?= Date: Wed, 16 Apr 2025 10:35:52 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9C=80=E6=B1=82=E6=B5=8B=E8=AF=95=E8=A1=A5?= =?UTF-8?q?=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 徐未来 --- ylong_http/src/h2/mod.rs | 3 +- ylong_http/src/h3/mod.rs | 3 +- ylong_http/src/pseudo.rs | 6 +- ylong_http_client/tests/common/mod.rs | 49 ++ ylong_http_client/tests/file/invalid_cert.pem | 19 + ylong_http_client/tests/file/invalid_key.pem | 28 + .../tests/sdv_async_certs_adapter.rs | 268 ++++++++ .../tests/sdv_async_dns_resolver.rs | 592 ++++++++++++++++++ 8 files changed, 963 insertions(+), 5 deletions(-) create mode 100644 ylong_http_client/tests/file/invalid_cert.pem create mode 100644 ylong_http_client/tests/file/invalid_key.pem create mode 100644 ylong_http_client/tests/sdv_async_certs_adapter.rs create mode 100644 ylong_http_client/tests/sdv_async_dns_resolver.rs diff --git a/ylong_http/src/h2/mod.rs b/ylong_http/src/h2/mod.rs index f9c235d..b583ba6 100644 --- a/ylong_http/src/h2/mod.rs +++ b/ylong_http/src/h2/mod.rs @@ -57,7 +57,6 @@ mod frame; mod hpack; mod parts; -pub use crate::pseudo::PseudoHeaders; pub use decoder::{FrameDecoder, FrameKind, Frames, FramesIntoIter}; pub use encoder::FrameEncoder; pub use error::{ErrorCode, H2Error}; @@ -67,3 +66,5 @@ pub use frame::{ }; pub(crate) use hpack::{HpackDecoder, HpackEncoder}; pub use parts::Parts; + +pub use crate::pseudo::PseudoHeaders; diff --git a/ylong_http/src/h3/mod.rs b/ylong_http/src/h3/mod.rs index d39e7b8..8b5db50 100644 --- a/ylong_http/src/h3/mod.rs +++ b/ylong_http/src/h3/mod.rs @@ -23,7 +23,6 @@ mod qpack; // mod octets; mod stream; -pub use crate::pseudo::PseudoHeaders; pub use decoder::FrameDecoder; pub use encoder::FrameEncoder; pub use error::{DecodeError, EncodeError, H3Error, H3ErrorCode}; @@ -37,6 +36,8 @@ pub use stream::{ QPACK_ENCODER_STREAM_TYPE, }; +pub use crate::pseudo::PseudoHeaders; + pub(crate) fn is_bidirectional(id: u64) -> bool { (id & 0x02) == 0 } diff --git a/ylong_http/src/pseudo.rs b/ylong_http/src/pseudo.rs index c8f4552..7fc8189 100644 --- a/ylong_http/src/pseudo.rs +++ b/ylong_http/src/pseudo.rs @@ -12,8 +12,9 @@ // limitations under the License. //! HTTP [`PseudoHeaders`], HTTP/2 uses a special pseudo-header file beginning -//! with the “:” character (ASCII 0x3a) to replace the message start line in HTTP/1.x -//! to convey the target URI, request method, and status code of the response. +//! with the “:” character (ASCII 0x3a) to replace the message start line in +//! HTTP/1.x to convey the target URI, request method, and status code of the +//! response. //! //! //! # Example @@ -23,7 +24,6 @@ //! pseudo.set_method(Some("GET".to_string())); //! assert_eq!(pseudo.method(), Some("GET")); //! ``` -//! /// [Pseudo-Header fields] that may appear in http2 and http3 header fields. /// diff --git a/ylong_http_client/tests/common/mod.rs b/ylong_http_client/tests/common/mod.rs index bc271b6..1c6ef09 100644 --- a/ylong_http_client/tests/common/mod.rs +++ b/ylong_http_client/tests/common/mod.rs @@ -19,6 +19,7 @@ mod sync_utils; use tokio::runtime::Runtime; +#[macro_export] macro_rules! define_service_handle { ( HTTP; @@ -198,6 +199,54 @@ macro_rules! start_http_server { server_shutdown: server_rx, } }}; + ( + HTTP; + Ipv6; + $server_fn: ident + ) => {{ + use hyper::service::{make_service_fn, service_fn}; + use std::convert::Infallible; + use tokio::sync::mpsc::channel; + + let (start_tx, start_rx) = channel::<()>(1); + let (client_tx, mut client_rx) = channel::<()>(1); + let (server_tx, server_rx) = channel::<()>(1); + + let tcp_listener = std::net::TcpListener::bind("::1:0").expect("server bind port failed !"); + let addr = tcp_listener.local_addr().expect("get server local address failed!"); + let port = addr.port(); + let server = hyper::Server::from_tcp(tcp_listener).expect("build hyper server from tcp listener failed !"); + + tokio::spawn(async move { + let make_svc = + make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn($server_fn)) }); + server + .serve(make_svc) + .with_graceful_shutdown(async { + start_tx + .send(()) + .await + .expect("Start channel (Client-Half) be closed unexpectedly"); + client_rx + .recv() + .await + .expect("Client channel (Client-Half) be closed unexpectedly"); + }) + .await + .expect("Start server failed"); + server_tx + .send(()) + .await + .expect("Server channel (Client-Half) be closed unexpectedly"); + }); + + HttpHandle { + port, + server_start: start_rx, + client_shutdown: client_tx, + server_shutdown: server_rx, + } + }}; ( HTTPS; $service_fn: ident, diff --git a/ylong_http_client/tests/file/invalid_cert.pem b/ylong_http_client/tests/file/invalid_cert.pem new file mode 100644 index 0000000..506c555 --- /dev/null +++ b/ylong_http_client/tests/file/invalid_cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDHTCCAgWgAwIBAgIUdr7iGoCdo6bYOrzir7kSuzUapMswDQYJKoZIhvcNAQEL +BQAwHjEcMBoGA1UEAwwTaW52YWxpZC5leGFtcGxlLmNvbTAeFw0yNDA0MjExMDU2 +NTFaFw0yNDA0MjIxMDU2NTFaMB4xHDAaBgNVBAMME2ludmFsaWQuZXhhbXBsZS5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8NNBIHOGKY7LbTe4j +WstizazBBKkZpmi2uvRp6JpfCGF2S+sYdEAWQxhjI/iczJHRC5vKjFRTPGl+xWsn +WcenF/+bnRvvYB7lzJZJi5GMd/hYEebWH3q4a/b6oVod7VuFUmbHRad/Soc067eG +aukFWqLoLGfxPnrooA1hp+IXEkeYdXKkDuAoYC79GtdEz5I71ru5n1XqJwlijvNZ ++CH3BS7uSuxMuZ/5MpbxyGXcb6jsOKenWOIXtxSAfXAua5FAj0ELqOWdQIUcE0W5 +o8LnRd3tIG7ru5Au7qA24EFQfhOzrRXcCBe69lsaKsnAEisxGtAr2Npp5wunL0mT +VfpxAgMBAAGjUzBRMB0GA1UdDgQWBBT1p0UvvayS6U2HbuIdsHhW9VELVzAfBgNV +HSMEGDAWgBT1p0UvvayS6U2HbuIdsHhW9VELVzAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4IBAQAXKgfjJ9zSrpFWf7WkjX5TICdN71cnhKc74YBiOdCD +ZHtPj/3yLmuBgEQx47I5HU5i2Pk1ZiFQa9GkTAv2NnUi9T14ZQbx0nUQr9VtqxER +4lmelBxAm5nnLwm1zFGF27eKr1nE1CD9dBt5bD1Ud3QialtWPYnIi7UehjwgMvY5 +8fO334iUR3E0KrJqasAogb9CpivvsIXxfr5DcVdj84rfzUrSeZjy7b0iJ1cph/s3 +jhisIQkMTAlsEeeaicTaS6x9/9/SIWL8ukl6Ig9J+X09GWWF/3uTDAgdf2EVfoDX +WKEylDkDJV7PICVFnSrV8/QA7ED63DFmLaaj/ASwyFmg +-----END CERTIFICATE----- diff --git a/ylong_http_client/tests/file/invalid_key.pem b/ylong_http_client/tests/file/invalid_key.pem new file mode 100644 index 0000000..80d3832 --- /dev/null +++ b/ylong_http_client/tests/file/invalid_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC8NNBIHOGKY7Lb +Te4jWstizazBBKkZpmi2uvRp6JpfCGF2S+sYdEAWQxhjI/iczJHRC5vKjFRTPGl+ +xWsnWcenF/+bnRvvYB7lzJZJi5GMd/hYEebWH3q4a/b6oVod7VuFUmbHRad/Soc0 +67eGaukFWqLoLGfxPnrooA1hp+IXEkeYdXKkDuAoYC79GtdEz5I71ru5n1XqJwli +jvNZ+CH3BS7uSuxMuZ/5MpbxyGXcb6jsOKenWOIXtxSAfXAua5FAj0ELqOWdQIUc +E0W5o8LnRd3tIG7ru5Au7qA24EFQfhOzrRXcCBe69lsaKsnAEisxGtAr2Npp5wun +L0mTVfpxAgMBAAECggEAFoMtv9Z+jwZ2dD+Z4u4/IlouVidHz+rlHyh1SH7bik8P +E26o2DO9n7wWXHOGz0C0F4CPOdb31j4Fc3XLZ7lYRa878sID/6nsgDns5y6z8gy+ +rtAzX70DubAmapvm0uobtrKg2yjEjJgeXLDucrxyk6KgFmwLYeLConVUrG//vhkL +wafnh6+NPOHGZJrFp0nKCoQ24YMQkW3hfyV3sOB9J326t+2TenYHi5gaiZCpSMRE +/P7Xt33GQf03uwYdcFKjIFi3eNyA+lC14GMEIDjpSjt7pyCSUNGmVoqq3g51qqHk +LJlA7anDfxg7FBWma3dLKGsWYo9rk6/+bDt11ECCUQKBgQDLzTkyeXjSQbqXiy9F +Rcb8oKj1DM4wAXzA8tVPbjCaVGxwcUAI7BK8Fa/owV0jcQ5vZagCGu3aBVYRHvMm +6QyOd7xD4lFarzkd/6SaohCaIZy9F7Lmy0/WvjjNBwSrFQgE6wZyVFx6PgfSjGA7 +8Zpzu9B3K3CfPOjDeDXFMkDpWQKBgQDsaQqGiZi+Jgg2YvdobEU1QJUL4//id0KT +1rMOp5h4o3D8i+AJxZeIjbF9IDkjGsIRA+FoGmhQHGONn4KlWN1JLOd4YxZI2QZc +FAj10WdVkltm8WP7Tv9wg0RW6iil7cD+Ye7qCZIGG1K7tz+7ewYs2ajLt8tYqz95 +9/gaS/3e2QKBgDaTWHPJz68OZvdaEGiOjQDtzRECQtzcKRpKbRD+Qwmw/fEGgs1g +2ot/i0HbNiPdjK4MHPejWnp931RqY1bSJcipC8NqAbs1HSiJYvz91VBRG/YPOJtR +aqno+rD85iUiVqlqfqlzKeiaX+HkMYkb4+6GJwcinOAnoXVm5Y9PK87RAoGAOiCx +t9bJOae8STMYRhKpCajxfNT4q8RLGHKn4CiRMshtFYuG8KAm3CqKw4tSwTDpGqnr +mF812szAJahcxS8OadmiqFUku5CxIZ7DfT2ZvM6ZrrwXq0kHQbDk8sHscK/+zg1i +YyUrRFkfhIxQHVboc8qo2D4+v3SLzP+Nf1nD+DECgYAZ/QcuOw0aHPHm7mr27JKY +NXCvJ25z4xFN3u6vLSrSffyFNpHDNw7is2KAeOiZeNOXJKwv+bsU9O13dhI6sYEM +ibWVegJXHujHojD7xEnfKkjDtvGkYbrV9rH7g3H1iUPfU2Y26/RmgJYNxjohGKmu +K1wAe1PqQ+WMGTZgDoGLDw== +-----END PRIVATE KEY----- diff --git a/ylong_http_client/tests/sdv_async_certs_adapter.rs b/ylong_http_client/tests/sdv_async_certs_adapter.rs new file mode 100644 index 0000000..dbf02d1 --- /dev/null +++ b/ylong_http_client/tests/sdv_async_certs_adapter.rs @@ -0,0 +1,268 @@ +// Copyright (c) 2025 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(all(feature = "async", feature = "tokio_base", feature = "__tls"))] + +mod common; + +use std::convert::Infallible; + +use ylong_http::response::status::StatusCode; +use ylong_http_client::async_impl::{Body, Client, Request}; +use ylong_http_client::{CertVerifier, ServerCerts}; + +use crate::common::init_test_work_runtime; + +async fn server_fn( + _req: hyper::Request, +) -> Result, Infallible> { + let response = hyper::Response::builder() + .status(hyper::StatusCode::OK) + .body(hyper::Body::empty()) + .expect("build hyper response failed"); + Ok(response) +} + +#[test] +#[cfg(feature = "http1_1")] +fn sdv_client_request_http1_verify_true() { + struct Verifier; + impl CertVerifier for Verifier { + fn verify(&self, _certs: &ServerCerts) -> bool { + true + } + } + + define_service_handle!(HTTPS;); + + let rt = init_test_work_runtime(4); + + let mut handles_vec = vec![]; + start_server!( + HTTPS; + ServerNum: 1, + Runtime: rt, + Handles: handles_vec, + ServeFnName: server_fn, + ); + let handle = handles_vec.pop().expect("No more handles !"); + + let client = Client::builder() + .cert_verifier(Verifier) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .url(format!("{}:{}", "127.0.0.1", handle.port).as_str()) + .version("HTTP/1.1") + .method("GET") + .body(Body::empty()) + .expect("Client build Request failed."); + + rt.block_on(async move { + let response = client.request(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + }); +} + +#[test] +#[cfg(feature = "http2")] +fn sdv_client_request_http2_verify_true() { + struct Verifier; + impl CertVerifier for Verifier { + fn verify(&self, _certs: &ServerCerts) -> bool { + true + } + } + + define_service_handle!(HTTPS;); + + let rt = init_test_work_runtime(4); + + let key_path = std::path::PathBuf::from("tests/file/key.pem"); + let cert_path = std::path::PathBuf::from("tests/file/cert.pem"); + + let (tx, rx) = std::sync::mpsc::channel(); + let server_handle = rt.spawn(async move { + let handle = { + let mut port = 10000; + let listener = loop { + let addr = std::net::SocketAddr::from(([127, 0, 0, 1], port)); + match tokio::net::TcpListener::bind(addr).await { + Ok(listener) => break listener, + Err(_) => { + port += 1; + if port == u16::MAX { + port = 10000; + } + continue; + } + } + }; + let port = listener.local_addr().unwrap().port(); + + tokio::spawn(async move { + let mut acceptor = + openssl::ssl::SslAcceptor::mozilla_intermediate(openssl::ssl::SslMethod::tls()) + .expect("SslAcceptorBuilder error"); + acceptor + .set_session_id_context(b"test") + .expect("Set session id error"); + acceptor + .set_private_key_file(key_path, openssl::ssl::SslFiletype::PEM) + .expect("Set private key error"); + acceptor + .set_certificate_chain_file(cert_path) + .expect("Set cert error"); + acceptor.set_alpn_protos(b"\x02h2").unwrap(); + acceptor.set_alpn_select_callback(|_, client| { + openssl::ssl::select_next_proto(b"\x02h2", client) + .ok_or(openssl::ssl::AlpnError::NOACK) + }); + + let acceptor = acceptor.build(); + + let (stream, _) = listener.accept().await.expect("TCP listener accept error"); + let ssl = openssl::ssl::Ssl::new(acceptor.context()).expect("Ssl Error"); + let mut stream = + tokio_openssl::SslStream::new(ssl, stream).expect("SslStream Error"); + core::pin::Pin::new(&mut stream).accept().await.unwrap(); + + hyper::server::conn::Http::new() + .serve_connection(stream, hyper::service::service_fn(server_fn)) + .await + }); + + TlsHandle { port } + }; + tx.send(handle) + .expect("Failed to send the handle to the test thread."); + }); + rt.block_on(server_handle) + .expect("Runtime start server coroutine failed"); + let handle = rx + .recv() + .expect("Handle send channel (Server-Half) be closed unexpectedly"); + + let client = Client::builder() + .http2_prior_knowledge() + .cert_verifier(Verifier) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .url(format!("{}:{}", "127.0.0.1", handle.port).as_str()) + .version("HTTP/2.0") + .method("GET") + .body(Body::empty()) + .expect("Client build Request failed."); + + rt.block_on(async move { + let response = client.request(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + }); +} + +#[test] +#[cfg(feature = "http1_1")] +fn sdv_client_request_http1_verify_false() { + struct Verifier; + impl CertVerifier for Verifier { + fn verify(&self, _certs: &ServerCerts) -> bool { + false + } + } + + define_service_handle!(HTTPS;); + + let rt = init_test_work_runtime(4); + + let mut handles_vec = vec![]; + start_server!( + HTTPS; + ServerNum: 1, + Runtime: rt, + Handles: handles_vec, + ServeFnName: server_fn, + ); + let handle = handles_vec.pop().expect("No more handles !"); + + let client = Client::builder() + .cert_verifier(Verifier) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .url(format!("{}:{}", "127.0.0.1", handle.port).as_str()) + .version("HTTP/1.1") + .method("GET") + .body(Body::empty()) + .expect("Client build Request failed."); + + rt.block_on(async move { + let response = client.request(request).await; + assert!(response.is_err()); + }); +} + +#[test] +#[cfg(feature = "http1_1")] +fn sdv_client_request_http1_verify_true_invalid_cert() { + struct Verifier; + impl CertVerifier for Verifier { + fn verify(&self, _certs: &ServerCerts) -> bool { + true + } + } + + define_service_handle!(HTTPS;); + + let rt = init_test_work_runtime(4); + + let key_path = std::path::PathBuf::from("tests/file/invalid_key.pem"); + let cert_path = std::path::PathBuf::from("tests/file/invalid_cert.pem"); + + let (tx, rx) = std::sync::mpsc::channel(); + let server_handle = rt.spawn(async move { + let handle = start_http_server!( + HTTPS ; + server_fn , + key_path , + cert_path + ); + tx.send(handle) + .expect("Failed to send the handle to the test thread."); + }); + rt.block_on(server_handle) + .expect("Runtime start server coroutine failed"); + let handle = rx + .recv() + .expect("Handle send channel (Server-Half) be closed unexpectedly"); + + let client = Client::builder() + .cert_verifier(Verifier) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .url(format!("{}:{}", "127.0.0.1", handle.port).as_str()) + .version("HTTP/1.1") + .method("GET") + .body(Body::empty()) + .expect("Client build Request failed."); + + rt.block_on(async move { + let response = client.request(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::OK); + }); +} diff --git a/ylong_http_client/tests/sdv_async_dns_resolver.rs b/ylong_http_client/tests/sdv_async_dns_resolver.rs new file mode 100644 index 0000000..b0ee33a --- /dev/null +++ b/ylong_http_client/tests/sdv_async_dns_resolver.rs @@ -0,0 +1,592 @@ +// Copyright (c) 2025 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(all( + feature = "async", + feature = "tokio_base", + feature = "http1_1", + not(feature = "__tls") +))] + +mod common; + +use std::convert::Infallible; +use std::io; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; + +use ylong_http_client::async_impl::{Body, Client, Request, Resolver, SocketFuture}; + +use crate::common::init_test_work_runtime; + +async fn server_fn( + _req: hyper::Request, +) -> Result, Infallible> { + let response = hyper::Response::builder() + .status(hyper::StatusCode::OK) + .body(hyper::Body::empty()) + .expect("build hyper response failed"); + Ok(response) +} + +macro_rules! dns_test { + ( + Ipv4; + Success; + $resolver: ident + ) => { + define_service_handle!(HTTP;); + let rt = init_test_work_runtime(4); + let (tx, rx) = std::sync::mpsc::channel(); + + rt.block_on(async move { + let mut handle = start_http_server!(HTTP; server_fn); + handle + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + tx.send(handle) + .expect("send Handle out the server coroutine failed !"); + }); + + let mut handle = rx.recv().expect("recv Handle failed !"); + + let client = Client::builder() + .dns_resolver($resolver) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .url(format!("localhost:{}", handle.port).as_str()) + .body(Body::empty()) + .expect("Client build Request failed."); + + rt.block_on(async move { + let response = client.request(request).await; + assert!(response.is_ok()); + + handle + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }) + }; + ( + Ipv6; + Success; + $resolver: ident + ) => { + define_service_handle!(HTTP;); + let rt = init_test_work_runtime(4); + let (tx, rx) = std::sync::mpsc::channel(); + + rt.block_on(async move { + let mut handle = start_http_server!(HTTP; Ipv6; server_fn); + handle + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + tx.send(handle) + .expect("send Handle out the server coroutine failed !"); + }); + + let mut handle = rx.recv().expect("recv Handle failed !"); + + let client = Client::builder() + .dns_resolver($resolver) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .url(format!("localhost:{}", handle.port).as_str()) + .body(Body::empty()) + .expect("Client build Request failed."); + + rt.block_on(async move { + let response = client.request(request).await; + assert!(response.is_ok()); + + handle + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }) + }; + ( + Ipv4; + Fail; + $resolver: ident + ) => { + define_service_handle!(HTTP;); + let rt = init_test_work_runtime(4); + let (tx, rx) = std::sync::mpsc::channel(); + + rt.block_on(async move { + let mut handle = start_http_server!(HTTP; server_fn); + handle + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + tx.send(handle) + .expect("send Handle out the server coroutine failed !"); + }); + + let mut handle = rx.recv().expect("recv Handle failed !"); + + let client = Client::builder() + .dns_resolver($resolver) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .url(format!("localhost:{}", handle.port).as_str()) + .body(Body::empty()) + .expect("Client build Request failed."); + + rt.block_on(async move { + let response = client.request(request).await; + assert!(response.is_err()); + + handle + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }) + }; + ( + Ipv6; + Fail; + $resolver: ident + ) => { + define_service_handle!(HTTP;); + let rt = init_test_work_runtime(4); + let (tx, rx) = std::sync::mpsc::channel(); + + rt.block_on(async move { + let mut handle = start_http_server!(HTTP; Ipv6; server_fn); + handle + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + tx.send(handle) + .expect("send Handle out the server coroutine failed !"); + }); + + let mut handle = rx.recv().expect("recv Handle failed !"); + + let client = Client::builder() + .dns_resolver($resolver) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .url(format!("localhost:{}", handle.port).as_str()) + .body(Body::empty()) + .expect("Client build Request failed."); + + rt.block_on(async move { + let response = client.request(request).await; + assert!(response.is_err()); + + handle + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }) + }; +} + +#[test] +fn sdv_client_request_dns_resolver_ipv4() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.trim_start_matches("localhost:").to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let ip = Ipv4Addr::new(127, 0, 0, 1); + let addr = SocketAddr::from((ip, port.parse().unwrap())); + let addrs = vec![addr]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + dns_test!(Ipv4; Success; ExampleDnsResolver); +} + +#[test] +fn sdv_client_request_dns_resolver_ipv6() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.trim_start_matches("localhost:").to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let ip = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + let addr = SocketAddr::from((ip, port.parse().unwrap())); + let addrs = vec![addr]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + dns_test!(Ipv6; Success; ExampleDnsResolver); +} + +#[test] +fn sdv_client_request_dns_resolver_ipv4_invalid() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.trim_start_matches("localhost:").to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let ip = Ipv4Addr::new(127, 0, 0, 2); + let addr = SocketAddr::from((ip, port.parse().unwrap())); + let addrs = vec![addr]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + dns_test!(Ipv4; Fail; ExampleDnsResolver); +} + +#[test] +fn sdv_client_request_dns_resolver_ipv6_invalid() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.trim_start_matches("localhost:").to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let ip = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2); + let addr = SocketAddr::from((ip, port.parse().unwrap())); + let addrs = vec![addr]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + dns_test!(Ipv6; Fail; ExampleDnsResolver); +} + +#[test] +fn sdv_client_request_dns_resolver_ipv4_multiple1() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.trim_start_matches("localhost:").to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let ip1 = Ipv4Addr::new(127, 0, 0, 1); + let addr1 = SocketAddr::from((ip1, port.parse().unwrap())); + let ip2 = Ipv4Addr::new(127, 0, 0, 2); + let addr2 = SocketAddr::from((ip2, port.parse().unwrap())); + let addrs = vec![addr1, addr2]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + dns_test!(Ipv4; Success; ExampleDnsResolver); +} + +#[test] +fn sdv_client_request_dns_resolver_ipv6_multiple1() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.trim_start_matches("localhost:").to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let ip1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + let addr1 = SocketAddr::from((ip1, port.parse().unwrap())); + let ip2 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2); + let addr2 = SocketAddr::from((ip2, port.parse().unwrap())); + let addrs = vec![addr1, addr2]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + dns_test!(Ipv6; Success; ExampleDnsResolver); +} + +#[test] +fn sdv_client_request_dns_resolver_ipv4_multiple2() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let (port1, port2) = port.split_once(':').unwrap(); + let ip1 = Ipv4Addr::new(127, 0, 0, 1); + let addr1 = SocketAddr::from((ip1, port1.parse().unwrap())); + let ip2 = Ipv4Addr::new(127, 0, 0, 1); + let addr2 = SocketAddr::from((ip2, port2.parse().unwrap())); + let addrs = vec![addr1, addr2]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + define_service_handle!( HTTP ; ); + let rt = init_test_work_runtime(4); + let (mut handle1, mut handle2) = rt.block_on(async move { + let mut handle1 = start_http_server!( HTTP ; server_fn ); + let mut handle2 = start_http_server!( HTTP ; server_fn ); + handle1 + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + handle2 + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + (handle1, handle2) + }); + let client = Client::builder() + .dns_resolver(ExampleDnsResolver) + .build() + .expect("Build Client failed."); + let request = Request::builder() + .url(format!("{}:{}", handle1.port, handle2.port).as_str()) + .body(Body::empty()) + .expect("Client build Request failed."); + rt.block_on(async move { + let response = client.request(request).await; + assert!(response.is_ok()); + + handle1 + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle1 + .server_shutdown + .recv() + .await + .expect("server shutdown"); + handle2 + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle2 + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }) +} + +#[test] +fn sdv_client_request_dns_resolver_ipv6_multiple2() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let (port1, port2) = port.split_once(':').unwrap(); + let ip1 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + let addr1 = SocketAddr::from((ip1, port1.parse().unwrap())); + let ip2 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + let addr2 = SocketAddr::from((ip2, port2.parse().unwrap())); + let addrs = vec![addr1, addr2]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + define_service_handle!( HTTP ; ); + let rt = init_test_work_runtime(4); + let (mut handle1, mut handle2) = rt.block_on(async move { + let mut handle1 = start_http_server!( HTTP ; Ipv6; server_fn ); + let mut handle2 = start_http_server!( HTTP ; Ipv6; server_fn ); + handle1 + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + handle2 + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + (handle1, handle2) + }); + let client = Client::builder() + .dns_resolver(ExampleDnsResolver) + .build() + .expect("Build Client failed."); + let request = Request::builder() + .url(format!("{}:{}", handle1.port, handle2.port).as_str()) + .body(Body::empty()) + .expect("Client build Request failed."); + rt.block_on(async move { + let response = client.request(request).await; + assert!(response.is_ok()); + + handle1 + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle1 + .server_shutdown + .recv() + .await + .expect("server shutdown"); + handle2 + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle2 + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }) +} + +#[test] +fn sdv_client_request_dns_resolver_ipv4_ipv6() { + struct ExampleDnsResolver; + impl Resolver for ExampleDnsResolver { + fn resolve(&self, authority: &str) -> SocketFuture { + let port = authority.to_string(); + Box::pin(async move { + if port.is_empty() { + Err(io::Error::new(io::ErrorKind::Other, "").into()) + } else { + let (port1, port2) = port.split_once(':').unwrap(); + let ip1 = Ipv4Addr::new(0, 0, 0, 1); + let addr1 = SocketAddr::from((ip1, port1.parse().unwrap())); + let ip2 = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + let addr2 = SocketAddr::from((ip2, port2.parse().unwrap())); + let addrs = vec![addr1, addr2]; + Ok(Box::new(addrs.into_iter()) + as Box + Sync + Send>) + } + }) + } + } + define_service_handle!( HTTP ; ); + let rt = init_test_work_runtime(4); + let (mut handle1, mut handle2) = rt.block_on(async move { + let mut handle1 = start_http_server!( HTTP ; server_fn ); + let mut handle2 = start_http_server!( HTTP ; Ipv6; server_fn ); + handle1 + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + handle2 + .server_start + .recv() + .await + .expect("recv server start msg failed !"); + (handle1, handle2) + }); + let client = Client::builder() + .dns_resolver(ExampleDnsResolver) + .build() + .expect("Build Client failed."); + let request = Request::builder() + .url(format!("{}:{}", handle1.port, handle2.port).as_str()) + .body(Body::empty()) + .expect("Client build Request failed."); + rt.block_on(async move { + let response = client.request(request).await; + assert!(response.is_ok()); + + handle1 + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle1 + .server_shutdown + .recv() + .await + .expect("server shutdown"); + handle2 + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle2 + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }) +} -- Gitee