diff --git a/ylong_http/src/h2/error.rs b/ylong_http/src/h2/error.rs index d73555206ef8f0bec8245e46ab12decfd66f7b2a..67d3d45d5bf86ee928ced1e24a623b0ffd3f9147 100644 --- a/ylong_http/src/h2/error.rs +++ b/ylong_http/src/h2/error.rs @@ -26,9 +26,8 @@ use std::convert::{Infallible, TryFrom}; -use crate::error::{ErrorKind, HttpError}; - use super::frame::StreamId; +use crate::error::{ErrorKind, HttpError}; /// The http2 error handle implementation #[derive(Debug, Eq, PartialEq, Copy, Clone)] diff --git a/ylong_http/src/h2/mod.rs b/ylong_http/src/h2/mod.rs index 584d72187b0ffaadf683a5f3371fd8264740fd9c..63eb202d10827512604a3b8db3f644b798afaf26 100644 --- a/ylong_http/src/h2/mod.rs +++ b/ylong_http/src/h2/mod.rs @@ -63,7 +63,7 @@ pub use encoder::FrameEncoder; pub use error::{ErrorCode, H2Error}; pub use frame::{ Data, Frame, FrameFlags, Goaway, Headers, Payload, Ping, RstStream, Setting, Settings, - SettingsBuilder, WindowUpdate, StreamId + SettingsBuilder, StreamId, WindowUpdate, }; pub(crate) use hpack::{HpackDecoder, HpackEncoder}; pub use parts::Parts; diff --git a/ylong_http_client/src/async_impl/client.rs b/ylong_http_client/src/async_impl/client.rs index 19d6e1401adaf888573001fe63c1af88d7954674..b941f6a34989ec7bec3c565472d12b2c6be75a12 100644 --- a/ylong_http_client/src/async_impl/client.rs +++ b/ylong_http_client/src/async_impl/client.rs @@ -667,6 +667,23 @@ impl ClientBuilder { self.http.http3_config.set_qpack_blocked_streams(size); self } + + /// Enables 0rtt in http3 + /// + /// Defaults to `false` + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::async_impl::ClientBuilder; + /// + /// let builder = ClientBuilder::new().enable_0rtt(); + /// ``` + #[cfg(feature = "http3")] + pub fn enable_0rtt(mut self) -> Self { + self.tls = self.tls.enable_0rtt(); + self + } } #[cfg(feature = "__tls")] diff --git a/ylong_http_client/src/async_impl/connector/mod.rs b/ylong_http_client/src/async_impl/connector/mod.rs index cf88b993a30a7c4f4e4b6a98dfdb468e75fe34db..9195c831aa6c838aeed22d1d40937cbfdf9bd56b 100644 --- a/ylong_http_client/src/async_impl/connector/mod.rs +++ b/ylong_http_client/src/async_impl/connector/mod.rs @@ -16,6 +16,8 @@ mod stream; use core::future::Future; +#[cfg(feature = "http3")] +use std::sync::{Arc, Mutex}; /// Information of an IO. pub use stream::ConnInfo; @@ -41,7 +43,12 @@ pub trait Connector { + 'static; /// Attempts to establish a connection. - fn connect(&self, uri: &Uri, http_version: HttpVersion) -> Self::Future; + fn connect( + &self, + uri: &Uri, + http_version: HttpVersion, + #[cfg(feature = "http3")] session: Option>>>>>, + ) -> Self::Future; } /// Connector for creating HTTP or HTTPS connections asynchronously. @@ -152,6 +159,8 @@ mod tls { use std::error; use std::fmt::{Debug, Display, Formatter}; use std::io::{Error, ErrorKind, Write}; + #[cfg(feature = "http3")] + use std::sync::{Arc, Mutex}; use ylong_http::request::uri::{Scheme, Uri}; @@ -173,7 +182,12 @@ mod tls { type Future = Pin> + Sync + Send>>; - fn connect(&self, uri: &Uri, _http_version: HttpVersion) -> Self::Future { + fn connect( + &self, + uri: &Uri, + _http_version: HttpVersion, + #[cfg(feature = "http3")] session: Option>>>>>, + ) -> Self::Future { // Make sure all parts of uri is accurate. let mut addr = uri.authority().unwrap().to_string(); let mut auth = None; @@ -253,8 +267,13 @@ mod tls { }; let mut stream = HttpStream::new(MixStream::Udp(udp_socket), detail); - let Ok(quic_conn) = - QuicConn::connect(&mut stream, &config, &host).await + let Ok(quic_conn) = QuicConn::connect( + &mut stream, + &config, + &host, + session.clone().unwrap(), + ) + .await else { continue; }; diff --git a/ylong_http_client/src/async_impl/pool.rs b/ylong_http_client/src/async_impl/pool.rs index 2ed4317ce88e7574ad65c969d30bd3951ba7a512..09310139555a0c9c62e88b0a105dc6aac35fdc41 100644 --- a/ylong_http_client/src/async_impl/pool.rs +++ b/ylong_http_client/src/async_impl/pool.rs @@ -92,6 +92,8 @@ pub(crate) struct Conns { h2_conn: Arc>>>, #[cfg(feature = "http3")] h3_conn: Arc>>>, + #[cfg(feature = "http3")] + session: Arc>>>>, } impl Conns { @@ -104,10 +106,11 @@ impl Conns { #[cfg(feature = "http3")] h3_conn: Arc::new(crate::runtime::AsyncMutex::new(Vec::with_capacity(1))), + + #[cfg(feature = "http3")] + session: Arc::new(Mutex::new(Box::new(None))), } } - - // fn get_alt_svcs } impl Clone for Conns { @@ -120,6 +123,9 @@ impl Clone for Conns { #[cfg(feature = "http3")] h3_conn: self.h3_conn.clone(), + + #[cfg(feature = "http3")] + session: self.session.clone(), } } } @@ -169,7 +175,16 @@ impl Conns if let Some(conn) = self.exist_h1_conn() { return Ok(conn); } - let dispatcher = ConnDispatcher::http1(connector.connect(url, HttpVersion::Http1).await?); + let dispatcher = ConnDispatcher::http1( + connector + .connect( + url, + HttpVersion::Http1, + #[cfg(feature = "http3")] + None, + ) + .await?, + ); Ok(self.dispatch_h1_conn(dispatcher)) } @@ -191,7 +206,14 @@ impl Conns if let Some(conn) = Self::exist_h2_conn(&mut lock) { return Ok(conn); } - let stream = connector.connect(url, HttpVersion::Http2).await?; + let stream = connector + .connect( + url, + HttpVersion::Http2, + #[cfg(feature = "http3")] + None, + ) + .await?; let details = stream.conn_detail(); let tls = if let Some(scheme) = url.scheme() { *scheme == Scheme::HTTPS @@ -224,7 +246,9 @@ impl Conns if let Some(conn) = Self::exist_h3_conn(&mut lock) { return Ok(conn); } - let mut stream = connector.connect(url, HttpVersion::Http3).await?; + let mut stream = connector + .connect(url, HttpVersion::Http3, Some(self.session.clone())) + .await?; let quic_conn = stream.quic_conn().ok_or(HttpClientError::from_str( crate::ErrorKind::Connect, "QUIC connect failed", @@ -254,7 +278,14 @@ impl Conns return Ok(conn); } - let stream = connector.connect(url, HttpVersion::Negotiate).await?; + let stream = connector + .connect( + url, + HttpVersion::Negotiate, + #[cfg(feature = "http3")] + Some(self.session.clone()), + ) + .await?; let details = stream.conn_detail(); let protocol = if let Some(bytes) = details.alpn() { @@ -310,7 +341,10 @@ impl Conns let path = url.path().cloned(); let query = url.query().cloned(); let alt_url = Uri::from_raw_parts(Some(scheme), Some(authority), path, query); - let mut stream = connector.connect(&alt_url, HttpVersion::Http3).await.ok()?; + let mut stream = connector + .connect(&alt_url, HttpVersion::Http3, Some(self.session.clone())) + .await + .ok()?; let quic_conn = stream.quic_conn().unwrap(); return Some(Self::dispatch_h3_conn( h3_config.clone(), diff --git a/ylong_http_client/src/async_impl/quic/mod.rs b/ylong_http_client/src/async_impl/quic/mod.rs index 223a14c2a4147d6699a223594035f2bedd02a9d8..1a1c9bb6f4793ce1372bfdd194330c2abfb4a521 100644 --- a/ylong_http_client/src/async_impl/quic/mod.rs +++ b/ylong_http_client/src/async_impl/quic/mod.rs @@ -17,6 +17,7 @@ use std::ffi::c_void; use std::net::SocketAddr; use std::ops::{Deref, DerefMut}; use std::ptr; +use std::sync::{Arc, Mutex}; use libc::{ in6_addr, in_addr, sa_family_t, size_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, @@ -28,7 +29,8 @@ use ylong_runtime::time::timeout; use crate::async_impl::connector::ConnInfo; use crate::c_openssl::ssl::verify_server_cert; use crate::runtime::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; -use crate::util::c_openssl::ssl::Ssl; +use crate::util::c_openssl::ffi::ssl::SSL_set_ex_data; +use crate::util::c_openssl::ssl::{get_ex_data_index, Ssl}; use crate::{ErrorKind, HttpClientError, TlsConfig}; const MAX_DATAGRAM_SIZE: usize = 1350; @@ -36,14 +38,14 @@ const UDP_BUF_SIZE: usize = 65535; const MAX_STREAM_DATA: u64 = 1_000_000; const MAX_TOTAL_DATA: u64 = 10_000_000; const MAX_STREAM_NUM: u64 = 100; -const MAX_IDLE_TIME: u64 = 5000; +const MAX_IDLE_TIME: u64 = 120 * 1000; pub struct QuicConn { inner: quiche::Connection, } impl QuicConn { - fn quic_config() -> Result { + fn quic_config(enable_0rtt: bool) -> Result { let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; config.verify_peer(true); config.set_application_protos(quiche::h3::APPLICATION_PROTOCOL)?; @@ -57,6 +59,9 @@ impl QuicConn { config.set_initial_max_streams_bidi(MAX_STREAM_NUM); config.set_initial_max_streams_uni(MAX_STREAM_NUM); config.set_disable_active_migration(true); + if enable_0rtt { + config.enable_early_data(); + } Ok(config) } @@ -64,11 +69,13 @@ impl QuicConn { stream: &mut S, tls_config: &TlsConfig, host: &str, + session: Arc>>>>, ) -> Result where S: AsyncRead + AsyncWrite + ConnInfo + Unpin + Sync + Send + 'static, { - let config = Self::quic_config() + let enable_0rtt = tls_config.is_0rtt_enabled(); + let config = Self::quic_config(enable_0rtt) .map_err(|_| HttpClientError::from_str(ErrorKind::Connect, "Quic init error"))?; // Generate a random source connection ID for the connection. let mut scid = [0; quiche::MAX_CONN_ID_LEN]; @@ -103,11 +110,27 @@ impl QuicConn { let mut conn = QuicConn { inner: unsafe { *Box::from_raw(conn) }, }; + if enable_0rtt { + if let Some(session) = session.clone().lock().unwrap().as_ref() { + let _ = conn.set_session(session.as_slice()); + std::mem::forget(new_ssl); + return Ok(conn); + } + + unsafe { + SSL_set_ex_data( + new_ssl.get_raw_ptr(), + get_ex_data_index(), + &mut **session.clone().lock().unwrap() as *mut _ as *mut c_void, + ) + }; + } if let Err(e) = conn.connect_inner(stream, &mut new_ssl, tls_config).await { std::mem::forget(new_ssl); return Err(e); } std::mem::forget(new_ssl); + if conn.is_established() { Ok(conn) } else { @@ -118,6 +141,7 @@ impl QuicConn { } } + // todo: remove all the io op in connection? async fn connect_inner( &mut self, stream: &mut S, diff --git a/ylong_http_client/src/util/c_openssl/adapter.rs b/ylong_http_client/src/util/c_openssl/adapter.rs index d2d5820a481c23c28f1685f2f25a0bc14ed9f824..64401e743ee6265127a8ba0692e9662d77db5348 100644 --- a/ylong_http_client/src/util/c_openssl/adapter.rs +++ b/ylong_http_client/src/util/c_openssl/adapter.rs @@ -47,6 +47,8 @@ pub struct TlsConfigBuilder { certs_list: Vec, pins: Option, paths_list: Vec, + #[cfg(feature = "http3")] + enable_0rtt: bool, } impl TlsConfigBuilder { @@ -68,6 +70,8 @@ impl TlsConfigBuilder { certs_list: vec![], pins: None, paths_list: vec![], + #[cfg(feature = "http3")] + enable_0rtt: false, } } @@ -347,6 +351,28 @@ impl TlsConfigBuilder { self } + /// Enables 0rtt in http3 + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::TlsConfigBuilder; + /// + /// let builder = TlsConfigBuilder::new().enable_0rtt(); + /// ``` + #[cfg(feature = "http3")] + pub fn enable_0rtt(mut self) -> Self { + self.enable_0rtt = true; + self.inner = self.inner.and_then(|mut builder| { + { + builder.enable_0rtt(); + Ok(()) + } + .map(|_| builder) + }); + self + } + /// Builds a `TlsContext`. Returns `Err` if an error occurred during /// configuration. /// @@ -390,6 +416,8 @@ impl TlsConfigBuilder { use_sni: self.use_sni, verify_hostname: self.verify_hostname, pins: self.pins, + #[cfg(feature = "http3")] + enable_0rtt: self.enable_0rtt, }) } } @@ -418,6 +446,8 @@ pub struct TlsConfig { use_sni: bool, verify_hostname: bool, pins: Option, + #[cfg(feature = "http3")] + enable_0rtt: bool, } impl TlsConfig { @@ -457,6 +487,11 @@ impl TlsConfig { Some(pins) => pins.get_pin(domain), } } + + #[cfg(feature = "http3")] + pub(crate) fn is_0rtt_enabled(&self) -> bool { + self.enable_0rtt + } } impl Default for TlsConfig { diff --git a/ylong_http_client/src/util/c_openssl/ffi/mod.rs b/ylong_http_client/src/util/c_openssl/ffi/mod.rs index d7373e37db57825819dfcb7ed6ab27e5526f60c4..3700db45efd70041734b48a68ed61055bd7a51f2 100644 --- a/ylong_http_client/src/util/c_openssl/ffi/mod.rs +++ b/ylong_http_client/src/util/c_openssl/ffi/mod.rs @@ -18,6 +18,8 @@ pub(crate) mod bio; pub(crate) mod callback; pub(crate) mod err; pub(crate) mod pem; +#[cfg(feature = "http3")] +pub(crate) mod session; pub mod ssl; // todo pub(crate) mod stack; pub(crate) mod x509; diff --git a/ylong_http_client/src/util/c_openssl/ffi/session.rs b/ylong_http_client/src/util/c_openssl/ffi/session.rs new file mode 100644 index 0000000000000000000000000000000000000000..b3fd5f9764392963f707308fa5372e9a1dd0c4ee --- /dev/null +++ b/ylong_http_client/src/util/c_openssl/ffi/session.rs @@ -0,0 +1,31 @@ +// Copyright (c) 2024 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. + +use libc::{c_int, c_uchar}; + +use crate::util::c_openssl::ffi::ssl::{SSL, SSL_CTX}; + +pub(crate) enum SSL_SESSION {} + +extern "C" { + pub(crate) fn SSL_CTX_sess_set_new_cb( + ctx: *mut SSL_CTX, + cb: Option c_int>, + ); + + pub(crate) fn SSL_CTX_set_early_data_enabled(ctx: *mut SSL_CTX, enabled: i32); + + pub(crate) fn SSL_CTX_set_session_cache_mode(ctx: *mut SSL_CTX, mode: i32); + + pub(crate) fn i2d_SSL_SESSION(in_: *mut SSL_SESSION, pp: *mut *mut c_uchar) -> c_int; +} diff --git a/ylong_http_client/src/util/c_openssl/ffi/ssl.rs b/ylong_http_client/src/util/c_openssl/ffi/ssl.rs index 7f516a091af944a0808e828bf1851f78cbff3558..0d1396c309db5d06c817b742128a719c8df2a5ca 100644 --- a/ylong_http_client/src/util/c_openssl/ffi/ssl.rs +++ b/ylong_http_client/src/util/c_openssl/ffi/ssl.rs @@ -198,6 +198,28 @@ extern "C" { #[cfg(feature = "c_boringssl")] pub(crate) fn SSL_set_tlsext_host_name(ssl: *mut SSL, name: *mut c_void) -> c_int; + + #[cfg(feature = "c_boringssl")] + pub(crate) fn SSL_set_ex_data(ssl: *mut SSL, idx: c_int, ptr: *mut c_void) -> c_int; + + #[cfg(feature = "c_boringssl")] + pub(crate) fn SSL_get_ex_data(ssl: *const SSL, idx: c_int) -> *mut c_void; + + #[cfg(feature = "c_boringssl")] + pub(crate) fn SSL_get_ex_new_index( + argl: c_long, + argp: *const c_void, + unused: *const c_void, + dup_unused: *const c_void, + free_func: *const c_void, + ) -> c_int; + + #[cfg(feature = "c_boringssl")] + pub(crate) fn SSL_get_peer_quic_transport_params( + ssl: *const SSL, + out_params: *mut *const u8, + out_params_len: *mut usize, + ); } /// This is a dispatch structure describing the internal ssl library diff --git a/ylong_http_client/src/util/c_openssl/ssl/ctx.rs b/ylong_http_client/src/util/c_openssl/ssl/ctx.rs index c23bd612bd1d37068f3632d113f8240364b02c85..b6c9a09d5e05069ddc53c4683a47ac484df9e95e 100644 --- a/ylong_http_client/src/util/c_openssl/ssl/ctx.rs +++ b/ylong_http_client/src/util/c_openssl/ssl/ctx.rs @@ -25,6 +25,11 @@ use crate::c_openssl::ffi::ssl::{ }; use crate::c_openssl::x509::{X509Store, X509StoreRef}; use crate::util::c_openssl::error::ErrorStack; +#[cfg(feature = "c_boringssl")] +use crate::util::c_openssl::ffi::session::{ + i2d_SSL_SESSION, SSL_CTX_sess_set_new_cb, SSL_CTX_set_early_data_enabled, + SSL_CTX_set_session_cache_mode, SSL_SESSION, +}; #[cfg(feature = "__c_openssl")] use crate::util::c_openssl::ffi::ssl::SSL_CTX_ctrl; use crate::util::c_openssl::ffi::ssl::{ @@ -35,6 +40,8 @@ use crate::util::c_openssl::ffi::ssl::{ #[cfg(feature = "c_boringssl")] use crate::util::c_openssl::ffi::ssl::{ SSL_CTX_set1_sigalgs_list, SSL_CTX_set_max_proto_version, SSL_CTX_set_min_proto_version, + SSL_get_ex_data, SSL_get_ex_new_index, SSL_get_peer_quic_transport_params, SSL_set_ex_data, + SSL, }; use crate::util::c_openssl::foreign::{Foreign, ForeignRef}; use crate::util::c_openssl::{cert_verify, check_ptr, check_ret, ssl_init}; @@ -324,4 +331,106 @@ impl SslContextBuilder { } as c_int) .map(|_| ()); } + + #[cfg(feature = "http3")] + pub(crate) fn enable_0rtt(&mut self) { + unsafe { + SSL_CTX_set_session_cache_mode( + self.as_ptr_mut(), + 0x1, // SSL_SESS_CACHE_CLIENT + ); + + SSL_CTX_sess_set_new_cb(self.as_ptr_mut(), Some(session_get_callback)); + + SSL_CTX_set_early_data_enabled(self.as_ptr_mut(), 1); + }; + } +} + +#[cfg(feature = "http3")] +pub(crate) fn get_ex_data_index() -> c_int { + static mut EX_DATA_INDEX: c_int = 0; + static EX_DATA_INDEX_INIT: std::sync::Once = std::sync::Once::new(); + + unsafe { + EX_DATA_INDEX_INIT.call_once(|| { + EX_DATA_INDEX = + SSL_get_ex_new_index(0, ptr::null(), ptr::null(), ptr::null(), ptr::null()); + }); + EX_DATA_INDEX + } +} + +#[cfg(feature = "http3")] +extern "C" fn session_get_callback(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int { + use std::io::Write; + + let data = match unsafe { + let data = SSL_get_ex_data(ssl, get_ex_data_index()) as *mut Option>; + data.as_mut() + } { + Some(v) => v, + None => return 0, + }; + + let mut ptr: *const u8 = ptr::null(); + let mut len: usize = 0; + + unsafe { + SSL_get_peer_quic_transport_params(ssl, &mut ptr, &mut len); + } + + if len == 0 { + return 0; + } + + let Some(session_bytes) = get_session_bytes(session) else { + return 0; + }; + + let peer_params: &[u8] = unsafe { std::slice::from_raw_parts(ptr, len) }; + + let session_bytes_len = session_bytes.len() as u64; + + // 8 bytes to store a u64 num + let mut buffer = Vec::with_capacity(8 + peer_params.len() + 8 + session_bytes.len()); + + if buffer.write(&session_bytes_len.to_be_bytes()).is_err() { + return 0; + } + + if buffer.write(&session_bytes).is_err() { + return 0; + } + + let peer_params_len = peer_params.len() as u64; + + if buffer.write(&peer_params_len.to_be_bytes()).is_err() { + return 0; + } + + if buffer.write(peer_params).is_err() { + return 0; + } + *data = Some(buffer); + unsafe { + SSL_set_ex_data(ssl, get_ex_data_index(), ptr::null_mut()); + } + 0 +} + +#[cfg(feature = "http3")] +pub(crate) fn get_session_bytes(session: *mut SSL_SESSION) -> Option> { + unsafe { + // get session encoding length + let len = i2d_SSL_SESSION(session, ptr::null_mut()); + if len == 0 { + return None; + } + let mut out: Vec = Vec::with_capacity(len as usize); + + let len = i2d_SSL_SESSION(session, &mut out.as_mut_ptr()); + let session_bytes = std::slice::from_raw_parts(out.as_mut_ptr(), len as usize).to_vec(); + Some(session_bytes) + } } diff --git a/ylong_http_client/src/util/c_openssl/ssl/mod.rs b/ylong_http_client/src/util/c_openssl/ssl/mod.rs index ab0dfd09d44fea7523f648902f6802c930ac475f..ba1fdd1d201a07f54541edb5b537b7712236e790 100644 --- a/ylong_http_client/src/util/c_openssl/ssl/mod.rs +++ b/ylong_http_client/src/util/c_openssl/ssl/mod.rs @@ -19,6 +19,8 @@ mod ssl_base; mod stream; mod version; +#[cfg(feature = "http3")] +pub(crate) use ctx::get_ex_data_index; pub(crate) use ctx::{SslContext, SslContextBuilder, SSL_VERIFY_NONE}; pub(crate) use error::{InternalError, SslError, SslErrorCode}; pub(crate) use filetype::SslFiletype; diff --git a/ylong_http_client/src/util/h2/manager.rs b/ylong_http_client/src/util/h2/manager.rs index 1fae51bc461a1ea49458ec9617e2189766c58e74..ff355d38a4b5e50e14fd93922058b3fa6744b1a6 100644 --- a/ylong_http_client/src/util/h2/manager.rs +++ b/ylong_http_client/src/util/h2/manager.rs @@ -436,11 +436,7 @@ impl ConnManager { cx: &mut Context<'_>, frame: Frame, ) -> Poll> { - match self - .controller - .streams - .recv_remote_reset(frame.stream_id()) - { + match self.controller.streams.recv_remote_reset(frame.stream_id()) { StreamEndState::OK => self.controller.send_message_to_stream( cx, frame.stream_id(), diff --git a/ylong_http_client/src/util/h2/streams.rs b/ylong_http_client/src/util/h2/streams.rs index d3076393336449594d25403d29c56965879cce39..31cda9b87fa84575472f131a6e3e87b2b37ca7a7 100644 --- a/ylong_http_client/src/util/h2/streams.rs +++ b/ylong_http_client/src/util/h2/streams.rs @@ -266,7 +266,11 @@ impl Streams { } } - pub(crate) fn release_stream_recv_window(&mut self, id: StreamId, size: u32) -> Result<(), H2Error> { + pub(crate) fn release_stream_recv_window( + &mut self, + id: StreamId, + size: u32, + ) -> Result<(), H2Error> { if let Some(stream) = self.stream_map.get_mut(&id) { if stream.recv_window.notification_available() < size { return Err(H2Error::StreamError(id, ErrorCode::FlowControlError)); diff --git a/ylong_http_client/src/util/h3/io_manager.rs b/ylong_http_client/src/util/h3/io_manager.rs index f71b122824aedc66aac4470ec5608b9fdbac9dc9..462ca16a9bb193652930601272129f66c59a7257 100644 --- a/ylong_http_client/src/util/h3/io_manager.rs +++ b/ylong_http_client/src/util/h3/io_manager.rs @@ -16,6 +16,7 @@ use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; +use ylong_http::h3::{H3Error, H3ErrorCode}; use ylong_runtime::time::{sleep, Sleep}; use crate::async_impl::{ConnInfo, QuicConn}; @@ -90,7 +91,17 @@ impl IOMan if let Some(delay) = self.recv_timeout.as_mut() { if let Poll::Ready(()) = delay.as_mut().poll(cx) { self.recv_timeout = None; - self.conn.lock().unwrap().on_timeout(); + let mut conn = self.conn.lock().unwrap(); + conn.on_timeout(); + if conn.is_closed() { + return if conn.is_timed_out() { + Poll::Ready(Err(DispatchErrorKind::H3(H3Error::Connection( + H3ErrorCode::H3InternalError, + )))) + } else { + Poll::Ready(Err(DispatchErrorKind::Disconnect)) + }; + } self.state = IOManagerState::Timeout; return Poll::Ready(Ok(())); } @@ -175,6 +186,7 @@ impl Futur match this.state { IOManagerState::IORecving => { if let Poll::Ready(Err(e)) = this.poll_io_recv(cx) { + // todo: error on timeout return Poll::Ready(Err(e)); } }