diff --git a/ylong_http/src/h2/mod.rs b/ylong_http/src/h2/mod.rs index f9c235d48e63a9c85ca58a5095ff2fdcff393017..b583ba64f4720cda4d328cb0ff86ab469d2fa169 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 d39e7b81b2574c8e444e058be3a798bc69af6d93..8b5db50b49f4a9da310e7c60c68236c7cd349a62 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 c8f4552ea0493c0886ed519736a84fc895b68a24..7fc818957ff82710f612e6a83c38aaea90af426e 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/src/async_impl/client.rs b/ylong_http_client/src/async_impl/client.rs index a451c752a4dd9d1c03a29fadd43cc1f95ff005f3..93dbec4a546a7531a67e10f50665e7862e893108 100644 --- a/ylong_http_client/src/async_impl/client.rs +++ b/ylong_http_client/src/async_impl/client.rs @@ -340,6 +340,22 @@ impl ClientBuilder { self } + /// Sets whether to cache the last alpn result during negotiation, default + /// to false. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::async_impl::ClientBuilder; + /// + /// let builder = ClientBuilder::new().alpn_cache(false); + /// ``` + #[cfg(all(feature = "http2", feature = "http1_1"))] + pub fn alpn_cache(mut self, use_alpn_cache: bool) -> Self { + self.http.use_alpn_cache = use_alpn_cache; + self + } + /// Enables a request timeout. /// /// The timeout is applied from when the request starts connection util the diff --git a/ylong_http_client/src/async_impl/pool.rs b/ylong_http_client/src/async_impl/pool.rs index 1106c8987b05190f36a5bbc6e8c3ad5cfa20a1b4..b36b3d5dde6a4dccdf79ec26bf5d7b2f0647eb55 100644 --- a/ylong_http_client/src/async_impl/pool.rs +++ b/ylong_http_client/src/async_impl/pool.rs @@ -98,6 +98,15 @@ pub(crate) enum H1ConnOption { None(WrappedSemPermit), } +#[derive(Clone)] +#[cfg(all(feature = "http2", feature = "http1_1"))] +enum AlpnResult { + /// Does not use cache. + None, + Http1, + Http2, +} + pub(crate) struct Conns { usable: WrappedSemaphore, list: Arc>>>, @@ -105,6 +114,8 @@ pub(crate) struct Conns { h2_conn: Arc>>>, #[cfg(feature = "http3")] h3_conn: Arc>>>, + #[cfg(all(feature = "http2", feature = "http1_1"))] + alpn_cache: Arc>, } impl Conns { @@ -119,6 +130,9 @@ impl Conns { #[cfg(feature = "http3")] h3_conn: Arc::new(crate::runtime::AsyncMutex::new(Vec::with_capacity(1))), + + #[cfg(all(feature = "http2", feature = "http1_1"))] + alpn_cache: Arc::new(Mutex::new(AlpnResult::None)), } } @@ -136,6 +150,9 @@ impl Clone for Conns { #[cfg(feature = "http3")] h3_conn: self.h3_conn.clone(), + + #[cfg(all(feature = "http2", feature = "http1_1"))] + alpn_cache: self.alpn_cache.clone(), } } } @@ -172,8 +189,7 @@ impl Conns conn.time_group_mut().set_connect_end(Instant::now()); return Ok(conn); } - self.conn_negotiate(connector, url, config.http2_config) - .await + self.conn_negotiate(connector, url, config).await } }?; conn.time_group_mut().set_connect_start(conn_start); @@ -275,7 +291,7 @@ impl Conns &self, connector: Arc, url: &Uri, - h2_config: H2Config, + config: HttpConfig, ) -> Result, HttpClientError> where C: Connector, @@ -293,32 +309,63 @@ impl Conns } H1ConnOption::None(permit) => permit, }; + if config.use_alpn_cache { + let alpn_cache = self.alpn_cache.lock().unwrap().clone(); + match alpn_cache { + AlpnResult::Http1 => { + drop(lock); + let stream = connector.connect(url, HttpVersion::Negotiate).await?; + let mut data = stream.conn_data(); + let time_group = take(data.time_group_mut()); + let dispatcher = ConnDispatcher::http1(stream); + return Ok(TimeInfoConn::new( + self.dispatch_h1_conn(dispatcher, permit), + time_group, + )); + } + AlpnResult::Http2 => { + drop(permit); + let stream = connector.connect(url, HttpVersion::Negotiate).await?; + let mut data = stream.conn_data(); + let time_group = take(data.time_group_mut()); + let conn = Self::dispatch_h2_conn( + data.detail(), + config.http2_config, + stream, + &mut lock, + ); + return Ok(TimeInfoConn::new(conn, time_group)); + } + _ => {} + } + }; let stream = connector.connect(url, HttpVersion::Negotiate).await?; let mut data = stream.conn_data(); let time_group = take(data.time_group_mut()); - let protocol = if let Some(bytes) = data.negotiate().alpn() { - bytes - } else { - let dispatcher = ConnDispatcher::http1(stream); - return Ok(TimeInfoConn::new( - self.dispatch_h1_conn(dispatcher, permit), - time_group, - )); - }; - + let protocol = data.negotiate().alpn().unwrap_or(b"http/1.1"); if protocol == b"http/1.1" { + if config.use_alpn_cache { + *self.alpn_cache.lock().unwrap() = AlpnResult::Http1; + } let dispatcher = ConnDispatcher::http1(stream); Ok(TimeInfoConn::new( self.dispatch_h1_conn(dispatcher, permit), time_group, )) } else if protocol == b"h2" { - std::mem::drop(permit); - let conn = Self::dispatch_h2_conn(data.detail(), h2_config, stream, &mut lock); + drop(permit); + if config.use_alpn_cache { + *self.alpn_cache.lock().unwrap() = AlpnResult::Http2; + } + let conn = Self::dispatch_h2_conn( + data.detail(), + config.http2_config, + stream, + &mut lock, + ); Ok(TimeInfoConn::new(conn, time_group)) } else { - std::mem::drop(permit); err_from_msg!(Connect, "Alpn negotiate a wrong protocol version.") } } diff --git a/ylong_http_client/src/util/c_openssl/ffi/stack.rs b/ylong_http_client/src/util/c_openssl/ffi/stack.rs index c5f9eb7c2d4e15ccf849187ff220f98464eeeb6e..35dfdad0a818c8091722d9179d30e32b8007d252 100644 --- a/ylong_http_client/src/util/c_openssl/ffi/stack.rs +++ b/ylong_http_client/src/util/c_openssl/ffi/stack.rs @@ -57,13 +57,14 @@ pub(crate) unsafe fn unified_sk_free(st: STACK) { } } -/// Retrieves a pointer to a stack element from a stack allocated by OpenSSL or BoringSSL at the specified index. +/// Retrieves a pointer to a stack element from a stack allocated by OpenSSL or +/// BoringSSL at the specified index. /// /// # Safety /// - `st` must be a valid pointer to a stack allocated by the same library /// (OpenSSL or BoringSSL) used in this crate. -/// - `idx` must be a valid index within the bounds of the stack. -/// if the index is out of range, the function will return `null`. +/// - `idx` must be a valid index within the bounds of the stack. if the index +/// is out of range, the function will return `null`. pub(crate) unsafe fn unified_sk_value(st: STACK, idx: c_int) -> *mut c_void { #[cfg(feature = "c_boringssl")] { @@ -99,8 +100,9 @@ pub(crate) unsafe fn unified_sk_num(st: STACK) -> c_int { /// # Safety /// - `st` must be a valid pointer to a stack allocated by the same library /// (OpenSSL or BoringSSL) used in this crate. -/// - The caller must check the return value of this function. If the stack is empty, -/// the function will return `null`. The caller must handle this case appropriately. +/// - The caller must check the return value of this function. If the stack is +/// empty, the function will return `null`. The caller must handle this case +/// appropriately. pub(crate) unsafe fn unified_sk_pop(st: STACK) -> *mut c_void { #[cfg(feature = "c_boringssl")] { diff --git a/ylong_http_client/src/util/c_openssl/ssl/stream.rs b/ylong_http_client/src/util/c_openssl/ssl/stream.rs index 14ed1b40b04a8e5b088efdcf9e986795f3b51e19..01a7dc7b970f4debfb1dbfe7ece8f32306f7a895 100644 --- a/ylong_http_client/src/util/c_openssl/ssl/stream.rs +++ b/ylong_http_client/src/util/c_openssl/ssl/stream.rs @@ -26,7 +26,6 @@ use crate::c_openssl::error::ErrorStack; use crate::c_openssl::ffi::ssl::{SSL_connect, SSL_set_bio, SSL_shutdown}; use crate::c_openssl::foreign::Foreign; use crate::c_openssl::verify::PinsVerifyInfo; - use crate::util::base64::encode; use crate::util::c_openssl::bio::BioMethod; use crate::util::c_openssl::error::VerifyError; @@ -256,8 +255,9 @@ pub(crate) enum ShutdownResult { } pub(crate) fn verify_server_root_cert(ssl: *const SSL, pinned_key: &str) -> Result<(), SslError> { - use crate::c_openssl::ffi::{ssl::SSL_get_peer_cert_chain, ssl::X509_chain_up_ref}; - use crate::c_openssl::{stack::Stack, x509::X509}; + use crate::c_openssl::ffi::ssl::{SSL_get_peer_cert_chain, X509_chain_up_ref}; + use crate::c_openssl::stack::Stack; + use crate::c_openssl::x509::X509; let cert_chain = unsafe { X509_chain_up_ref(SSL_get_peer_cert_chain(ssl)) }; if cert_chain.is_null() { diff --git a/ylong_http_client/src/util/c_openssl/stack.rs b/ylong_http_client/src/util/c_openssl/stack.rs index 5640b4bf40e87c5dce3555bd47eb3353ca576221..421c0ca3f0ea3353d92e5182b1a1dc96b1e28cbb 100644 --- a/ylong_http_client/src/util/c_openssl/stack.rs +++ b/ylong_http_client/src/util/c_openssl/stack.rs @@ -19,7 +19,6 @@ use core::ops::{Deref, DerefMut, Range}; use libc::c_int; use super::ffi::stack::{unified_sk_free, unified_sk_num, unified_sk_pop, unified_sk_value, STACK}; - use crate::c_openssl::foreign::{Foreign, ForeignRef, ForeignRefWrapper}; pub(crate) trait Stackof: Foreign { diff --git a/ylong_http_client/src/util/c_openssl/verify/pinning.rs b/ylong_http_client/src/util/c_openssl/verify/pinning.rs index d14bc32ae4cfe7297fe94241c9a0c12cb452a055..2b4984ab271321cb240719ec497214df1ab71c22 100644 --- a/ylong_http_client/src/util/c_openssl/verify/pinning.rs +++ b/ylong_http_client/src/util/c_openssl/verify/pinning.rs @@ -134,9 +134,9 @@ impl PubKeyPinsBuilder { /// Sets a tuple of (server, public key digest) for `PubKeyPins`, using /// the root certificate pinning strategy. ///
- /// Ensure that the server returns the complete certificate chain, including the root certificate; - /// otherwise, the client's public key pinning validation will fail and return an error. - ///
+ /// Ensure that the server returns the complete certificate chain, including + /// the root certificate; otherwise, the client's public key pinning + /// validation will fail and return an error. /// /// # Examples /// diff --git a/ylong_http_client/src/util/config/http.rs b/ylong_http_client/src/util/config/http.rs index abc5f268b5c965fb08943332f8349cceb19be332..cd015c841dda1015a59a351c5ea36522b5cacbbb 100644 --- a/ylong_http_client/src/util/config/http.rs +++ b/ylong_http_client/src/util/config/http.rs @@ -28,6 +28,9 @@ pub(crate) struct HttpConfig { #[cfg(feature = "http3")] pub(crate) http3_config: http3::H3Config, + + #[cfg(all(feature = "http2", feature = "http1_1"))] + pub(crate) use_alpn_cache: bool, } impl HttpConfig { @@ -44,6 +47,9 @@ impl HttpConfig { #[cfg(feature = "http3")] http3_config: http3::H3Config::new(), + + #[cfg(all(feature = "http2", feature = "http1_1"))] + use_alpn_cache: false, } } } diff --git a/ylong_http_client/src/util/h2/streams.rs b/ylong_http_client/src/util/h2/streams.rs index b1a395e9dc5d70b381d1a15027ca76570f27305a..cb4270a6b9bb2901f5dcd3c615d094709d04fb85 100644 --- a/ylong_http_client/src/util/h2/streams.rs +++ b/ylong_http_client/src/util/h2/streams.rs @@ -885,7 +885,8 @@ mod ut_streams { /// # Brief /// 1. Insert streams with different states and sends go_away with a stream /// id. - /// 2. Asserts that only streams with IDs greater than to the go_away ID are closed. + /// 2. Asserts that only streams with IDs greater than to the go_away ID are + /// closed. #[test] fn ut_streams_get_unset_streams() { let mut streams = Streams::new(100, 100, FlowControl::new(100, 100)); diff --git a/ylong_http_client/tests/sdv_async_https_pinning.rs b/ylong_http_client/tests/sdv_async_https_pinning.rs index 963d258dd50ed6c4c84bc67c1e0880aa82e0b3af..c4bf5c4f8bb58dc4e271adc932a0f031b4d30d3c 100644 --- a/ylong_http_client/tests/sdv_async_https_pinning.rs +++ b/ylong_http_client/tests/sdv_async_https_pinning.rs @@ -397,7 +397,8 @@ fn sdv_client_public_key_root_pinning() { .expect("Runtime block on server shutdown failed"); } - // Root certificate pinning strategy, but using the server certificate public key hash + // Root certificate pinning strategy, but using the server certificate public + // key hash { start_server!( HTTPS;