From 83e0acad80dbc534bb386342278ff1b6af1310b2 Mon Sep 17 00:00:00 2001 From: Tiga Ultraman Date: Thu, 19 Oct 2023 20:40:08 +0800 Subject: [PATCH] http2 and better binary Signed-off-by: Tiga Ultraman --- ylong_http/src/h2/decoder.rs | 4 +- ylong_http/src/h2/error.rs | 6 +- ylong_http/src/h2/frame.rs | 34 +- ylong_http/src/h2/mod.rs | 2 +- ylong_http_client/Cargo.toml | 2 +- ylong_http_client/src/async_impl/client.rs | 74 +- .../src/async_impl/conn/http1.rs | 17 +- .../src/async_impl/conn/http2.rs | 243 ++- ylong_http_client/src/async_impl/conn/mod.rs | 5 +- ylong_http_client/src/async_impl/http_body.rs | 21 - ylong_http_client/src/async_impl/pool.rs | 13 +- ylong_http_client/src/async_impl/request.rs | 5 +- ylong_http_client/src/lib.rs | 27 +- ylong_http_client/src/util/config/http.rs | 56 +- ylong_http_client/src/util/dispatcher.rs | 1414 +++-------------- ylong_http_client/src/util/h2/buffer/mod.rs | 27 + .../src/util/h2/buffer/settings.rs | 69 + .../src/util/h2/buffer/window.rs | 139 ++ ylong_http_client/src/util/h2/data_ref.rs | 60 + ylong_http_client/src/util/h2/input.rs | 223 +++ ylong_http_client/src/util/h2/io/mod.rs | 26 + ylong_http_client/src/util/h2/io/split.rs | 135 ++ ylong_http_client/src/util/h2/manager.rs | 544 +++++++ ylong_http_client/src/util/h2/mod.rs | 43 + ylong_http_client/src/util/h2/output.rs | 144 ++ ylong_http_client/src/util/h2/streams.rs | 728 +++++++++ ylong_http_client/src/util/mod.rs | 7 + ylong_http_client/src/util/request.rs | 59 + ylong_http_client/tests/common/mod.rs | 2 +- ylong_http_client/tests/sdv_async_http2.rs | 304 ++++ 30 files changed, 3055 insertions(+), 1378 deletions(-) create mode 100644 ylong_http_client/src/util/h2/buffer/mod.rs create mode 100644 ylong_http_client/src/util/h2/buffer/settings.rs create mode 100644 ylong_http_client/src/util/h2/buffer/window.rs create mode 100644 ylong_http_client/src/util/h2/data_ref.rs create mode 100644 ylong_http_client/src/util/h2/input.rs create mode 100644 ylong_http_client/src/util/h2/io/mod.rs create mode 100644 ylong_http_client/src/util/h2/io/split.rs create mode 100644 ylong_http_client/src/util/h2/manager.rs create mode 100644 ylong_http_client/src/util/h2/mod.rs create mode 100644 ylong_http_client/src/util/h2/output.rs create mode 100644 ylong_http_client/src/util/h2/streams.rs create mode 100644 ylong_http_client/src/util/request.rs create mode 100644 ylong_http_client/tests/sdv_async_http2.rs diff --git a/ylong_http/src/h2/decoder.rs b/ylong_http/src/h2/decoder.rs index 6c35595..23323ee 100644 --- a/ylong_http/src/h2/decoder.rs +++ b/ylong_http/src/h2/decoder.rs @@ -318,7 +318,7 @@ impl FrameDecoder { /// Updates the SETTINGS_MAX_FRAME_SIZE. pub fn set_max_frame_size(&mut self, size: u32) -> Result<(), H2Error> { - if size < DEFAULT_MAX_FRAME_SIZE && size > MAX_ALLOWED_MAX_FRAME_SIZE { + if !(DEFAULT_MAX_FRAME_SIZE..=MAX_ALLOWED_MAX_FRAME_SIZE).contains(&size) { return Err(H2Error::ConnectionError(ErrorCode::ProtocolError)); } self.max_frame_size = size; @@ -509,7 +509,7 @@ impl FrameDecoder { if self.header.payload_length != 4 || buf.len() != 4 { return Err(H2Error::ConnectionError(ErrorCode::FrameSizeError)); } - let increment_size = (((0x7f | buf[0]) as u32) << 24) + let increment_size = (((0x7f & buf[0]) as u32) << 24) | ((buf[1] as u32) << 16) | ((buf[2] as u32) << 8) | (buf[3] as u32); diff --git a/ylong_http/src/h2/error.rs b/ylong_http/src/h2/error.rs index 52b39b7..99a6033 100644 --- a/ylong_http/src/h2/error.rs +++ b/ylong_http/src/h2/error.rs @@ -29,7 +29,7 @@ use std::convert::{Infallible, TryFrom}; use crate::error::{ErrorKind, HttpError}; /// The http2 error handle implementation -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub enum H2Error { /// [`Stream Error`] Handling. /// @@ -108,7 +108,7 @@ impl ErrorCode { } impl TryFrom for ErrorCode { - type Error = HttpError; + type Error = H2Error; fn try_from(value: u32) -> Result { let err = match value { 0x00 => ErrorCode::NoError, @@ -125,7 +125,7 @@ impl TryFrom for ErrorCode { 0x0b => ErrorCode::EnhanceYourCalm, 0x0c => ErrorCode::InadequateSecurity, 0x0d => ErrorCode::Http1_1Required, - _ => return Err(HttpError::from(ErrorKind::InvalidInput)), + _ => return Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), }; Ok(err) } diff --git a/ylong_http/src/h2/frame.rs b/ylong_http/src/h2/frame.rs index 94acf73..aef07df 100644 --- a/ylong_http/src/h2/frame.rs +++ b/ylong_http/src/h2/frame.rs @@ -347,6 +347,11 @@ impl Data { pub fn data(&self) -> &Vec { &self.data } + + /// Returns the number of bytes in the `Data` payload. + pub fn size(&self) -> usize { + self.data.len() + } } impl Settings { @@ -381,6 +386,15 @@ impl Settings { let setting_size = 6; settings_count * setting_size } + + /// Returns a ACK SETTINGS frame. + pub fn ack() -> Frame { + Frame::new( + 0, + FrameFlags::new(0x1), + Payload::Settings(Settings::new(vec![])), + ) + } } impl Setting { @@ -415,6 +429,12 @@ impl SettingsBuilder { self } + /// SETTINGS_INITIAL_WINDOW_SIZE(0x04) setting. + pub fn initial_window_size(mut self, size: u32) -> Self { + self.settings.push(Setting::InitialWindowSize(size)); + self + } + /// SETTINGS_MAX_FRAME_SIZE (0x05) setting. pub fn max_frame_size(mut self, size: u32) -> Self { self.settings.push(Setting::MaxFrameSize(size)); @@ -494,7 +514,7 @@ impl WindowUpdate { } /// Returns the window size increment. - pub(crate) fn get_increment(&self) -> u32 { + pub fn get_increment(&self) -> u32 { self.window_size_increment } @@ -543,12 +563,17 @@ impl RstStream { } /// GET the `ErrorCode` of `RstStream` - pub fn error(&self, id: u32) -> Result { + pub fn error(&self, id: u32) -> Result { Ok(H2Error::StreamError( id, ErrorCode::try_from(self.error_code)?, )) } + + /// Returns whether error code is 0. + pub fn is_no_error(&self) -> bool { + self.error_code == 0 + } } impl Ping { @@ -561,6 +586,11 @@ impl Ping { pub fn data(&self) -> [u8; 8] { self.data } + + /// Returns a ACK PING frame. + pub fn ack(ping: Ping) -> Frame { + Frame::new(0, FrameFlags::new(0x1), Payload::Ping(ping)) + } } impl PushPromise { diff --git a/ylong_http/src/h2/mod.rs b/ylong_http/src/h2/mod.rs index 9bdadd7..c7331c8 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, + SettingsBuilder, WindowUpdate, }; pub(crate) use hpack::{HpackDecoder, HpackEncoder}; pub use parts::Parts; diff --git a/ylong_http_client/Cargo.toml b/ylong_http_client/Cargo.toml index 9cbf26a..ba9babf 100644 --- a/ylong_http_client/Cargo.toml +++ b/ylong_http_client/Cargo.toml @@ -14,7 +14,7 @@ tokio = { version = "1.20.1", features = ["io-util", "net", "rt", "rt-multi-thre ylong_runtime = { git = "https://gitee.com/openharmony/commonlibrary_rust_ylong_runtime.git", features = ["net", "sync", "fs", "macros", "time"], optional = true } [dev-dependencies] -hyper = { version = "0.14.23", features = ["http1", "tcp", "server"] } +hyper = { version = "0.14.23", features = ["http1", "http2", "tcp", "server"] } openssl = { version = "0.10.29" } tokio-openssl = { version = "0.6.3" } diff --git a/ylong_http_client/src/async_impl/client.rs b/ylong_http_client/src/async_impl/client.rs index 2a309cb..fa61fc5 100644 --- a/ylong_http_client/src/async_impl/client.rs +++ b/ylong_http_client/src/async_impl/client.rs @@ -31,6 +31,7 @@ use crate::util::dispatcher::Conn; use crate::util::normalizer::RequestFormatter; use crate::util::proxy::Proxies; use crate::util::redirect::{RedirectInfo, Trigger}; +use crate::util::request::RequestArc; #[cfg(feature = "__tls")] use crate::CertVerifier; use crate::Retry; @@ -139,12 +140,12 @@ impl Client { /// } /// ``` pub async fn request(&self, request: Request) -> Result { - let mut request = request; + let mut request = RequestArc::new(request); let mut retries = self.config.retry.times().unwrap_or(0); loop { - let response = self.send_request(&mut request).await; + let response = self.send_request(request.clone()).await; if let Err(ref err) = response { - if retries > 0 && request.body_mut().reuse() { + if retries > 0 && request.ref_mut().body_mut().reuse() { self.interceptors.intercept_retry(err)?; retries -= 1; continue; @@ -156,17 +157,17 @@ impl Client { } impl Client { - async fn send_request(&self, request: &mut Request) -> Result { - let response = self.send_unformatted_request(request).await?; + async fn send_request(&self, request: RequestArc) -> Result { + let response = self.send_unformatted_request(request.clone()).await?; self.redirect(response, request).await } async fn send_unformatted_request( &self, - request: &mut Request, + mut request: RequestArc, ) -> Result { - RequestFormatter::new(&mut *request).format()?; - let conn = self.connect_to(request.uri()).await?; + RequestFormatter::new(request.ref_mut()).format()?; + let conn = self.connect_to(request.ref_mut().uri()).await?; self.send_request_on_conn(conn, request).await } @@ -185,7 +186,7 @@ impl Client { async fn send_request_on_conn( &self, conn: Conn, - request: &mut Request, + request: RequestArc, ) -> Result { let message = Message { request, @@ -201,7 +202,7 @@ impl Client { async fn redirect( &self, response: Response, - request: &mut Request, + mut request: RequestArc, ) -> Result { let mut response = response; let mut info = RedirectInfo::new(); @@ -210,15 +211,16 @@ impl Client { .config .redirect .inner() - .redirect(request, &response, &mut info)? + .redirect(request.ref_mut(), &response, &mut info)? { Trigger::NextLink => { // Here the body should be reused. - if !request.body_mut().reuse() { - *request.body_mut() = Body::empty(); + if !request.ref_mut().body_mut().reuse() { + *request.ref_mut().body_mut() = Body::empty(); } - self.interceptors.intercept_redirect_request(request)?; - response = self.send_unformatted_request(request).await?; + self.interceptors + .intercept_redirect_request(request.ref_mut())?; + response = self.send_unformatted_request(request.clone()).await?; self.interceptors.intercept_redirect_response(&response)?; } Trigger::Stop => { @@ -460,7 +462,7 @@ impl ClientBuilder { /// let config = ClientBuilder::new().set_http2_max_frame_size(2 << 13); /// ``` pub fn set_http2_max_frame_size(mut self, size: u32) -> Self { - self.http.http2_config.max_frame_size = size; + self.http.http2_config.set_max_frame_size(size); self } @@ -474,7 +476,7 @@ impl ClientBuilder { /// let config = ClientBuilder::new().set_http2_max_header_list_size(16 << 20); /// ``` pub fn set_http2_max_header_list_size(mut self, size: u32) -> Self { - self.http.http2_config.max_header_list_size = size; + self.http.http2_config.set_max_header_list_size(size); self } @@ -488,7 +490,37 @@ impl ClientBuilder { /// let config = ClientBuilder::new().set_http2_max_header_list_size(4096); /// ``` pub fn set_http2_header_table_size(mut self, size: u32) -> Self { - self.http.http2_config.header_table_size = size; + self.http.http2_config.set_header_table_size(size); + self + } + + /// Sets the maximum connection window allowed by the client. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::async_impl::ClientBuilder; + /// + /// let config = ClientBuilder::new().set_conn_recv_window_size(4096); + /// ``` + pub fn set_conn_recv_window_size(mut self, size: u32) -> Self { + assert!(size <= crate::util::h2::MAX_FLOW_CONTROL_WINDOW); + self.http.http2_config.set_conn_window_size(size); + self + } + + /// Sets the `SETTINGS_INITIAL_WINDOW_SIZE`. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::async_impl::ClientBuilder; + /// + /// let config = ClientBuilder::new().set_stream_recv_window_size(4096); + /// ``` + pub fn set_stream_recv_window_size(mut self, size: u32) -> Self { + assert!(size <= crate::util::h2::MAX_FLOW_CONTROL_WINDOW); + self.http.http2_config.set_stream_window_size(size); self } } @@ -786,6 +818,7 @@ mod ut_async_impl_client { use crate::async_impl::interceptor::IdleInterceptor; use crate::async_impl::{ClientBuilder, HttpBody}; use crate::util::normalizer::BodyLength; + use crate::util::request::RequestArc; use crate::util::Redirect; let response_str = "HTTP/1.1 304 \r\nAge: \t 270646 \t \t\r\nLocation: \t http://example3.com:80/foo?a=1 \t \t\r\nDate: \t Mon, 19 Dec 2022 01:46:59 GMT \t \t\r\nEtag:\t \"3147526947+gzip\" \t \t\r\n\r\n".as_bytes(); @@ -803,17 +836,18 @@ mod ut_async_impl_client { .unwrap(); let response = HttpResponse::from_raw_parts(result.0, until_close); let response = Response::new(response); - let mut request = Request::builder() + let request = Request::builder() .url("http://example1.com:80/foo?a=1") .body(Body::slice("this is a body")) .unwrap(); + let request = RequestArc::new(request); let client = ClientBuilder::default() .redirect(Redirect::limited(2)) .connect_timeout(Timeout::from_secs(2)) .build() .unwrap(); - let res = client.redirect(response, &mut request).await; + let res = client.redirect(response, request.clone()).await; assert!(res.is_ok()) } diff --git a/ylong_http_client/src/async_impl/conn/http1.rs b/ylong_http_client/src/async_impl/conn/http1.rs index fd74173..b6ce44e 100644 --- a/ylong_http_client/src/async_impl/conn/http1.rs +++ b/ylong_http_client/src/async_impl/conn/http1.rs @@ -33,7 +33,7 @@ const TEMP_BUF_SIZE: usize = 16 * 1024; pub(crate) async fn request( mut conn: Http1Conn, - message: Message<'_>, + mut message: Message, ) -> Result where S: AsyncRead + AsyncWrite + ConnInfo + Sync + Send + Unpin + 'static, @@ -41,12 +41,15 @@ where message .interceptor .intercept_connection(conn.raw_mut().conn_detail())?; - message.interceptor.intercept_request(message.request)?; + message + .interceptor + .intercept_request(message.request.ref_mut())?; let mut buf = vec![0u8; TEMP_BUF_SIZE]; // Encodes and sends Request-line and Headers(non-body fields). - let mut part_encoder = RequestEncoder::new(message.request.part().clone()); - if conn.raw_mut().is_proxy() && message.request.uri().scheme() == Some(&Scheme::HTTP) { + let mut part_encoder = RequestEncoder::new(message.request.ref_mut().part().clone()); + if conn.raw_mut().is_proxy() && message.request.ref_mut().uri().scheme() == Some(&Scheme::HTTP) + { part_encoder.absolute_uri(true); } loop { @@ -69,6 +72,7 @@ where let content_length = message .request + .ref_mut() .part() .headers .get("Content-Length") @@ -78,6 +82,7 @@ where let transfer_encoding = message .request + .ref_mut() .part() .headers .get("Transfer-Encoding") @@ -85,7 +90,7 @@ where .map(|v| v.contains("chunked")) .unwrap_or(false); - let body = message.request.body_mut(); + let body = message.request.ref_mut().body_mut(); match (content_length, transfer_encoding) { (_, true) => { @@ -161,7 +166,7 @@ where } } - let length = match BodyLengthParser::new(message.request.method(), &part).parse() { + let length = match BodyLengthParser::new(message.request.ref_mut().method(), &part).parse() { Ok(length) => length, Err(e) => { conn.shutdown(); diff --git a/ylong_http_client/src/async_impl/conn/http2.rs b/ylong_http_client/src/async_impl/conn/http2.rs index 3feb846..602d4c3 100644 --- a/ylong_http_client/src/async_impl/conn/http2.rs +++ b/ylong_http_client/src/async_impl/conn/http2.rs @@ -12,95 +12,74 @@ // limitations under the License. use std::cmp::min; -use std::future::Future; +use std::ops::Deref; use std::pin::Pin; +use std::sync::atomic::Ordering; use std::task::{Context, Poll}; -use ylong_http::body::async_impl::Body; use ylong_http::error::HttpError; use ylong_http::h2; use ylong_http::h2::{ErrorCode, Frame, FrameFlags, H2Error, Payload, PseudoHeaders}; use ylong_http::headers::Headers; use ylong_http::request::uri::Scheme; -use ylong_http::request::{Request, RequestPart}; +use ylong_http::request::RequestPart; use ylong_http::response::status::StatusCode; -use ylong_http::response::{Response, ResponsePart}; +use ylong_http::response::ResponsePart; -use crate::async_impl::client::Retryable; -use crate::async_impl::conn::HttpBody; +use crate::async_impl::conn::StreamData; use crate::async_impl::request::Message; -use crate::async_impl::StreamData; +use crate::async_impl::{HttpBody, Response}; use crate::error::{ErrorKind, HttpClientError}; -use crate::runtime::{AsyncRead, AsyncWrite, ReadBuf}; +use crate::runtime::{AsyncRead, ReadBuf}; use crate::util::dispatcher::http2::Http2Conn; +use crate::util::h2::{BodyDataRef, RequestWrapper}; +use crate::util::normalizer::BodyLengthParser; const UNUSED_FLAG: u8 = 0x0; -pub(crate) async fn request( +pub(crate) async fn request( mut conn: Http2Conn, - message: Message, - retryable: &mut Retryable, -) -> Result, HttpClientError> + mut message: Message, +) -> Result where - T: Body, - S: AsyncRead + AsyncWrite + Sync + Send + Unpin + 'static, + S: Sync + Send + Unpin + 'static, { - let part = message.request.part().clone(); - let body = message.request.body_mut(); + message + .interceptor + .intercept_request(message.request.ref_mut())?; + let part = message.request.ref_mut().part().clone(); - // TODO Due to the reason of the Body structure, the use of the trailer is not - // implemented here for the time being, and it needs to be completed after the - // Body trait is provided to obtain the trailer interface - match build_data_frame(conn.id as usize, body).await? { - None => { - let headers = build_headers_frame(conn.id, part, true) - .map_err(|e| HttpClientError::from_error(ErrorKind::Request, Some(e)))?; - conn.send_frame_to_controller(headers).map_err(|e| { - retryable.set_retry(true); - HttpClientError::from_error(ErrorKind::Request, Some(e)) - })?; - } - Some(data) => { - let headers = build_headers_frame(conn.id, part, false) - .map_err(|e| HttpClientError::from_error(ErrorKind::Request, Some(e)))?; - conn.send_frame_to_controller(headers).map_err(|e| { - retryable.set_retry(true); - HttpClientError::from_error(ErrorKind::Request, Some(e)) - })?; - conn.send_frame_to_controller(data).map_err(|e| { - retryable.set_retry(true); - HttpClientError::from_error(ErrorKind::Request, Some(e)) - })?; - } - } - let frame = Pin::new(&mut conn.stream_info) - .await - .map_err(|e| HttpClientError::from_error(ErrorKind::Request, Some(e)))?; - frame_2_response(conn, frame, retryable) + // TODO Implement trailer. + let headers = build_headers_frame(conn.id, part, false) + .map_err(|e| HttpClientError::from_error(ErrorKind::Request, e))?; + let data = BodyDataRef::new(message.request.clone()); + let stream = RequestWrapper { + header: headers, + data, + }; + conn.send_frame_to_controller(stream)?; + let frame = conn.receiver.recv().await?; + frame_2_response(conn, frame, message) } -fn frame_2_response( +fn frame_2_response( conn: Http2Conn, headers_frame: Frame, - retryable: &mut Retryable, - message: Message, -) -> Result, HttpClientError> + mut message: Message, +) -> Result where - S: AsyncRead + AsyncWrite + Sync + Send + Unpin + 'static, + S: Sync + Send + Unpin + 'static, { let part = match headers_frame.payload() { Payload::Headers(headers) => { let (pseudo, fields) = headers.parts(); let status_code = match pseudo.status() { Some(status) => StatusCode::from_bytes(status.as_bytes()) - .map_err(|e| HttpClientError::from_error(ErrorKind::Request, Some(e)))?, + .map_err(|e| HttpClientError::from_error(ErrorKind::Request, e))?, None => { return Err(HttpClientError::from_error( ErrorKind::Request, - Some(HttpError::from(H2Error::StreamError( - conn.id, - ErrorCode::ProtocolError, - ))), + HttpError::from(H2Error::StreamError(conn.id, ErrorCode::ProtocolError)), )); } }; @@ -110,82 +89,28 @@ where headers: fields.clone(), } } - Payload::RstStream(reset) => { - return Err(HttpClientError::from_error( - ErrorKind::Request, - Some(HttpError::from(reset.error(conn.id).map_err(|e| { - HttpClientError::from_error(ErrorKind::Request, Some(e)) - })?)), - )); - } - Payload::Goaway(_) => { - // return Err(HttpClientError::from(ErrorKind::Resend)); - retryable.set_retry(true); - return Err(HttpClientError::from_str(ErrorKind::Request, "GoAway")); - } _ => { return Err(HttpClientError::from_error( ErrorKind::Request, - Some(HttpError::from(H2Error::StreamError( - conn.id, - ErrorCode::ProtocolError, - ))), + HttpError::from(H2Error::StreamError(conn.id, ErrorCode::ProtocolError)), )); } }; - let body = { - if headers_frame.flags().is_end_stream() { - HttpBody::empty() - } else { - // TODO Can Content-Length in h2 be null? - let content_length = part - .headers - .get("Content-Length") - .map(|v| v.to_string().unwrap_or(String::new())) - .and_then(|s| s.parse::().ok()); - match content_length { - None => HttpBody::empty(), - Some(0) => HttpBody::empty(), - Some(size) => { - let text_io = TextIo::new(conn); - HttpBody::text(size, &[0u8; 0], Box::new(text_io), message.interceptor) - } - } + let text_io = TextIo::new(conn); + // TODO Whether HTTP2 can have no Content_Length, only whether the END_STREAM + // flag has a Body + let length = match BodyLengthParser::new(message.request.ref_mut().method(), &part).parse() { + Ok(length) => length, + Err(e) => { + return Err(e); } }; - Ok(Response::from_raw_parts(part, body)) -} + let body = HttpBody::new(message.interceptor, length, Box::new(text_io), &[0u8; 0])?; -pub(crate) async fn build_data_frame( - id: usize, - body: &mut T, -) -> Result, HttpClientError> { - let mut data_vec = vec![]; - let mut buf = [0u8; 1024]; - loop { - let size = body - .data(&mut buf) - .await - .map_err(|e| HttpClientError::from_error(ErrorKind::Request, Some(e)))?; - if size == 0 { - break; - } - data_vec.extend_from_slice(&buf[..size]); - } - if data_vec.is_empty() { - Ok(None) - } else { - // TODO When the Body trait supports trailer, END_STREAM_FLAG needs to be - // modified - let mut flag = FrameFlags::new(UNUSED_FLAG); - flag.set_end_stream(true); - Ok(Some(Frame::new( - id, - flag, - Payload::Data(h2::Data::new(data_vec)), - ))) - } + Ok(Response::new( + ylong_http::response::Response::from_raw_parts(part, body), + )) } pub(crate) fn build_headers_frame( @@ -229,8 +154,10 @@ fn check_connection_specific_headers(id: u32, headers: &Headers) -> Result<(), H return Err(H2Error::StreamError(id, ErrorCode::ProtocolError).into()); } } - if let Some(te_value) = headers.get("te") { - if te_value.to_string()? != "trailers" { + + if let Some(te_ref) = headers.get("te") { + let te = te_ref.to_string()?; + if te.as_str() != "trailers" { return Err(H2Error::StreamError(id, ErrorCode::ProtocolError).into()); } } @@ -252,8 +179,6 @@ fn build_pseudo_headers(request_part: &RequestPart) -> PseudoHeaders { .path_and_query() .or_else(|| Some(String::from("/"))), ); - // TODO Validity verification is required, for example: `Authority` must be - // consistent with the `Host` header pseudo.set_authority(request_part.uri.authority().map(|auth| auth.to_string())); pseudo } @@ -265,7 +190,32 @@ struct TextIo { pub(crate) is_closed: bool, } -impl TextIo { +struct HttpReadBuf<'a, 'b> { + buf: &'a mut ReadBuf<'b>, +} + +impl<'a, 'b> HttpReadBuf<'a, 'b> { + pub(crate) fn append_slice(&mut self, buf: &[u8]) { + #[cfg(feature = "ylong_base")] + self.buf.append(buf); + + #[cfg(feature = "tokio_base")] + self.buf.put_slice(buf); + } +} + +impl<'a, 'b> Deref for HttpReadBuf<'a, 'b> { + type Target = ReadBuf<'b>; + + fn deref(&self) -> &Self::Target { + self.buf + } +} + +impl TextIo +where + S: Sync + Send + Unpin + 'static, +{ pub(crate) fn new(handle: Http2Conn) -> Self { Self { handle, @@ -276,19 +226,20 @@ impl TextIo { } } -impl StreamData for TextIo { +impl StreamData for TextIo { fn shutdown(&self) { - todo!() + self.handle.io_shutdown.store(true, Ordering::Release); } } -impl AsyncRead for TextIo { +impl AsyncRead for TextIo { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { let text_io = self.get_mut(); + let mut buf = HttpReadBuf { buf }; if buf.remaining() == 0 || text_io.is_closed { return Poll::Ready(Ok(())); @@ -305,12 +256,12 @@ impl AsyncRead for Te let data_len = data.len() - text_io.offset; let fill_len = min(unfilled_len, data_len); if unfilled_len < data_len { - buf.put_slice(&data[text_io.offset..text_io.offset + fill_len]); + buf.append_slice(&data[text_io.offset..text_io.offset + fill_len]); text_io.offset += fill_len; break; } else { - buf.put_slice(&data[text_io.offset..text_io.offset + fill_len]); - text_io.offset += fill_len; + buf.append_slice(&data[text_io.offset..text_io.offset + fill_len]); + text_io.offset = 0; if frame.flags().is_end_stream() { text_io.is_closed = true; break; @@ -326,11 +277,12 @@ impl AsyncRead for Te } } - let poll_result = Pin::new(&mut text_io.handle.stream_info) - .poll(cx) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + let poll_result = text_io + .handle + .receiver + .poll_recv(cx) + .map_err(|_e| std::io::Error::from(std::io::ErrorKind::Other))?; - // TODO Added the frame type. match poll_result { Poll::Ready(frame) => match frame.payload() { Payload::Headers(_) => { @@ -344,23 +296,28 @@ impl AsyncRead for Te let data_len = data.len(); let fill_len = min(data_len, unfilled_len); if unfilled_len < data_len { - buf.put_slice(&data[..fill_len]); + buf.append_slice(&data[..fill_len]); text_io.offset += fill_len; text_io.remain = Some(frame); break; } else { - buf.put_slice(&data[..fill_len]); + buf.append_slice(&data[..fill_len]); if frame.flags().is_end_stream() { text_io.is_closed = true; break; } } } - Payload::RstStream(_) => { - return Poll::Ready(Err(std::io::Error::new( - std::io::ErrorKind::Other, - HttpError::from(H2Error::ConnectionError(ErrorCode::ProtocolError)), - ))) + Payload::RstStream(error) => { + if error.is_no_error() { + text_io.is_closed = true; + break; + } else { + return Poll::Ready(Err(std::io::Error::new( + std::io::ErrorKind::Other, + HttpError::from(H2Error::ConnectionError(ErrorCode::ProtocolError)), + ))); + } } _ => { return Poll::Ready(Err(std::io::Error::new( diff --git a/ylong_http_client/src/async_impl/conn/mod.rs b/ylong_http_client/src/async_impl/conn/mod.rs index 1bafb34..17405b1 100644 --- a/ylong_http_client/src/async_impl/conn/mod.rs +++ b/ylong_http_client/src/async_impl/conn/mod.rs @@ -31,10 +31,7 @@ pub(crate) trait StreamData: AsyncRead { // TODO: Use structures instead of a function to reuse the io buf. // TODO: Maybe `AsyncWrapper>` ?. -pub(crate) async fn request( - conn: Conn, - message: Message<'_>, -) -> Result +pub(crate) async fn request(conn: Conn, message: Message) -> Result where S: AsyncRead + AsyncWrite + ConnInfo + Sync + Send + Unpin + 'static, { diff --git a/ylong_http_client/src/async_impl/http_body.rs b/ylong_http_client/src/async_impl/http_body.rs index 998c4b9..82afdf6 100644 --- a/ylong_http_client/src/async_impl/http_body.rs +++ b/ylong_http_client/src/async_impl/http_body.rs @@ -94,27 +94,6 @@ impl HttpBody { Ok(Self { kind, sleep: None }) } - #[cfg(feature = "http2")] - pub(crate) fn empty() -> Self { - Self { - kind: Kind::Empty, - sleep: None, - } - } - - #[cfg(feature = "http2")] - pub(crate) fn text( - len: usize, - pre: &[u8], - io: BoxStreamData, - interceptors: Arc, - ) -> Self { - Self { - kind: Kind::Text(Text::new(len, pre, io, interceptors)), - sleep: None, - } - } - pub(crate) fn set_sleep(&mut self, sleep: Option>>) { self.sleep = sleep; } diff --git a/ylong_http_client/src/async_impl/pool.rs b/ylong_http_client/src/async_impl/pool.rs index 6849c5d..a7d0e76 100644 --- a/ylong_http_client/src/async_impl/pool.rs +++ b/ylong_http_client/src/async_impl/pool.rs @@ -57,7 +57,7 @@ pub(crate) struct Conns { list: Arc>>>, #[cfg(feature = "http2")] - h2_occupation: Arc>, + h2_occupation: Arc>, } impl Conns { @@ -66,7 +66,7 @@ impl Conns { list: Arc::new(Mutex::new(Vec::new())), #[cfg(feature = "http2")] - h2_occupation: Arc::new(crate::AsyncMutex::new(())), + h2_occupation: Arc::new(crate::runtime::AsyncMutex::new(())), } } } @@ -82,7 +82,7 @@ impl Clone for Conns { } } -impl Conns { +impl Conns { async fn conn( &self, config: HttpConfig, @@ -106,9 +106,9 @@ impl Conns { // create tcp connection. let dispatcher = ConnDispatcher::http2( config.http2_config, - connect_fut.await.map_err(|e| { - HttpClientError::from_error(ErrorKind::Connect, Some(e)) - })?, + connect_fut + .await + .map_err(|e| HttpClientError::from_error(ErrorKind::Connect, e))?, ); Ok(self.dispatch_conn(dispatcher)) } @@ -145,6 +145,7 @@ impl Conns { let mut list = self.list.lock().unwrap(); let mut conn = None; let curr = take(&mut *list); + // TODO Distinguish between http2 connections and http1 connections. for dispatcher in curr.into_iter() { // Discard invalid dispatchers. if dispatcher.is_shutdown() { diff --git a/ylong_http_client/src/async_impl/request.rs b/ylong_http_client/src/async_impl/request.rs index 35a571c..1a61eaa 100644 --- a/ylong_http_client/src/async_impl/request.rs +++ b/ylong_http_client/src/async_impl/request.rs @@ -26,6 +26,7 @@ use ylong_http::request::{Request as Req, RequestBuilder as ReqBuilder}; use crate::async_impl::interceptor::Interceptors; use crate::error::{ErrorKind, HttpClientError}; use crate::runtime::{AsyncRead, ReadBuf}; +use crate::util::request::RequestArc; /// A structure that represents an HTTP `Request`. It contains a request line, /// some HTTP headers and a HTTP body. @@ -396,8 +397,8 @@ impl PercentEncoder { } } -pub(crate) struct Message<'a> { - pub(crate) request: &'a mut Request, +pub(crate) struct Message { + pub(crate) request: RequestArc, pub(crate) interceptor: Arc, } diff --git a/ylong_http_client/src/lib.rs b/ylong_http_client/src/lib.rs index aebced7..ae871a9 100644 --- a/ylong_http_client/src/lib.rs +++ b/ylong_http_client/src/lib.rs @@ -68,9 +68,13 @@ pub use util::*; #[cfg(any(feature = "tokio_base", feature = "ylong_base"))] pub(crate) mod runtime { #[cfg(all(feature = "tokio_base", feature = "http2"))] - pub(crate) use tokio::sync::{ - mpsc::{error::TryRecvError, unbounded_channel, UnboundedReceiver, UnboundedSender}, - Mutex as AsyncMutex, MutexGuard, + pub(crate) use tokio::{ + io::{split, ReadHalf, WriteHalf}, + spawn, + sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + Mutex as AsyncMutex, + }, }; #[cfg(all(feature = "tokio_base", feature = "async"))] pub(crate) use tokio::{ @@ -78,15 +82,22 @@ pub(crate) mod runtime { net::TcpStream, time::{sleep, timeout, Sleep}, }; - #[cfg(all(feature = "ylong_base", feature = "http2"))] - pub(crate) use ylong_runtime::sync::{ - mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - Mutex as AsyncMutex, MutexGuard, RecvError as TryRecvError, - }; #[cfg(feature = "ylong_base")] pub(crate) use ylong_runtime::{ io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}, net::TcpStream, time::{sleep, timeout, Sleep}, }; + // TODO add ReadHalf and WriteHalf + #[cfg(all(feature = "ylong_base", feature = "http2"))] + pub(crate) use ylong_runtime::{ + spawn, + sync::{ + mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + Mutex as AsyncMutex, + }, + }; + + #[cfg(all(feature = "ylong_base", feature = "http2"))] + pub(crate) use crate::{split, Reader as ReadHalf, Writer as WriteHalf}; } diff --git a/ylong_http_client/src/util/config/http.rs b/ylong_http_client/src/util/config/http.rs index fed252e..fc5e368 100644 --- a/ylong_http_client/src/util/config/http.rs +++ b/ylong_http_client/src/util/config/http.rs @@ -29,7 +29,7 @@ impl HttpConfig { version: HttpVersion::Http1, #[cfg(feature = "http2")] - http2_config: http2::H2Config::default(), + http2_config: http2::H2Config::new(), } } } @@ -56,13 +56,23 @@ pub(crate) mod http2 { const DEFAULT_MAX_FRAME_SIZE: u32 = 2 << 13; const DEFAULT_HEADER_TABLE_SIZE: u32 = 4096; const DEFAULT_MAX_HEADER_LIST_SIZE: u32 = 16 << 20; + // window size at the client connection level + // The initial value specified in rfc9113 is 64kb, + // but the default value is 1mb for performance purposes and is synchronized + // using WINDOW_UPDATE after sending SETTINGS. + const DEFAULT_CONN_WINDOW_SIZE: u32 = 1024 * 1024; + // TODO Raise the default value size here. + const DEFAULT_STREAM_WINDOW_SIZE: u32 = 64 * 1024; /// Settings which can be used to configure a http2 connection. #[derive(Clone)] pub(crate) struct H2Config { - pub(crate) max_frame_size: u32, - pub(crate) max_header_list_size: u32, - pub(crate) header_table_size: u32, + max_frame_size: u32, + max_header_list_size: u32, + header_table_size: u32, + init_conn_window_size: u32, + init_stream_window_size: u32, + enable_push: bool, } impl H2Config { @@ -71,6 +81,29 @@ pub(crate) mod http2 { Self::default() } + /// Sets the SETTINGS_MAX_FRAME_SIZE. + pub(crate) fn set_max_frame_size(&mut self, size: u32) { + self.max_frame_size = size; + } + + /// Sets the SETTINGS_MAX_HEADER_LIST_SIZE. + pub(crate) fn set_max_header_list_size(&mut self, size: u32) { + self.max_header_list_size = size; + } + + /// Sets the SETTINGS_HEADER_TABLE_SIZE. + pub(crate) fn set_header_table_size(&mut self, size: u32) { + self.header_table_size = size; + } + + pub(crate) fn set_conn_window_size(&mut self, size: u32) { + self.init_conn_window_size = size; + } + + pub(crate) fn set_stream_window_size(&mut self, size: u32) { + self.init_stream_window_size = size; + } + /// Gets the SETTINGS_MAX_FRAME_SIZE. pub(crate) fn max_frame_size(&self) -> u32 { self.max_frame_size @@ -85,6 +118,18 @@ pub(crate) mod http2 { pub(crate) fn header_table_size(&self) -> u32 { self.header_table_size } + + pub(crate) fn enable_push(&self) -> bool { + self.enable_push + } + + pub(crate) fn conn_window_size(&self) -> u32 { + self.init_conn_window_size + } + + pub(crate) fn stream_window_size(&self) -> u32 { + self.init_stream_window_size + } } impl Default for H2Config { @@ -93,6 +138,9 @@ pub(crate) mod http2 { max_frame_size: DEFAULT_MAX_FRAME_SIZE, max_header_list_size: DEFAULT_MAX_HEADER_LIST_SIZE, header_table_size: DEFAULT_HEADER_TABLE_SIZE, + init_conn_window_size: DEFAULT_CONN_WINDOW_SIZE, + init_stream_window_size: DEFAULT_STREAM_WINDOW_SIZE, + enable_push: false, } } } diff --git a/ylong_http_client/src/util/dispatcher.rs b/ylong_http_client/src/util/dispatcher.rs index b1d5f63..0ae3ef2 100644 --- a/ylong_http_client/src/util/dispatcher.rs +++ b/ylong_http_client/src/util/dispatcher.rs @@ -147,422 +147,205 @@ pub(crate) mod http1 { #[cfg(feature = "http2")] pub(crate) mod http2 { - use std::collections::{HashMap, VecDeque}; - use std::future::Future; - use std::mem::take; + use std::collections::HashMap; + use std::marker::PhantomData; use std::pin::Pin; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; - use std::task::{Context, Poll, Waker}; + use std::task::{Context, Poll}; - use ylong_http::error::HttpError; - use ylong_http::h2; - use ylong_http::h2::Payload::Settings; use ylong_http::h2::{ - ErrorCode, Frame, FrameDecoder, FrameEncoder, FrameFlags, FrameKind, FramesIntoIter, - Goaway, H2Error, Payload, RstStream, Setting, SettingsBuilder, + ErrorCode, Frame, FrameDecoder, FrameEncoder, FrameFlags, Goaway, H2Error, Payload, + Settings, SettingsBuilder, }; - use super::{ConnDispatcher, Dispatcher}; - use crate::dispatcher::http2::StreamState::Closed; - use crate::error::{ErrorKind, HttpClientError}; use crate::runtime::{ - unbounded_channel, AsyncMutex, AsyncRead, AsyncWrite, MutexGuard, ReadBuf, TryRecvError, - UnboundedReceiver, UnboundedSender, + unbounded_channel, AsyncRead, AsyncWrite, AsyncWriteExt, UnboundedReceiver, + UnboundedSender, WriteHalf, }; use crate::util::config::H2Config; - - impl ConnDispatcher { - pub(crate) fn http2(config: H2Config, io: S) -> Self { - Self::Http2(Http2Dispatcher::new(config, io)) - } - } - - // The data type of the first Frame sent to the `StreamController`. - type Send2Ctrl = (Option<(u32, UnboundedSender)>, Frame); + use crate::util::dispatcher::{ConnDispatcher, Dispatcher}; + use crate::util::h2::{ConnManager, FlowControl, RecvData, RequestWrapper, SendData, Streams}; + use crate::{ErrorKind, HttpClientError}; const DEFAULT_MAX_STREAM_ID: u32 = u32::MAX >> 1; const DEFAULT_MAX_FRAME_SIZE: usize = 2 << 13; const DEFAULT_MAX_HEADER_LIST_SIZE: usize = 16 << 20; + const DEFAULT_WINDOW_SIZE: u32 = 65535; - // HTTP2-based connection manager, which can dispatch connections to other - // threads according to HTTP2 syntax. - pub(crate) struct Http2Dispatcher { - pub(crate) controller: Arc>, - pub(crate) next_stream_id: Arc, - pub(crate) sender: UnboundedSender, + pub(crate) enum RespMessage { + Output(Frame), + OutputExit(DispatchErrorKind), } - pub(crate) struct Http2Conn { - // Handle id - pub(crate) id: u32, - // Sends frame to StreamController - pub(crate) sender: UnboundedSender, - pub(crate) stream_info: StreamInfo, + pub(crate) enum OutputMessage { + Output(Frame), + OutputExit(DispatchErrorKind), } - pub(crate) struct StreamInfo { - // Stream id + pub(crate) struct ReqMessage { pub(crate) id: u32, - pub(crate) next_stream_id: Arc, - // Receive the Response frame transmitted from the StreamController - pub(crate) receiver: FrameReceiver, - // Used to handle TCP Stream - pub(crate) controller: Arc>, + pub(crate) sender: UnboundedSender, + pub(crate) request: RequestWrapper, } - pub(crate) struct StreamController { - // I/O unavailability flag, which prevents the upper layer from using this I/O to create - // new streams. - pub(crate) io_shutdown: AtomicBool, - // Indicates that the dispatcher is occupied. At this time, a user coroutine is already - // acting as the dispatcher. - pub(crate) occupied: AtomicU32, - pub(crate) dispatcher_invalid: AtomicBool, - pub(crate) manager: AsyncMutex>, - pub(crate) stream_waker: Mutex, + #[derive(Debug, Eq, PartialEq, Clone)] + pub(crate) enum DispatchErrorKind { + H2(H2Error), + Io(std::io::ErrorKind), + ChannelClosed, + Disconnect, } - pub(crate) struct StreamWaker { - waker: HashMap, + // HTTP2-based connection manager, which can dispatch connections to other + // threads according to HTTP2 syntax. + pub(crate) struct Http2Dispatcher { + pub(crate) next_stream_id: StreamId, + pub(crate) sender: UnboundedSender, + pub(crate) io_shutdown: Arc, + pub(crate) _mark: PhantomData, } - pub(crate) struct IoManager { - inner: Inner, - senders: HashMap>, - frame_receiver: UnboundedReceiver, - streams: Streams, - frame_iter: FrameIter, - connection_frame: ConnectionFrames, + pub(crate) struct Http2Conn { + // Handle id + pub(crate) id: u32, + // Sends frame to StreamController + pub(crate) sender: UnboundedSender, + pub(crate) receiver: RespReceiver, + pub(crate) io_shutdown: Arc, + pub(crate) _mark: PhantomData, + } + + pub(crate) struct StreamController { + // The connection close flag organizes new stream commits to the current connection when + // closed. + pub(crate) io_shutdown: Arc, + // The senders of all connected stream channels of response. + pub(crate) senders: HashMap>, + // Stream information on the connection. + pub(crate) streams: Streams, + // Received GO_AWAY frame. + pub(crate) go_away: Option, + // The last GO_AWAY frame sent by the client. + pub(crate) go_away_sync: GoAwaySync, } #[derive(Default)] - pub(crate) struct FrameIter { - iter: Option, + pub(crate) struct GoAwaySync { + pub(crate) going_away: Option, } - pub(crate) struct Streams { - stream_to_send: VecDeque, - buffer: HashMap, - } - - pub(crate) struct StreamBuffer { - state: StreamState, - frames: VecDeque, - } - - pub(crate) struct Inner { - pub(crate) io: S, - pub(crate) encoder: FrameEncoder, - pub(crate) decoder: FrameDecoder, - } - - pub(crate) enum ReadState { - EmptyIo, - CurrentStream, - } - - enum DispatchState { - Partial, - Finish, - } - - #[derive(Clone)] - pub(crate) enum ResetReason { - Local, - Remote, - Goaway(u32), + #[derive(Default)] + pub(crate) struct SettingsSync { + pub(crate) settings: SettingsState, } - #[derive(Clone)] - pub(crate) enum SettingsSync { - Send(h2::Settings), - Acknowledging(h2::Settings), + #[derive(Default, Clone)] + pub(crate) enum SettingsState { + Acknowledging(Settings), + #[default] Synced, } pub(crate) struct StreamId { // TODO Determine the maximum value of id. - next_id: AtomicU32, - } - - // TODO Add "open", "half-closed", "reserved" state - #[derive(Clone)] - pub(crate) enum StreamState { - Idle, - Closed(ResetReason), + id: AtomicU32, } #[derive(Default)] - pub(crate) struct FrameReceiver { - receiver: Option>, + pub(crate) struct RespReceiver { + receiver: Option>, } - impl StreamController { - pub(crate) fn new( - inner: Inner, - frame_receiver: UnboundedReceiver, - connection_frame: ConnectionFrames, - ) -> Self { - let manager = IoManager::new(inner, frame_receiver, connection_frame); - Self { - io_shutdown: AtomicBool::new(false), - // 0 means io is not occupied - occupied: AtomicU32::new(0), - dispatcher_invalid: AtomicBool::new(false), - manager: AsyncMutex::new(manager), - stream_waker: Mutex::new(StreamWaker::new()), - } - } - - pub(crate) fn shutdown(&self) { - self.io_shutdown.store(true, Ordering::Release); - } - - pub(crate) fn invalid(&self) { - self.dispatcher_invalid.store(true, Ordering::Release); + impl ConnDispatcher + where + S: AsyncRead + AsyncWrite + Sync + Send + Unpin + 'static, + { + pub(crate) fn http2(config: H2Config, io: S) -> Self { + Self::Http2(Http2Dispatcher::new(config, io)) } } - impl Streams { - pub(crate) fn new() -> Self { - Self { - stream_to_send: VecDeque::new(), - buffer: HashMap::new(), - } - } + impl Http2Dispatcher + where + S: AsyncRead + AsyncWrite + Sync + Send + Unpin + 'static, + { + pub(crate) fn new(config: H2Config, io: S) -> Self { + let settings = create_initial_settings(&config); - pub(crate) fn size(&self) -> usize { - self.stream_to_send.len() - } + let mut flow = FlowControl::new(DEFAULT_WINDOW_SIZE, DEFAULT_WINDOW_SIZE); + flow.setup_recv_window(config.conn_window_size()); - pub(crate) fn insert(&mut self, frame: Frame) { - let id = frame.stream_id() as u32; - self.stream_to_send.push_back(id); - match self.buffer.get_mut(&id) { - Some(sender) => { - sender.push_back(frame); - } - None => { - let mut sender = StreamBuffer::new(); - sender.push_back(frame); - self.buffer.insert(id, sender); - } - } - } + let streams = Streams::new(config.stream_window_size(), DEFAULT_WINDOW_SIZE, flow); - pub(crate) fn get_goaway_streams( - &mut self, - last_stream_id: u32, - ) -> Result, H2Error> { - let mut ids = vec![]; - for (id, sender) in self.buffer.iter_mut() { - if *id >= last_stream_id { - ids.push(*id); - sender.go_away(*id)?; - } - } - Ok(ids) - } + let encoder = FrameEncoder::new(DEFAULT_MAX_FRAME_SIZE, DEFAULT_MAX_HEADER_LIST_SIZE); + let decoder = FrameDecoder::new(); - pub(crate) fn recv_local_reset(&mut self, id: u32) -> Result<(), H2Error> { - match self.buffer.get_mut(&id) { - None => Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), - Some(sender) => { - match sender.state { - Closed(ResetReason::Remote | ResetReason::Local) => {} - _ => { - sender.state = Closed(ResetReason::Local); - } - } - Ok(()) - } - } - } + let (read, write) = crate::runtime::split(io); - pub(crate) fn recv_remote_reset(&mut self, id: u32) -> Result<(), H2Error> { - match self.buffer.get_mut(&id) { - None => Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), - Some(sender) => { - match sender.state { - Closed(ResetReason::Remote) => {} - _ => { - sender.state = Closed(ResetReason::Remote); - } - } - Ok(()) - } - } - } + let shutdown_flag = Arc::new(AtomicBool::new(false)); + let settings_sync = Arc::new(Mutex::new(SettingsSync::default())); - // TODO At present, only the state is changed to closed, and other states are - // not involved, and it needs to be added later - pub(crate) fn recv_headers(&mut self, id: u32) -> Result { - match self.buffer.get_mut(&id) { - None => Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), - Some(sender) => match sender.state { - Closed(ResetReason::Goaway(last_id)) => { - if id > last_id { - return Err(H2Error::ConnectionError(ErrorCode::StreamClosed)); - } - Ok(sender.state.clone()) - } - Closed(ResetReason::Remote) => { - Err(H2Error::ConnectionError(ErrorCode::StreamClosed)) - } - _ => Ok(sender.state.clone()), - }, - } - } + let controller = StreamController::new(streams, shutdown_flag.clone()); - pub(crate) fn recv_data(&mut self, id: u32) -> Result { - self.recv_headers(id) - } + // The id of the client stream, starting from 1 + let next_stream_id = StreamId { + id: AtomicU32::new(1), + }; - pub(crate) fn pop_front(&mut self) -> Result, H2Error> { - match self.stream_to_send.pop_front() { - None => Ok(None), - Some(id) => { - // TODO Subsequent consideration is to delete the corresponding elements in the - // map after the status becomes Closed - match self.buffer.get_mut(&id) { - None => Err(H2Error::ConnectionError(ErrorCode::IntervalError)), - Some(sender) => { - // TODO For the time being, match state is used here, and the complete - // logic should be judged based on the frame type and state - match sender.state { - Closed(ResetReason::Remote | ResetReason::Local) => Ok(None), - _ => Ok(sender.pop_front()), + let (input_tx, input_rx) = unbounded_channel(); + let (resp_tx, resp_rx) = unbounded_channel(); + let (req_tx, req_rx) = unbounded_channel(); + + match input_tx.send(settings) { + Ok(_) => { + let send_settings_sync = settings_sync.clone(); + let _send = crate::runtime::spawn(async move { + let mut writer = write; + match async_send_preface(&mut writer).await { + Ok(_) => { + let mut send = + SendData::new(encoder, send_settings_sync, writer, input_rx); + match Pin::new(&mut send).await { + Ok(_) => {} + Err(_e) => {} + } } + Err(_e) => {} } - } - } - } - } - } - - impl StreamBuffer { - pub(crate) fn push_back(&mut self, frame: Frame) { - self.frames.push_back(frame); - } - - pub(crate) fn pop_front(&mut self) -> Option { - self.frames.pop_front() - } - - pub(crate) fn new() -> Self { - Self { - state: StreamState::Idle, - frames: VecDeque::new(), - } - } - - pub(crate) fn go_away(&mut self, last_stream_id: u32) -> Result<(), H2Error> { - match self.state { - Closed(ResetReason::Local | ResetReason::Remote) => {} - Closed(ResetReason::Goaway(id)) => { - if last_stream_id > id { - return Err(H2Error::ConnectionError(ErrorCode::ProtocolError)); - } - self.state = Closed(ResetReason::Goaway(last_stream_id)); + }); + + let recv_settings_sync = settings_sync.clone(); + let _recv = crate::runtime::spawn(async move { + let mut recv = RecvData::new(decoder, recv_settings_sync, read, resp_tx); + match Pin::new(&mut recv).await { + Ok(_) => {} + Err(_e) => {} + } + }); + + let _manager = crate::runtime::spawn(async move { + let mut conn_manager = + ConnManager::new(settings_sync, input_tx, resp_rx, req_rx, controller); + match Pin::new(&mut conn_manager).await { + Ok(_) => {} + Err(e) => { + conn_manager.exit_with_error(e); + } + } + }); } - _ => { - self.state = Closed(ResetReason::Goaway(last_stream_id)); + Err(_e) => { + // Error is not possible, so it is not handled for the time + // being. } } - Ok(()) - } - } - - impl SettingsSync { - pub(crate) fn ack_settings() -> Frame { - Frame::new(0, FrameFlags::new(0x1), Settings(h2::Settings::new(vec![]))) - } - } - - pub(crate) struct ConnectionFrames { - preface: bool, - settings: SettingsSync, - } - - impl ConnectionFrames { - pub(crate) fn new(settings: h2::Settings) -> Self { - Self { - preface: true, - settings: SettingsSync::Send(settings), - } - } - } - - impl StreamWaker { - pub(crate) fn new() -> Self { - Self { - waker: HashMap::new(), - } - } - } - - impl IoManager { - pub(crate) fn new( - inner: Inner, - frame_receiver: UnboundedReceiver, - connection_frame: ConnectionFrames, - ) -> Self { - Self { - inner, - senders: HashMap::new(), - frame_receiver, - streams: Streams::new(), - frame_iter: FrameIter::default(), - connection_frame, - } - } - - fn close_frame_receiver(&mut self) { - self.frame_receiver.close() - } - } - - impl FrameIter { - pub(crate) fn is_empty(&self) -> bool { - self.iter.is_none() - } - } - - impl StreamId { - fn stream_id_generate(&self) -> u32 { - self.next_id.fetch_add(2, Ordering::Relaxed) - } - fn get_next_id(&self) -> u32 { - self.next_id.load(Ordering::Relaxed) - } - } - - impl Http2Dispatcher { - pub(crate) fn new(config: H2Config, io: S) -> Self { - // send_preface(&mut io).await?; - - let connection_frames = build_connection_frames(config); - let inner = Inner { - io, - encoder: FrameEncoder::new(DEFAULT_MAX_FRAME_SIZE, DEFAULT_MAX_HEADER_LIST_SIZE), - decoder: FrameDecoder::new(), - }; - - // For each stream to send the frame to the controller - let (tx, rx) = unbounded_channel::(); - - let stream_controller = Arc::new(StreamController::new(inner, rx, connection_frames)); - - // The id of the client stream, starting from 1 - let next_stream_id = StreamId { - next_id: AtomicU32::new(1), - }; Self { - controller: stream_controller, - sender: tx, - next_stream_id: Arc::new(next_stream_id), + next_stream_id, + sender: req_tx, + io_shutdown: shutdown_flag, + _mark: PhantomData, } } } @@ -570,886 +353,209 @@ pub(crate) mod http2 { impl Dispatcher for Http2Dispatcher { type Handle = Http2Conn; - // Call this method to get a stream fn dispatch(&self) -> Option { - let id = self.next_stream_id.stream_id_generate(); - // TODO Consider how to create a new connection and transfer state + let id = self.next_stream_id.generate_id(); if id > DEFAULT_MAX_STREAM_ID { return None; } - let controller = self.controller.clone(); let sender = self.sender.clone(); - let handle = Http2Conn::new(id, self.next_stream_id.clone(), sender, controller); + let handle = Http2Conn::new(id, self.io_shutdown.clone(), sender); Some(handle) } - // TODO When the stream id reaches the maximum value, shutdown the current - // connection fn is_shutdown(&self) -> bool { - self.controller.io_shutdown.load(Ordering::Relaxed) + self.io_shutdown.load(Ordering::Relaxed) } } impl Http2Conn { pub(crate) fn new( id: u32, - next_stream_id: Arc, - sender: UnboundedSender, - controller: Arc>, + io_shutdown: Arc, + sender: UnboundedSender, ) -> Self { - let stream_info = StreamInfo { - id, - next_stream_id, - receiver: FrameReceiver::default(), - controller, - }; Self { id, sender, - stream_info, + receiver: RespReceiver::default(), + io_shutdown, + _mark: PhantomData, } } pub(crate) fn send_frame_to_controller( &mut self, - frame: Frame, + request: RequestWrapper, ) -> Result<(), HttpClientError> { - if self.stream_info.receiver.is_none() { - let (tx, rx) = unbounded_channel::(); - self.stream_info.receiver.set_receiver(rx); - self.sender.send((Some((self.id, tx)), frame)).map_err(|_| { - HttpClientError::from_error(ErrorKind::Request, String::from("resend")) + let (tx, rx) = unbounded_channel::(); + self.receiver.set_receiver(rx); + self.sender + .send(ReqMessage { + id: self.id, + sender: tx, + request, }) - } else { - self.sender.send((None, frame)).map_err(|_| { - HttpClientError::from_error(ErrorKind::Request, String::from("resend")) + .map_err(|_| { + HttpClientError::from_str(ErrorKind::Request, "Request Sender Closed !") }) - } } } - impl Future for StreamInfo { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let stream_info = self.get_mut(); - - // First, check whether the frame of the current stream is in the Channel. - // The error cannot occur. Therefore, the error is thrown directly without - // connection-level processing. - if let Some(frame) = stream_info.receiver.recv_frame(stream_info.id)? { - { - let mut stream_waker = stream_info - .controller - .stream_waker - .lock() - .expect("Blocking get waker lock failed! "); - // - wakeup_next_stream(&mut stream_waker.waker); - } - return Poll::Ready(Ok(frame)); - } - - // If the dispatcher sends a goaway frame, all streams on the current connection - // are unavailable. - if stream_info - .controller - .dispatcher_invalid - .load(Ordering::Relaxed) - { - return Poll::Ready(Err(H2Error::ConnectionError(ErrorCode::ConnectError).into())); - } - - // The error cannot occur. Therefore, the error is thrown directly without - // connection-level processing. - if is_io_available(&stream_info.controller.occupied, stream_info.id)? { - { - // Second, try to get io and read the frame of the current stream from io. - if let Ok(mut io_manager) = stream_info.controller.manager.try_lock() { - if stream_info - .poll_match_result(cx, &mut io_manager)? - .is_pending() - { - return Poll::Pending; - } - } - } - { - let mut stream_waker = stream_info - .controller - .stream_waker - .lock() - .expect("Blocking get waker lock failed! "); - wakeup_next_stream(&mut stream_waker.waker); - } - // The error cannot occur. Therefore, the error is thrown directly without - // connection-level processing. - let frame_opt = get_frame(stream_info.receiver.recv_frame(stream_info.id)?); - return Poll::Ready(frame_opt); - } - - { - let mut io_manager = { - // Third, wait to acquire the lock of waker, which is used to insert the current - // waker, and wait to be awakened by the io stream. - let mut stream_waker = stream_info - .controller - .stream_waker - .lock() - .expect("Blocking get waker lock failed! "); - - // Fourth, after obtaining the waker lock, - // you need to check the Receiver again to prevent the Receiver from receiving a - // frame while waiting for the waker. The error cannot - // occur. Therefore, the error is thrown directly without connection-level - // processing. - if let Some(frame) = stream_info.receiver.recv_frame(stream_info.id)? { - wakeup_next_stream(&mut stream_waker.waker); - return Poll::Ready(Ok(frame)); - } - - // The error cannot occur. Therefore, the error is thrown directly without - // connection-level processing. - if is_io_available(&stream_info.controller.occupied, stream_info.id)? { - // Fifth, get io again to prevent no other streams from controlling io while - // waiting for the waker, leaving only the current - // stream. - match stream_info.controller.manager.try_lock() { - Ok(guard) => guard, - _ => { - stream_waker - .waker - .insert(stream_info.id, cx.waker().clone()); - return Poll::Pending; - } - } - } else { - stream_waker - .waker - .insert(stream_info.id, cx.waker().clone()); - return Poll::Pending; - } - }; - if stream_info - .poll_match_result(cx, &mut io_manager)? - .is_pending() - { - return Poll::Pending; - } - } - { - { - let mut stream_waker = stream_info - .controller - .stream_waker - .lock() - .expect("Blocking get waker lock failed! "); - wakeup_next_stream(&mut stream_waker.waker); - } - // The error cannot occur. Therefore, the error is thrown directly without - // connection-level processing. - let frame_opt = get_frame(stream_info.receiver.recv_frame(stream_info.id)?); - Poll::Ready(frame_opt) - } + impl StreamId { + fn generate_id(&self) -> u32 { + self.id.fetch_add(2, Ordering::Relaxed) } } - impl StreamInfo { - fn poll_match_result( - &self, - cx: &mut Context<'_>, - io_manager: &mut MutexGuard>, - ) -> Poll> { - loop { - match self.poll_io(cx, io_manager) { - Poll::Ready(Ok(_)) => { - return Poll::Ready(Ok(())); - } - Poll::Ready(Err(h2_error)) => { - match h2_error { - H2Error::StreamError(id, code) => { - let rest_payload = RstStream::new(code.clone().into_code()); - let frame = Frame::new( - id as usize, - FrameFlags::empty(), - Payload::RstStream(rest_payload), - ); - io_manager.streams.recv_local_reset(id)?; - if self - .poll_send_reset(cx, frame.clone(), io_manager)? - .is_pending() - { - compare_exchange_occupation( - &self.controller.occupied, - 0, - self.id, - )?; - return Poll::Pending; - } - if self.id == id { - return Poll::Ready(Err(H2Error::StreamError(id, code).into())); - } else { - self.controller_send_frame_to_stream(id, frame, io_manager); - { - let mut stream_waker = self - .controller - .stream_waker - .lock() - .expect("Blocking get waker lock failed! "); - // TODO Is there a situation where the result has been - // returned, but the waker has not been inserted into the - // map? how to deal with. - if let Some(waker) = stream_waker.waker.remove(&id) { - waker.wake(); - } - } - } - } - H2Error::ConnectionError(code) => { - io_manager.close_frame_receiver(); - self.controller.shutdown(); - // Since ConnectError may be caused by an io error, so when the - // client actively sends a goaway - // frame, all streams are shut down and no streams are allowed to - // complete. TODO Then consider - // separating io errors from frame errors to allow streams whose - // stream id is less than last_stream_id to continue - self.controller.invalid(); - // last_stream_id is set to 0 to ensure that all streams are - // shutdown. - let goaway_payload = - Goaway::new(code.clone().into_code(), 0, vec![]); - let frame = Frame::new( - 0, - FrameFlags::empty(), - Payload::Goaway(goaway_payload), - ); - // io_manager.connection_frame.going_away(frame); - if self - .poll_send_go_away(cx, frame.clone(), io_manager)? - .is_pending() - { - compare_exchange_occupation( - &self.controller.occupied, - 0, - self.id, - )?; - return Poll::Pending; - } - - self.goaway_unsent_stream(io_manager, 0, frame)?; - self.goaway_and_shutdown(); - return Poll::Ready(Err(H2Error::ConnectionError(code).into())); - } - } - } - Poll::Pending => { - compare_exchange_occupation(&self.controller.occupied, 0, self.id)?; - return Poll::Pending; - } - } + impl StreamController { + pub(crate) fn new(streams: Streams, shutdown: Arc) -> Self { + Self { + io_shutdown: shutdown, + senders: HashMap::new(), + streams, + go_away: None, + go_away_sync: GoAwaySync::default(), } } - fn poll_io( - &self, - cx: &mut Context<'_>, - io_manager: &mut MutexGuard>, - ) -> Poll> { - if self.poll_send_preface(cx, io_manager)?.is_pending() { - return Poll::Pending; - } - if self.poll_send_settings(cx, io_manager)?.is_pending() { - return Poll::Pending; - } - match self.poll_dispatch_frame(cx, io_manager)? { - Poll::Ready(state) => { - if let DispatchState::Partial = state { - return Poll::Ready(Ok(())); - } - } - Poll::Pending => { - return Poll::Pending; - } - } - // Write and read frames to io in a loop until the frame of the current stream - // is read and exit the loop. - loop { - if self.poll_write_frame(cx, io_manager)?.is_pending() { - return Poll::Pending; - } - match self.poll_read_frame(cx, io_manager)? { - Poll::Ready(ReadState::EmptyIo) => {} - Poll::Ready(ReadState::CurrentStream) => { - return Poll::Ready(Ok(())); - } - Poll::Pending => { - return Poll::Pending; - } - } - } + pub(crate) fn shutdown(&self) { + self.io_shutdown.store(true, Ordering::Release); } - fn poll_dispatch_frame( - &self, - cx: &mut Context<'_>, - io_manager: &mut MutexGuard>, - ) -> Poll> { - if io_manager.frame_iter.is_empty() { - return Poll::Ready(Ok(DispatchState::Finish)); - } - let iter_option = take(&mut io_manager.frame_iter.iter); - match iter_option { - None => Poll::Ready(Err(H2Error::ConnectionError(ErrorCode::IntervalError))), - Some(iter) => self.dispatch_read_frames(cx, io_manager, iter), + pub(crate) fn go_away_unsent_stream( + &mut self, + last_stream_id: u32, + ) -> Result, H2Error> { + // The last-stream-id in the subsequent GO_AWAY frame + // cannot be greater than the last-stream-id in the previous GO_AWAY frame. + if self.streams.max_send_id < last_stream_id { + return Err(H2Error::ConnectionError(ErrorCode::ProtocolError)); } + self.streams.max_send_id = last_stream_id; + Ok(self.streams.get_go_away_streams(last_stream_id)) } - fn poll_send_preface( - &self, - cx: &mut Context<'_>, - io_manager: &mut MutexGuard>, - ) -> Poll> { - const PREFACE_MSG: &str = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; - if io_manager.connection_frame.preface { - let mut buf = [0u8; PREFACE_MSG.len()]; - buf.copy_from_slice(PREFACE_MSG.as_bytes()); - - let mut start_index = 0; - loop { - if start_index == PREFACE_MSG.len() { - io_manager.connection_frame.preface = false; - break; - } - match Pin::new(&mut io_manager.inner.io) - .poll_write(cx, &buf[start_index..]) - .map_err(|_| H2Error::ConnectionError(ErrorCode::IntervalError))? - { - Poll::Ready(written) => { - start_index += written; - } - Poll::Pending => { - return Poll::Pending; - } + pub(crate) fn send_message_to_stream(&mut self, stream_id: u32, message: RespMessage) { + if let Some(sender) = self.senders.get(&stream_id) { + // If the client coroutine has exited, this frame is skipped. + match sender.send(message) { + Ok(_) => {} + Err(_e) => { + self.senders.remove(&stream_id); } } - return poll_flush_io(cx, &mut io_manager.inner); - } - Poll::Ready(Ok(())) - } - - fn poll_send_go_away( - &self, - cx: &mut Context<'_>, - goaway: Frame, - io_manager: &mut MutexGuard>, - ) -> Poll> { - let mut buf = [0u8; 1024]; - if write_frame_to_io(cx, &mut buf, goaway, &mut io_manager.inner)?.is_pending() { - Poll::Pending - } else { - poll_flush_io(cx, &mut io_manager.inner) - } - } - - fn poll_send_reset( - &self, - cx: &mut Context<'_>, - reset: Frame, - io_manager: &mut MutexGuard>, - ) -> Poll> { - let mut buf = [0u8; 1024]; - if write_frame_to_io(cx, &mut buf, reset, &mut io_manager.inner)?.is_pending() { - Poll::Pending - } else { - poll_flush_io(cx, &mut io_manager.inner) - } - } - - fn poll_send_settings( - &self, - cx: &mut Context<'_>, - io_manager: &mut MutexGuard>, - ) -> Poll> { - if let SettingsSync::Send(settings) = io_manager.connection_frame.settings.clone() { - let mut buf = [0u8; 1024]; - let frame = Frame::new(0, FrameFlags::empty(), Settings(settings.clone())); - if write_frame_to_io(cx, &mut buf, frame, &mut io_manager.inner)?.is_pending() { - Poll::Pending - } else { - io_manager.connection_frame.settings = SettingsSync::Acknowledging(settings); - poll_flush_io(cx, &mut io_manager.inner) - } - } else { - Poll::Ready(Ok(())) } } + } - fn poll_write_frame( - &self, - cx: &mut Context<'_>, - io_manager: &mut MutexGuard>, - ) -> Poll> { - const FRAME_WRITE_NUM: usize = 10; - - // Send 10 frames each time, if there is not enough in the queue, read enough - // from mpsc::Receiver - while io_manager.streams.size() < FRAME_WRITE_NUM { - match io_manager.frame_receiver.try_recv() { - // The Frame sent by the Handle for the first time will carry a Sender at the - // same time, which is used to send the Response Frame back - // to the Handle - Ok((Some((id, sender)), frame)) => { - if io_manager.senders.insert(id, sender).is_some() { - return Poll::Ready(Err(H2Error::ConnectionError( - ErrorCode::IntervalError, - ))); - } - io_manager.streams.insert(frame); - } - Ok((None, frame)) => { - io_manager.streams.insert(frame); - } - Err(TryRecvError::Empty) => { - break; - } - Err(TryRecvError::Disconnected) => { - return Poll::Ready(Err(H2Error::ConnectionError(ErrorCode::ConnectError))) - } - } - } - let mut buf = [0u8; 1024]; - for _i in 0..FRAME_WRITE_NUM { - match io_manager.streams.pop_front()? { - Some(frame) => { - if write_frame_to_io(cx, &mut buf, frame, &mut io_manager.inner)? - .is_pending() - { - return Poll::Pending; - } - } - None => { - break; - } - } - } - poll_flush_io(cx, &mut io_manager.inner) + impl RespReceiver { + pub(crate) fn set_receiver(&mut self, receiver: UnboundedReceiver) { + self.receiver = Some(receiver); } - fn poll_read_frame( - &self, - cx: &mut Context<'_>, - io_manager: &mut MutexGuard>, - ) -> Poll> { - // Read all the frames in io until the frame of the current stream is read and - // stop. - let mut buf = [0u8; 1024]; - loop { - let mut read_buf = ReadBuf::new(&mut buf); - match Pin::new(&mut io_manager.inner.io).poll_read(cx, &mut read_buf) { - Poll::Ready(Err(_)) => { - return Poll::Ready(Err(H2Error::ConnectionError(ErrorCode::ConnectError))) - } - Poll::Pending => { - return Poll::Pending; + pub(crate) async fn recv(&mut self) -> Result { + match self.receiver { + Some(ref mut receiver) => { + #[cfg(feature = "tokio_base")] + match receiver.recv().await { + None => err_from_msg!(Request, "Response Receiver Closed !"), + Some(message) => match message { + RespMessage::Output(frame) => Ok(frame), + RespMessage::OutputExit(_e) => { + err_from_msg!(Request, "Response Recv Failed !") + } + }, } - _ => {} - } - let read = read_buf.filled().len(); - if read == 0 { - break; - } - let frames = io_manager.inner.decoder.decode(&buf[..read])?; - let frame_iterator = frames.into_iter(); - match self.dispatch_read_frames(cx, io_manager, frame_iterator)? { - Poll::Ready(state) => { - if let DispatchState::Partial = state { - return Poll::Ready(Ok(ReadState::CurrentStream)); - } - } - Poll::Pending => { - return Poll::Pending; + #[cfg(feature = "ylong_base")] + match receiver.recv().await { + Err(err) => Err(HttpClientError::from_error(ErrorKind::Request, err)), + Ok(message) => match message { + RespMessage::Output(frame) => Ok(frame), + RespMessage::OutputExit(_e) => { + err_from_msg!(Request, "Response Recv Failed !") + } + }, } } + None => Err(HttpClientError::from_str( + ErrorKind::Request, + "Invalid Frame Receiver !", + )), } - Poll::Ready(Ok(ReadState::EmptyIo)) } - fn dispatch_read_frames( - &self, + pub(crate) fn poll_recv( + &mut self, cx: &mut Context<'_>, - io_manager: &mut MutexGuard>, - mut frame_iterator: FramesIntoIter, - ) -> Poll> { - let mut meet_this = false; - loop { - match frame_iterator.next() { - None => break, - Some(frame_kind) => { - if let FrameKind::Complete(frame) = frame_kind { - match frame.payload() { - Settings(settings) => { - if self - .recv_settings_frame( - cx, - io_manager, - frame.flags().is_ack(), - settings, - )? - .is_pending() - { - return Poll::Pending; - } - continue; - } - Payload::Ping(ping) => { - if self - .recv_ping_frame( - cx, - io_manager, - frame.flags().is_ack(), - ping, - )? - .is_pending() - { - return Poll::Pending; - } - continue; - } - Payload::PushPromise(_) => { - // TODO The current settings_enable_push is fixed to false - return Poll::Ready(Err(H2Error::ConnectionError( - ErrorCode::ProtocolError, - ))); - } - Payload::Goaway(goaway) => { - // shutdown io,prevent the creation of new stream - self.controller.shutdown(); - io_manager.close_frame_receiver(); - let last_stream_id = goaway.get_last_stream_id(); - if self.next_stream_id.get_next_id() as usize <= last_stream_id - { - return Poll::Ready(Err(H2Error::ConnectionError( - ErrorCode::ProtocolError, - ))); - } - self.goaway_unsent_stream( - io_manager, - last_stream_id as u32, - frame.clone(), - )?; - continue; - } - Payload::RstStream(_reset) => { - io_manager - .streams - .recv_remote_reset(frame.stream_id() as u32)?; - } - Payload::Headers(_headers) => { - if let Closed(ResetReason::Local) = - io_manager.streams.recv_headers(frame.stream_id() as u32)? - { - continue; - } - } - Payload::Data(_data) => { - if let Closed(ResetReason::Local) = - io_manager.streams.recv_data(frame.stream_id() as u32)? - { - continue; - } - } - // TODO Windows that processes streams and connections separately. - Payload::WindowUpdate(_windows) => { - continue; - } - Payload::Priority(_priority) => continue, - } - - let stream_id = frame.stream_id() as u32; - if stream_id == self.id { - meet_this = true; - self.controller_send_frame_to_stream(stream_id, frame, io_manager); - break; - } else { - self.controller_send_frame_to_stream(stream_id, frame, io_manager); - // TODO After adding frames such as Reset/Priority, there may be - // problems with the following logic, because the lack of waker - // cannot wake up - let mut stream_waker = self - .controller - .stream_waker - .lock() - .expect("Blocking get waker lock failed! "); - // TODO Is there a situation where the result has been returned, but - // the waker has not been inserted into the map? how to deal with. - if let Some(waker) = stream_waker.waker.remove(&stream_id) { - waker.wake(); - } - } + ) -> Poll> { + if let Some(ref mut receiver) = self.receiver { + #[cfg(feature = "tokio_base")] + match receiver.poll_recv(cx) { + Poll::Ready(None) => { + Poll::Ready(err_from_msg!(Request, "Error receive response !")) + } + Poll::Ready(Some(message)) => match message { + RespMessage::Output(frame) => Poll::Ready(Ok(frame)), + RespMessage::OutputExit(_e) => { + Poll::Ready(err_from_msg!(Request, "H2 io manager occurs error !")) } - } + }, + Poll::Pending => Poll::Pending, } - } - - if meet_this { - io_manager.frame_iter.iter = Some(frame_iterator); - Poll::Ready(Ok(DispatchState::Partial)) - } else { - Poll::Ready(Ok(DispatchState::Finish)) - } - } - fn goaway_unsent_stream( - &self, - io_manager: &mut MutexGuard>, - last_stream_id: u32, - goaway: Frame, - ) -> Result<(), H2Error> { - let goaway_streams = io_manager.streams.get_goaway_streams(last_stream_id)?; - { - let mut stream_waker = self - .controller - .stream_waker - .lock() - .expect("Blocking get waker lock failed! "); - for goaway_stream in goaway_streams { - self.controller_send_frame_to_stream(goaway_stream, goaway.clone(), io_manager); - if let Some(waker) = stream_waker.waker.remove(&goaway_stream) { - waker.wake(); + #[cfg(feature = "ylong_base")] + match receiver.poll_recv(cx) { + Poll::Ready(Err(e)) => { + Poll::Ready(Err(HttpClientError::from_error(ErrorKind::Request, e))) } - } - } - Ok(()) - } - - fn goaway_and_shutdown(&self) { - { - let mut waker_guard = self - .controller - .stream_waker - .lock() - .expect("Blocking get waker lock failed! "); - let waker_map = take(&mut waker_guard.waker); - for (_id, waker) in waker_map.into_iter() { - waker.wake() - } - } - } - - fn recv_settings_frame( - &self, - cx: &mut Context<'_>, - guard: &mut MutexGuard>, - is_ack: bool, - settings: &h2::Settings, - ) -> Poll> { - if is_ack { - match guard.connection_frame.settings.clone() { - SettingsSync::Acknowledging(local_settings) => { - for setting in local_settings.get_settings() { - if let Setting::MaxHeaderListSize(size) = setting { - guard.inner.decoder.set_max_header_list_size(*size as usize); - } - if let Setting::MaxFrameSize(size) = setting { - guard.inner.decoder.set_max_frame_size(*size)?; - } + Poll::Ready(Ok(message)) => match message { + RespMessage::Output(frame) => Poll::Ready(Ok(frame)), + RespMessage::OutputExit(_e) => { + Poll::Ready(err_from_msg!(Request, "H2 io manager occurs error !")) } - guard.connection_frame.settings = SettingsSync::Synced; - Poll::Ready(Ok(())) - } - _ => Poll::Ready(Err(H2Error::ConnectionError(ErrorCode::ProtocolError))), - } - } else { - for setting in settings.get_settings() { - if let Setting::HeaderTableSize(size) = setting { - guard.inner.encoder.update_header_table_size(*size as usize); - } - if let Setting::MaxFrameSize(size) = setting { - guard.inner.encoder.update_max_frame_size(*size as usize); - } - } - // reply ack Settings - let mut buf = [0u8; 1024]; - if write_frame_to_io(cx, &mut buf, SettingsSync::ack_settings(), &mut guard.inner)? - .is_pending() - { - Poll::Pending - } else { - poll_flush_io(cx, &mut guard.inner) + }, + Poll::Pending => Poll::Pending, } - } - } - - fn recv_ping_frame( - &self, - cx: &mut Context<'_>, - guard: &mut MutexGuard>, - is_ack: bool, - ping: &h2::Ping, - ) -> Poll> { - if is_ack { - // TODO The sending logic of ping has not been implemented yet, so there is no - // processing for ack - Poll::Ready(Ok(())) } else { - // reply ack Settings - let ack = Frame::new(0, FrameFlags::new(0x1), Payload::Ping(ping.clone())); - let mut buf = [0u8; 1024]; - if write_frame_to_io(cx, &mut buf, ack, &mut guard.inner)?.is_pending() { - Poll::Pending - } else { - poll_flush_io(cx, &mut guard.inner) - } - } - } - - fn controller_send_frame_to_stream( - &self, - stream_id: u32, - frame: Frame, - guard: &mut MutexGuard>, - ) { - // TODO Need to consider when to delete useless Sender after support reset - // stream - if let Some(sender) = guard.senders.get(&stream_id) { - // If the client coroutine has exited, this frame is skipped. - let _ = sender.send(frame); + Poll::Ready(err_from_msg!(Request, "Invalid Frame Receiver !")) } } } - impl FrameReceiver { - fn set_receiver(&mut self, receiver: UnboundedReceiver) { - self.receiver = Some(receiver); - } - - fn recv_frame(&mut self, id: u32) -> Result, HttpError> { - if let Some(ref mut receiver) = self.receiver { - match receiver.try_recv() { - Ok(frame) => Ok(Some(frame)), - Err(TryRecvError::Disconnected) => { - Err(H2Error::StreamError(id, ErrorCode::StreamClosed).into()) - } - Err(TryRecvError::Empty) => Ok(None), - } - } else { - Err(H2Error::StreamError(id, ErrorCode::IntervalError).into()) - } - } - - fn is_none(&self) -> bool { - self.receiver.is_none() - } + async fn async_send_preface(writer: &mut WriteHalf) -> Result<(), DispatchErrorKind> + where + S: AsyncWrite + Unpin, + { + const PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + writer + .write_all(PREFACE) + .await + .map_err(|e| DispatchErrorKind::Io(e.kind())) } - // TODO Temporarily only deal with the Settings frame - pub(crate) fn build_connection_frames(config: H2Config) -> ConnectionFrames { - const DEFAULT_ENABLE_PUSH: bool = false; + pub(crate) fn create_initial_settings(config: &H2Config) -> Frame { let settings = SettingsBuilder::new() .max_header_list_size(config.max_header_list_size()) .max_frame_size(config.max_frame_size()) .header_table_size(config.header_table_size()) - .enable_push(DEFAULT_ENABLE_PUSH) + .enable_push(config.enable_push()) + .initial_window_size(config.stream_window_size()) .build(); - ConnectionFrames::new(settings) - } - - // io write interface - fn write_frame_to_io( - cx: &mut Context<'_>, - buf: &mut [u8], - frame: Frame, - inner: &mut Inner, - ) -> Poll> - where - S: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static, - { - let mut remain_size = 0; - inner.encoder.set_frame(frame); - loop { - let size = inner - .encoder - .encode(&mut buf[remain_size..]) - .map_err(|_| H2Error::ConnectionError(ErrorCode::IntervalError))?; - - let total = size + remain_size; - - // All the bytes of the frame are written - if total == 0 { - break; - } - match Pin::new(&mut inner.io) - .poll_write(cx, &buf[..total]) - .map_err(|_| H2Error::ConnectionError(ErrorCode::IntervalError))? - { - Poll::Ready(written) => { - remain_size = total - written; - // written is not necessarily equal to total - if remain_size > 0 { - for i in 0..remain_size { - buf[i] = buf[written + i]; - } - } - } - Poll::Pending => { - return Poll::Pending; - } - } - } - Poll::Ready(Ok(())) + Frame::new(0, FrameFlags::new(0), Payload::Settings(settings)) } - fn poll_flush_io(cx: &mut Context<'_>, inner: &mut Inner) -> Poll> - where - S: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static, - { - Pin::new(&mut inner.io) - .poll_flush(cx) - .map_err(|_| H2Error::ConnectionError(ErrorCode::ConnectError)) - } - - fn get_frame(frame: Option) -> Result { - frame.ok_or(H2Error::ConnectionError(ErrorCode::IntervalError).into()) - } - - fn wakeup_next_stream(waker_map: &mut HashMap) { - { - if !waker_map.is_empty() { - let mut id = 0; - if let Some((index, _)) = waker_map.iter().next() { - id = *index; - } - if let Some(waker) = waker_map.remove(&id) { - waker.wake(); - } - } + impl From for DispatchErrorKind { + fn from(value: std::io::Error) -> Self { + DispatchErrorKind::Io(value.kind()) } } - fn is_io_available(occupied: &AtomicU32, id: u32) -> Result { - let is_occupied = occupied.load(Ordering::Relaxed); - if is_occupied == 0 { - return Ok(true); - } - if is_occupied == id { - compare_exchange_occupation(occupied, id, 0)?; - return Ok(true); + impl From for DispatchErrorKind { + fn from(err: H2Error) -> Self { + DispatchErrorKind::H2(err) } - Ok(false) - } - - fn compare_exchange_occupation( - occupied: &AtomicU32, - current: u32, - new: u32, - ) -> Result<(), HttpError> { - occupied - .compare_exchange(current, new, Ordering::Acquire, Ordering::Relaxed) - .map_err(|_| H2Error::ConnectionError(ErrorCode::IntervalError))?; - Ok(()) } } diff --git a/ylong_http_client/src/util/h2/buffer/mod.rs b/ylong_http_client/src/util/h2/buffer/mod.rs new file mode 100644 index 0000000..c06edc2 --- /dev/null +++ b/ylong_http_client/src/util/h2/buffer/mod.rs @@ -0,0 +1,27 @@ +// Copyright (c) 2023 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. + +//! Http2 Flow Control module. +//! +//! A module that provides Flow-Control capability for the HTTP2 protocol. +//! +//! -[`FlowControl`] is used to Provides connection-level Flow-Control +//! capability. -[`RecvWindow`] is the structure of the client response +//! receiving window. -[`SendWindow`] is the structure of the client request +//! sending window. + +mod settings; +mod window; + +pub(crate) use settings::FlowControl; +pub(crate) use window::{RecvWindow, SendWindow}; diff --git a/ylong_http_client/src/util/h2/buffer/settings.rs b/ylong_http_client/src/util/h2/buffer/settings.rs new file mode 100644 index 0000000..d6b4124 --- /dev/null +++ b/ylong_http_client/src/util/h2/buffer/settings.rs @@ -0,0 +1,69 @@ +// Copyright (c) 2023 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. + +//! http2 connection flow control. + +use ylong_http::h2::{Frame, H2Error}; + +use crate::util::h2::buffer::window::RecvWindow; +use crate::util::h2::buffer::SendWindow; + +pub(crate) struct FlowControl { + recv_window: RecvWindow, + send_window: SendWindow, +} + +impl FlowControl { + pub(crate) fn new(conn_recv_window: u32, conn_send_window: u32) -> Self { + FlowControl { + recv_window: RecvWindow::new(conn_recv_window as i32), + send_window: SendWindow::new(conn_send_window as i32), + } + } + + pub(crate) fn check_conn_recv_window_update(&mut self) -> Option { + self.recv_window.check_window_update(0) + } + + pub(crate) fn setup_recv_window(&mut self, size: u32) { + let setup = size; + let actual = self.recv_window.actual_size() as u32; + if setup > actual { + let extra = setup - actual; + self.recv_window.increase_actual(extra); + } else { + let extra = actual - setup; + self.recv_window.reduce_actual(extra); + } + } + + pub(crate) fn increase_send_size(&mut self, size: u32) -> Result<(), H2Error> { + self.send_window.increase_size(size) + } + + pub(crate) fn send_size_available(&self) -> usize { + self.send_window.size_available() as usize + } + + pub(crate) fn recv_notification_size_available(&self) -> u32 { + self.recv_window.notification_available() + } + + pub(crate) fn send_data(&mut self, size: u32) { + self.send_window.send_data(size) + } + + pub(crate) fn recv_data(&mut self, size: u32) { + self.recv_window.recv_data(size) + } +} diff --git a/ylong_http_client/src/util/h2/buffer/window.rs b/ylong_http_client/src/util/h2/buffer/window.rs new file mode 100644 index 0000000..536fc3a --- /dev/null +++ b/ylong_http_client/src/util/h2/buffer/window.rs @@ -0,0 +1,139 @@ +// Copyright (c) 2023 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. + +//! http2 send and recv window definition. + +use ylong_http::h2::{ErrorCode, Frame, FrameFlags, H2Error, Payload, WindowUpdate}; + +pub(crate) struct SendWindow { + // As the sending window, the client retains only its visible window size, + // and updates only when the SETTINGS frame and WINDOW_UPDATE frame are received from the + // server. + size: i32, +} + +impl SendWindow { + pub(crate) fn new(size: i32) -> Self { + Self { size } + } + + pub(crate) fn size_available(&self) -> u32 { + if self.size < 0 { + 0 + } else { + self.size as u32 + } + } + + pub(crate) fn reduce_size(&mut self, size: u32) { + self.size -= size as i32; + } + + pub(crate) fn increase_size(&mut self, size: u32) -> Result<(), H2Error> { + let (curr, overflow) = self.size.overflowing_add(size as i32); + if overflow { + return Err(H2Error::ConnectionError(ErrorCode::FlowControlError)); + } + if curr > crate::util::h2::MAX_FLOW_CONTROL_WINDOW as i32 { + return Err(H2Error::ConnectionError(ErrorCode::FlowControlError)); + } + self.size = curr; + Ok(()) + } + + pub(crate) fn send_data(&mut self, size: u32) { + self.size -= size as i32; + } +} + +#[derive(Default)] +pub(crate) struct RecvWindow { + // The window size visible to the server. + // notification decreases the value when a DATA frame is received + // and increases the value when a WINDOW_UPDATE is sent. + notification: i32, + // The window size visible to the client. + // Since client is a receiving (WINDOW_UPDATE sending) window, + // the actual remains unchanged except for SETTINGS set by the user updates. + actual: i32, +} + +impl RecvWindow { + pub(crate) fn new(size: i32) -> Self { + Self { + notification: size, + actual: size, + } + } + + pub(crate) fn unreleased_size(&self) -> Option { + let unreleased = self.actual - self.notification; + if unreleased <= 0 { + return None; + } + if unreleased * 2 > self.notification { + Some(unreleased as u32) + } else { + None + } + } + + pub(crate) fn actual_size(&self) -> i32 { + self.actual + } + + pub(crate) fn notification_available(&self) -> u32 { + if self.notification < 0 { + 0 + } else { + self.notification as u32 + } + } + + pub(crate) fn reduce_actual(&mut self, size: u32) { + self.actual -= size as i32 + } + + pub(crate) fn increase_actual(&mut self, size: u32) { + self.actual += size as i32 + } + + pub(crate) fn reduce_notification(&mut self, size: u32) { + self.notification -= size as i32 + } + + pub(crate) fn increase_notification(&mut self, size: u32) { + self.notification += size as i32 + } + + pub(crate) fn check_window_update(&mut self, id: u32) -> Option { + if let Some(size) = self.unreleased_size() { + self.increase_notification(size); + let window_update = WindowUpdate::new(size); + let frame = Frame::new( + id as usize, + FrameFlags::new(0), + Payload::WindowUpdate(window_update), + ); + Some(frame) + } else { + None + } + } + + // The client receiving a DATA frame means that the server has less visible + // Windows + pub(crate) fn recv_data(&mut self, size: u32) { + self.notification -= size as i32; + } +} diff --git a/ylong_http_client/src/util/h2/data_ref.rs b/ylong_http_client/src/util/h2/data_ref.rs new file mode 100644 index 0000000..9e36f47 --- /dev/null +++ b/ylong_http_client/src/util/h2/data_ref.rs @@ -0,0 +1,60 @@ +// Copyright (c) 2023 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. + +//! defines `BodyDataRef`. + +use std::pin::Pin; +use std::task::{Context, Poll}; + +use ylong_http::h2::{ErrorCode, H2Error}; + +use crate::runtime::{AsyncRead, ReadBuf}; +use crate::util::request::RequestArc; + +pub(crate) struct BodyDataRef { + body: Option, +} + +impl BodyDataRef { + pub(crate) fn new(request: RequestArc) -> Self { + Self { + body: Some(request), + } + } + + pub(crate) fn clear(&mut self) { + self.body = None; + } + + pub(crate) fn poll_read( + &mut self, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + match self.body { + None => Poll::Ready(Ok(0)), + Some(ref mut request) => { + let data = request.ref_mut().body_mut(); + let mut read_buf = ReadBuf::new(buf); + let data = Pin::new(data); + match data.poll_read(cx, &mut read_buf) { + Poll::Ready(Err(_e)) => { + Poll::Ready(Err(H2Error::ConnectionError(ErrorCode::IntervalError))) + } + Poll::Ready(Ok(_)) => Poll::Ready(Ok(read_buf.filled().len())), + Poll::Pending => Poll::Pending, + } + } + } + } +} diff --git a/ylong_http_client/src/util/h2/input.rs b/ylong_http_client/src/util/h2/input.rs new file mode 100644 index 0000000..46119f1 --- /dev/null +++ b/ylong_http_client/src/util/h2/input.rs @@ -0,0 +1,223 @@ +// Copyright (c) 2023 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. + +//! Frame send coroutine. + +use std::future::Future; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll}; + +use ylong_http::h2::{ErrorCode, Frame, FrameEncoder, H2Error, Payload, Setting, Settings}; + +use crate::runtime::{AsyncWrite, UnboundedReceiver, WriteHalf}; +use crate::util::dispatcher::http2::{DispatchErrorKind, SettingsState, SettingsSync}; + +pub(crate) struct SendData { + encoder: FrameEncoder, + settings: Arc>, + writer: WriteHalf, + req_rx: UnboundedReceiver, + state: InputState, + buf: WriteBuf, +} + +enum InputState { + RecvFrame, + WriteFrame, +} + +enum SettingState { + Not, + Local(Settings), + Ack, +} + +pub(crate) struct WriteBuf { + buf: [u8; 1024], + end: usize, + start: usize, + empty: bool, +} + +impl WriteBuf { + pub(crate) fn new() -> Self { + Self { + buf: [0; 1024], + end: 0, + start: 0, + empty: true, + } + } + pub(crate) fn clear(&mut self) { + self.start = 0; + self.end = 0; + self.empty = true; + } +} + +impl Future for SendData { + type Output = Result<(), DispatchErrorKind>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let sender = self.get_mut(); + loop { + match sender.state { + InputState::RecvFrame => { + let frame = match sender.poll_recv_frame(cx) { + Poll::Ready(Ok(frame)) => frame, + Poll::Ready(Err(e)) => { + // Errors in the Frame Writer are thrown directly to exit the coroutine. + return Poll::Ready(Err(e)); + } + Poll::Pending => return Poll::Pending, + }; + + let state = sender.update_settings(&frame); + + if let SettingState::Local(setting) = &state { + let mut sync = sender.settings.lock().unwrap(); + sync.settings = SettingsState::Acknowledging(setting.clone()); + } + + let frame = if let SettingState::Ack = state { + Settings::ack() + } else { + frame + }; + sender.encoder.set_frame(frame); + sender.state = InputState::WriteFrame; + } + InputState::WriteFrame => { + match sender.poll_writer_frame(cx) { + Poll::Ready(_) => {} + Poll::Pending => return Poll::Pending, + }; + sender.state = InputState::RecvFrame; + } + } + } + } +} + +impl SendData { + pub(crate) fn new( + encoder: FrameEncoder, + settings: Arc>, + writer: WriteHalf, + req_rx: UnboundedReceiver, + ) -> Self { + Self { + encoder, + settings, + writer, + req_rx, + state: InputState::RecvFrame, + buf: WriteBuf::new(), + } + } + + // io write interface + fn poll_writer_frame(&mut self, cx: &mut Context<'_>) -> Poll> { + if !self.buf.empty { + loop { + match Pin::new(&mut self.writer) + .poll_write(cx, &self.buf.buf[self.buf.start..self.buf.end]) + .map_err(|e| DispatchErrorKind::Io(e.kind()))? + { + Poll::Ready(written) => { + self.buf.start += written; + if self.buf.start == self.buf.end { + self.buf.clear(); + break; + } + } + Poll::Pending => { + return Poll::Pending; + } + } + } + } + + loop { + let size = self.encoder.encode(&mut self.buf.buf).map_err(|_| { + DispatchErrorKind::H2(H2Error::ConnectionError(ErrorCode::IntervalError)) + })?; + + if size == 0 { + break; + } + let mut index = 0; + + loop { + match Pin::new(&mut self.writer) + .poll_write(cx, &self.buf.buf[index..size]) + .map_err(|e| DispatchErrorKind::Io(e.kind()))? + { + Poll::Ready(written) => { + index += written; + if index == size { + break; + } + } + Poll::Pending => { + self.buf.start = index; + self.buf.end = size; + self.buf.empty = false; + return Poll::Pending; + } + } + } + } + Poll::Ready(Ok(())) + } + + // io write interface + fn poll_recv_frame(&mut self, cx: &mut Context<'_>) -> Poll> { + #[cfg(feature = "tokio_base")] + match self.req_rx.poll_recv(cx) { + Poll::Ready(None) => Poll::Ready(Err(DispatchErrorKind::ChannelClosed)), + Poll::Ready(Some(frame)) => Poll::Ready(Ok(frame)), + Poll::Pending => Poll::Pending, + } + #[cfg(feature = "ylong_base")] + match self.req_rx.poll_recv(cx) { + Poll::Ready(Err(_e)) => Poll::Ready(Err(DispatchErrorKind::ChannelClosed)), + Poll::Ready(Ok(frame)) => Poll::Ready(Ok(frame)), + Poll::Pending => Poll::Pending, + } + } + + fn update_settings(&mut self, frame: &Frame) -> SettingState { + if let Payload::Settings(settings) = frame.payload() { + // The ack in Writer is sent from the client to the server to confirm the + // Settings of the encoder on the client. The ack in Reader is sent + // from the server to the client to confirm the Settings of the decoder on the + // client + return if frame.flags().is_ack() { + for setting in settings.get_settings() { + if let Setting::HeaderTableSize(size) = setting { + self.encoder.update_header_table_size(*size as usize); + } + if let Setting::MaxFrameSize(size) = setting { + self.encoder.update_max_frame_size(*size as usize); + } + } + SettingState::Ack + } else { + SettingState::Local(settings.clone()) + }; + } + SettingState::Not + } +} diff --git a/ylong_http_client/src/util/h2/io/mod.rs b/ylong_http_client/src/util/h2/io/mod.rs new file mode 100644 index 0000000..2dde732 --- /dev/null +++ b/ylong_http_client/src/util/h2/io/mod.rs @@ -0,0 +1,26 @@ +// Copyright (c) 2023 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. + +//! HTTP2 io split module. +//! +//! The module defines the io split method, which splits the input and output of +//! io into separate objects. +//! +//! - [`Reader`]: The io output object. +//! +//! - [`Writer`]: The io input object. +//! +//! - [`split`]: io split function. + +mod split; +pub(crate) use split::{split, Reader, Writer}; diff --git a/ylong_http_client/src/util/h2/io/split.rs b/ylong_http_client/src/util/h2/io/split.rs new file mode 100644 index 0000000..5653a77 --- /dev/null +++ b/ylong_http_client/src/util/h2/io/split.rs @@ -0,0 +1,135 @@ +// Copyright (c) 2023 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. + +//! implement `split` fn for io, split it into `Reader` half and `Writer` half. + +use std::io; +use std::io::IoSlice; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; + +use ylong_runtime::io::{AsyncRead, AsyncWrite, ReadBuf}; +use ylong_runtime::sync::{Mutex, MutexGuard}; + +macro_rules! ready { + ($e:expr $(,)?) => { + match $e { + std::task::Poll::Ready(t) => t, + std::task::Poll::Pending => return std::task::Poll::Pending, + } + }; +} + +pub(crate) struct Reader { + inner: Arc>, +} + +pub(crate) struct Writer { + inner: Arc>, +} + +struct InnerLock { + stream: Mutex, + is_write_vectored: bool, +} + +struct StreamGuard<'a, T> { + inner: MutexGuard<'a, T>, +} + +pub(crate) fn split(stream: T) -> (Reader, Writer) +where + T: AsyncRead + AsyncWrite, +{ + let is_write_vectored = stream.is_write_vectored(); + let inner = Arc::new(InnerLock { + stream: Mutex::new(stream), + is_write_vectored, + }); + + let rd = Reader { + inner: inner.clone(), + }; + + let wr = Writer { inner }; + + (rd, wr) +} + +impl AsyncRead for Reader { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let mut guard = ready!(self.inner.get_lock(cx)); + guard.stream().poll_read(cx, buf) + } +} + +impl AsyncWrite for Writer { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let mut inner = ready!(self.inner.get_lock(cx)); + inner.stream().poll_write(cx, buf) + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + let mut inner = ready!(self.inner.get_lock(cx)); + inner.stream().poll_write_vectored(cx, bufs) + } + + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut inner = ready!(self.inner.get_lock(cx)); + inner.stream().poll_flush(cx) + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut inner = ready!(self.inner.get_lock(cx)); + inner.stream().poll_shutdown(cx) + } +} + +impl<'a, T> StreamGuard<'a, T> { + fn stream(&mut self) -> Pin<&mut T> { + // safety: the stream is pinned in `Arc` and the `Guard` ensures mutual + // exclusion. + unsafe { Pin::new_unchecked(&mut *self.inner) } + } +} + +impl InnerLock { + fn get_lock(&self, cx: &mut Context<'_>) -> Poll> { + match self.stream.try_lock() { + Ok(guard) => Poll::Ready(StreamGuard { inner: guard }), + Err(_) => { + std::thread::yield_now(); + cx.waker().wake_by_ref(); + + Poll::Pending + } + } + } +} diff --git a/ylong_http_client/src/util/h2/manager.rs b/ylong_http_client/src/util/h2/manager.rs new file mode 100644 index 0000000..629a8fb --- /dev/null +++ b/ylong_http_client/src/util/h2/manager.rs @@ -0,0 +1,544 @@ +// Copyright (c) 2023 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. + +//! Streams manage coroutine. + +use std::future::Future; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll}; + +use ylong_http::h2::{ + ErrorCode, Frame, FrameFlags, Goaway, H2Error, Payload, Ping, RstStream, Setting, +}; + +use crate::runtime::{UnboundedReceiver, UnboundedSender}; +use crate::util::dispatcher::http2::{ + DispatchErrorKind, OutputMessage, ReqMessage, RespMessage, SettingsState, SettingsSync, + StreamController, +}; +use crate::util::h2::streams::{DataReadState, FrameRecvState, StreamEndState}; + +pub(crate) struct ConnManager { + // Synchronize SETTINGS frames sent by the client. + settings: Arc>, + // channel transmitter between manager and io input. + input_tx: UnboundedSender, + // channel receiver between manager and io output. + resp_rx: UnboundedReceiver, + // channel receiver between manager and stream coroutine. + req_rx: UnboundedReceiver, + controller: StreamController, +} + +impl Future for ConnManager { + type Output = Result<(), DispatchErrorKind>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let manager = self.get_mut(); + loop { + // Receives a response frame from io output. + match manager.resp_rx.poll_recv(cx) { + #[cfg(feature = "tokio_base")] + Poll::Ready(Some(message)) => match message { + OutputMessage::Output(frame) => { + manager.poll_recv_message(frame)?; + } + // io output occurs error. + OutputMessage::OutputExit(e) => { + manager.poll_deal_with_error(e)?; + } + }, + #[cfg(feature = "ylong_base")] + Poll::Ready(Ok(message)) => match message { + OutputMessage::Output(frame) => { + manager.poll_recv_message(frame)?; + } + // io output occurs error. + OutputMessage::OutputExit(e) => { + manager.poll_deal_with_error(e)?; + } + }, + #[cfg(feature = "tokio_base")] + Poll::Ready(None) => { + manager.exit_with_error(DispatchErrorKind::ChannelClosed); + return Poll::Ready(Ok(())); + } + #[cfg(feature = "ylong_base")] + Poll::Ready(Err(_e)) => { + manager.exit_with_error(DispatchErrorKind::ChannelClosed); + return Poll::Ready(Ok(())); + } + + Poll::Pending => { + // The manager previously accepted a GOAWAY Frame. + if let Some(code) = manager.controller.go_away { + manager.poll_deal_with_go_away(code)?; + } + + manager + .controller + .streams + .window_update_conn(&manager.input_tx)?; + manager + .controller + .streams + .window_update_streams(&manager.input_tx)?; + + loop { + #[cfg(feature = "tokio_base")] + match manager.req_rx.poll_recv(cx) { + Poll::Ready(Some(message)) => { + if manager.controller.streams.reach_max_concurrency() { + manager + .controller + .streams + .push_pending_concurrency(message.id); + } else { + manager.controller.streams.increase_current_concurrency(); + manager + .controller + .streams + .push_back_pending_send(message.id) + } + manager + .controller + .senders + .insert(message.id, message.sender); + manager + .controller + .streams + .insert(message.id, message.request); + } + Poll::Ready(None) => { + // TODO May need to close the connection after + // the channel is closed? + } + Poll::Pending => { + break; + } + } + #[cfg(feature = "ylong_base")] + match manager.req_rx.poll_recv(cx) { + Poll::Ready(Ok(message)) => { + if manager.controller.streams.reach_max_concurrency() { + manager + .controller + .streams + .push_pending_concurrency(message.id); + } else { + manager.controller.streams.increase_current_concurrency(); + manager + .controller + .streams + .push_back_pending_send(message.id) + } + manager + .controller + .senders + .insert(message.id, message.sender); + manager + .controller + .streams + .insert(message.id, message.request); + } + Poll::Ready(Err(_e)) => { + // TODO May need to close the connection after + // the channel is closed? + } + Poll::Pending => { + break; + } + } + } + loop { + manager.controller.streams.try_consume_pending_concurrency(); + match manager.controller.streams.next_stream() { + None => { + break; + } + Some(id) => { + match manager.controller.streams.headers(id)? { + None => {} + Some(header) => { + manager.poll_send_frame(header)?; + } + } + + loop { + match manager.controller.streams.poll_data(cx, id)? { + DataReadState::Closed => { + break; + } + DataReadState::Pending => { + break; + } + DataReadState::Ready(data) => { + manager.poll_send_frame(data)?; + } + DataReadState::Finish(frame) => { + manager.poll_send_frame(frame)?; + break; + } + } + } + } + } + } + + return Poll::Pending; + } + } + } + } +} + +impl ConnManager { + pub(crate) fn new( + settings: Arc>, + input_tx: UnboundedSender, + resp_rx: UnboundedReceiver, + req_rx: UnboundedReceiver, + controller: StreamController, + ) -> Self { + Self { + settings, + input_tx, + resp_rx, + req_rx, + controller, + } + } + + fn poll_send_frame(&mut self, frame: Frame) -> Result<(), DispatchErrorKind> { + match frame.payload() { + Payload::Headers(_) => { + match self + .controller + .streams + .send_headers_frame(frame.stream_id() as u32, frame.flags().is_end_stream()) + { + FrameRecvState::OK => {} + // Never return Ignore case. + FrameRecvState::Ignore => {} + FrameRecvState::Err(e) => { + return Err(e.into()); + } + } + } + Payload::Data(_) => { + match self + .controller + .streams + .send_data_frame(frame.stream_id() as u32, frame.flags().is_end_stream()) + { + FrameRecvState::OK => {} + FrameRecvState::Ignore => {} + FrameRecvState::Err(e) => { + return Err(e.into()); + } + } + } + _ => {} + } + + self.input_tx + .send(frame) + .map_err(|_e| DispatchErrorKind::ChannelClosed) + } + + fn poll_recv_frame(&mut self, frame: Frame) -> Result<(), DispatchErrorKind> { + match frame.payload() { + Payload::Settings(settings) => { + if frame.flags().is_ack() { + { + let mut connection = self.settings.lock().unwrap(); + match &connection.settings { + SettingsState::Acknowledging(settings) => { + for setting in settings.get_settings() { + if let Setting::InitialWindowSize(size) = setting { + self.controller + .streams + .apply_recv_initial_window_size(*size); + } + } + } + SettingsState::Synced => {} + } + connection.settings = SettingsState::Synced; + } + } else { + for setting in settings.get_settings() { + if let Setting::MaxConcurrentStreams(num) = setting { + self.controller.streams.apply_max_concurrent_streams(*num); + } + if let Setting::InitialWindowSize(size) = setting { + self.controller + .streams + .apply_send_initial_window_size(*size)?; + } + } + + // The reason for copying the payload is to pass information to the io input to + // set the frame encoder, and the input will empty the + // payload when it is sent + let new_settings = Frame::new( + frame.stream_id(), + FrameFlags::new(0x1), + frame.payload().clone(), + ); + return self + .input_tx + .send(new_settings) + .map_err(|_e| DispatchErrorKind::ChannelClosed); + } + } + Payload::Ping(ping) => { + return if frame.flags().is_ack() { + // TODO The client does not have the logic to send ping frames. Therefore, the + // ack ping is not processed. + Ok(()) + } else { + self.input_tx + .send(Ping::ack(ping.clone())) + .map_err(|_e| DispatchErrorKind::ChannelClosed) + }; + } + Payload::PushPromise(_) => { + // TODO The current settings_enable_push setting is fixed to false. + return Err(H2Error::ConnectionError(ErrorCode::ProtocolError).into()); + } + Payload::Goaway(go_away) => { + // Prevents the current connection from generating a new stream. + self.controller.shutdown(); + self.req_rx.close(); + let last_stream_id = go_away.get_last_stream_id(); + let streams = self + .controller + .go_away_unsent_stream(last_stream_id as u32)?; + + let error = + H2Error::ConnectionError(ErrorCode::try_from(go_away.get_error_code())?); + for stream_id in streams { + self.controller.send_message_to_stream( + stream_id, + RespMessage::OutputExit(error.clone().into()), + ); + } + // Exit after the allowed stream is complete. + self.controller.go_away = Some(go_away.get_error_code()); + } + Payload::RstStream(_reset) => { + match self + .controller + .streams + .recv_remote_reset(frame.stream_id() as u32) + { + StreamEndState::OK => { + self.controller.send_message_to_stream( + frame.stream_id() as u32, + RespMessage::Output(frame), + ); + } + StreamEndState::Err(e) => { + return Err(e.into()); + } + _ => {} + } + } + Payload::Headers(_headers) => { + match self + .controller + .streams + .recv_headers(frame.stream_id() as u32, frame.flags().is_end_stream()) + { + FrameRecvState::OK => { + self.controller.send_message_to_stream( + frame.stream_id() as u32, + RespMessage::Output(frame), + ); + } + FrameRecvState::Err(e) => { + return Err(e.into()); + } + _ => {} + } + } + Payload::Data(data) => { + let id = frame.stream_id() as u32; + let len = data.size() as u32; + match self + .controller + .streams + .recv_data(id, frame.flags().is_end_stream()) + { + FrameRecvState::OK => { + self.controller.send_message_to_stream( + frame.stream_id() as u32, + RespMessage::Output(frame), + ); + } + FrameRecvState::Ignore => {} + FrameRecvState::Err(e) => return Err(e.into()), + } + self.controller.streams.release_conn_recv_window(len)?; + self.controller + .streams + .release_stream_recv_window(id, len)?; + } + Payload::WindowUpdate(windows) => { + let id = frame.stream_id(); + let increment = windows.get_increment(); + if id == 0 { + self.controller + .streams + .increase_conn_send_window(increment)?; + self.controller.streams.reassign_conn_send_window(); + } else { + self.controller + .streams + .reassign_stream_send_window(id as u32, increment)?; + } + } + // Priority is no longer recommended, so keep it compatible but not processed. + Payload::Priority(_priority) => {} + } + Ok(()) + } + + fn poll_deal_with_error(&mut self, kind: DispatchErrorKind) -> Result<(), DispatchErrorKind> { + match kind { + DispatchErrorKind::H2(h2) => { + match h2 { + H2Error::StreamError(id, code) => { + let rest_payload = RstStream::new(code.into_code()); + let frame = Frame::new( + id as usize, + FrameFlags::empty(), + Payload::RstStream(rest_payload), + ); + match self.controller.streams.send_local_reset(id) { + StreamEndState::OK => { + self.input_tx + .send(frame) + .map_err(|_e| DispatchErrorKind::ChannelClosed)?; + + self.controller.send_message_to_stream( + id, + RespMessage::OutputExit(DispatchErrorKind::ChannelClosed), + ); + } + StreamEndState::Ignore => {} + StreamEndState::Err(e) => { + // This error will never happen. + return Err(e.into()); + } + } + } + H2Error::ConnectionError(code) => { + self.exit_with_error(DispatchErrorKind::H2(H2Error::ConnectionError( + code.clone(), + ))); + + // last_stream_id is set to 0 to ensure that all streams are + // shutdown. + let go_away_payload = Goaway::new( + code.clone().into_code(), + self.controller.streams.latest_remote_id as usize, + vec![], + ); + let frame = Frame::new( + 0, + FrameFlags::empty(), + Payload::Goaway(go_away_payload.clone()), + ); + if let Some(ref go_away) = self.controller.go_away_sync.going_away { + if go_away.get_error_code() == go_away_payload.get_error_code() + && go_away.get_last_stream_id() + == go_away_payload.get_last_stream_id() + { + return Ok(()); + } + } + // Avoid sending the same GO_AWAY frame multiple times. + self.controller.go_away_sync.going_away = Some(go_away_payload); + self.input_tx + .send(frame) + .map_err(|_e| DispatchErrorKind::ChannelClosed)?; + // TODO When the current client has an error, + // it always sends the GO_AWAY frame at the first time and exits directly. + // Should we consider letting part of the unfinished stream complete? + return Err(H2Error::ConnectionError(code).into()); + } + } + } + other => { + self.exit_with_error(other.clone()); + return Err(other); + } + } + Ok(()) + } + + fn poll_deal_with_go_away(&mut self, error_code: u32) -> Result<(), DispatchErrorKind> { + let last_stream_id = self.controller.streams.latest_remote_id as usize; + // The client that receives GO_AWAY needs to return a GO_AWAY to the server + // before closed. The preceding operations before receiving the frame + // ensure that the connection is in the closing state. + if self.controller.streams.is_closed() { + let go_away_payload = Goaway::new(error_code, last_stream_id, vec![]); + let frame = Frame::new( + 0, + FrameFlags::empty(), + Payload::Goaway(go_away_payload.clone()), + ); + + match self.controller.go_away_sync.going_away { + None => { + self.controller.go_away_sync.going_away = Some(go_away_payload); + self.input_tx + .send(frame) + .map_err(|_e| DispatchErrorKind::ChannelClosed)?; + } + Some(ref go_away) => { + // Whether the same GOAWAY Frame has been sent before. + if !(go_away.get_error_code() == error_code + && go_away.get_last_stream_id() == last_stream_id) + { + self.controller.go_away_sync.going_away = Some(go_away_payload); + self.input_tx + .send(frame) + .map_err(|_e| DispatchErrorKind::ChannelClosed)?; + } + } + } + return Err(H2Error::ConnectionError(ErrorCode::try_from(error_code)?).into()); + } + Ok(()) + } + + fn poll_recv_message(&mut self, frame: Frame) -> Result<(), DispatchErrorKind> { + if let Err(kind) = self.poll_recv_frame(frame) { + self.poll_deal_with_error(kind)?; + } + Ok(()) + } + + pub(crate) fn exit_with_error(&mut self, error: DispatchErrorKind) { + self.controller.shutdown(); + self.req_rx.close(); + self.controller + .streams + .go_away_all_streams(&mut self.controller.senders, error); + } +} diff --git a/ylong_http_client/src/util/h2/mod.rs b/ylong_http_client/src/util/h2/mod.rs new file mode 100644 index 0000000..50feae9 --- /dev/null +++ b/ylong_http_client/src/util/h2/mod.rs @@ -0,0 +1,43 @@ +// Copyright (c) 2023 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. + +//! Http2 Protocol module. +//! +//! A module that manages frame transport over HTTP2 protocol. +//! +//! -[`SendData`] is used to control io write half for send frames. +//! -[`RecvData`] is used to control io read half for recv frames. +//! -[`Streams`] is used to manage the state of individual streams. +//! -[`ConnManager`] is used to coordinate the Request sending and Response +//! receiving of multiple streams. + +mod buffer; +mod data_ref; +mod input; +mod manager; +mod output; +mod streams; + +#[cfg(feature = "ylong_base")] +mod io; + +pub(crate) use buffer::FlowControl; +pub(crate) use data_ref::BodyDataRef; +pub(crate) use input::SendData; +#[cfg(feature = "ylong_base")] +pub(crate) use io::{split, Reader, Writer}; +pub(crate) use manager::ConnManager; +pub(crate) use output::RecvData; +pub(crate) use streams::{RequestWrapper, Streams}; + +pub const MAX_FLOW_CONTROL_WINDOW: u32 = (1 << 31) - 1; diff --git a/ylong_http_client/src/util/h2/output.rs b/ylong_http_client/src/util/h2/output.rs new file mode 100644 index 0000000..11dfe74 --- /dev/null +++ b/ylong_http_client/src/util/h2/output.rs @@ -0,0 +1,144 @@ +// Copyright (c) 2023 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. + +//! Frame recv coroutine. + +use std::future::Future; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll}; + +use ylong_http::h2::{ + ErrorCode, Frame, FrameDecoder, FrameKind, Frames, H2Error, Payload, Setting, +}; + +use crate::runtime::{AsyncRead, ReadBuf, ReadHalf, UnboundedSender}; +use crate::util::dispatcher::http2::{ + DispatchErrorKind, OutputMessage, SettingsState, SettingsSync, +}; + +pub(crate) struct RecvData { + decoder: FrameDecoder, + settings: Arc>, + reader: ReadHalf, + resp_tx: UnboundedSender, +} + +impl Future for RecvData { + type Output = Result<(), DispatchErrorKind>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let receiver = self.get_mut(); + receiver.poll_read_frame(cx) + } +} + +impl RecvData { + pub(crate) fn new( + decoder: FrameDecoder, + settings: Arc>, + reader: ReadHalf, + resp_tx: UnboundedSender, + ) -> Self { + Self { + decoder, + settings, + reader, + resp_tx, + } + } + + fn poll_read_frame(&mut self, cx: &mut Context<'_>) -> Poll> { + let mut buf = [0u8; 1024]; + loop { + let mut read_buf = ReadBuf::new(&mut buf); + match Pin::new(&mut self.reader).poll_read(cx, &mut read_buf) { + Poll::Ready(Err(e)) => { + self.transmit_error(DispatchErrorKind::Disconnect)?; + return Poll::Ready(Err(e.into())); + } + Poll::Ready(Ok(())) => {} + Poll::Pending => { + return Poll::Pending; + } + } + let read = read_buf.filled().len(); + if read == 0 { + self.transmit_error(DispatchErrorKind::Disconnect)?; + return Poll::Ready(Err(DispatchErrorKind::Disconnect)); + } + + match self.decoder.decode(&buf[..read]) { + Ok(frames) => match self.transmit_frame(frames) { + Ok(_) => {} + Err(DispatchErrorKind::H2(e)) => { + self.transmit_error(e.into())?; + } + Err(e) => { + return Poll::Ready(Err(e)); + } + }, + Err(e) => { + self.transmit_error(e.into())?; + } + } + } + } + + fn transmit_frame(&mut self, frames: Frames) -> Result<(), DispatchErrorKind> { + for kind in frames.into_iter() { + match kind { + FrameKind::Complete(frame) => { + self.update_settings(&frame)?; + self.resp_tx + .send(OutputMessage::Output(frame)) + .map_err(|_e| DispatchErrorKind::ChannelClosed)?; + } + FrameKind::Partial => {} + } + } + Ok(()) + } + + fn transmit_error(&self, err: DispatchErrorKind) -> Result<(), DispatchErrorKind> { + self.resp_tx + .send(OutputMessage::OutputExit(err)) + .map_err(|_e| DispatchErrorKind::ChannelClosed) + } + + fn update_settings(&mut self, frame: &Frame) -> Result<(), H2Error> { + if let Payload::Settings(_settings) = frame.payload() { + if frame.flags().is_ack() { + { + let connection = self.settings.lock().unwrap(); + match &connection.settings { + SettingsState::Acknowledging(settings) => { + for setting in settings.get_settings() { + if let Setting::MaxHeaderListSize(size) = setting { + self.decoder.set_max_header_list_size(*size as usize); + } + if let Setting::MaxFrameSize(size) = setting { + self.decoder.set_max_frame_size(*size)?; + } + } + } + SettingsState::Synced => { + return Err(H2Error::ConnectionError(ErrorCode::ConnectError)) + } + } + } + } + } + Ok(()) + } +} diff --git a/ylong_http_client/src/util/h2/streams.rs b/ylong_http_client/src/util/h2/streams.rs new file mode 100644 index 0000000..36f5c8f --- /dev/null +++ b/ylong_http_client/src/util/h2/streams.rs @@ -0,0 +1,728 @@ +// Copyright (c) 2023 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. + +//! Streams operations utils. + +use std::cmp::{min, Ordering}; +use std::collections::{HashMap, HashSet, VecDeque}; +use std::task::{Context, Poll}; + +use ylong_http::h2::{Data, ErrorCode, Frame, FrameFlags, H2Error, Payload}; + +use crate::runtime::UnboundedSender; +use crate::util::dispatcher::http2::{DispatchErrorKind, RespMessage}; +use crate::util::h2::buffer::{FlowControl, RecvWindow, SendWindow}; +use crate::util::h2::data_ref::BodyDataRef; +use crate::util::h2::streams::StreamState::{LocalHalfClosed, Open, RemoteHalfClosed}; + +const INITIAL_MAX_SEND_STREAM_ID: u32 = u32::MAX >> 1; +const INITIAL_MAX_RECV_STREAM_ID: u32 = u32::MAX >> 1; +const INITIAL_LATEST_REMOTE_ID: u32 = 0; + +pub(crate) enum FrameRecvState { + OK, + Ignore, + Err(H2Error), +} + +pub(crate) enum DataReadState { + Closed, + // Wait for poll_read or wait for window. + Pending, + Ready(Frame), + Finish(Frame), +} + +pub(crate) enum StreamEndState { + OK, + Ignore, + Err(H2Error), +} + +// +--------+ +// send PP | | recv PP +// ,--------| idle |--------. +// / | | \ +// v +--------+ v +// +----------+ | +----------+ +// | | | send H / | | +// ,------| reserved | | recv H | reserved |------. +// | | (local) | | | (remote) | | +// | +----------+ v +----------+ | +// | | +--------+ | | +// | | recv ES | | send ES | | +// | send H | ,-------| open |-------. | recv H | +// | | / | | \ | | +// | v v +--------+ v v | +// | +----------+ | +----------+ | +// | | half | | | half | | +// | | closed | | send R / | closed | | +// | | (remote) | | recv R | (local) | | +// | +----------+ | +----------+ | +// | | | | | +// | | send ES / | recv ES / | | +// | | send R / v send R / | | +// | | recv R +--------+ recv R | | +// | send R / `----------->| |<-----------' send R / | +// | recv R | closed | recv R | +// `----------------------->| |<----------------------' +// +--------+ +#[derive(Clone, Debug)] +pub(crate) enum StreamState { + Idle, + // When response does not depend on request, + // the server can send response directly without waiting for the request to finish receiving. + // Therefore, the sending and receiving states of the client have their own states + Open { + send: ActiveState, + recv: ActiveState, + }, + #[allow(dead_code)] + ReservedRemote, + // After the request is sent, the state is waiting for the response to be received. + LocalHalfClosed(ActiveState), + // When the response is received but the request is not fully sent, + // this indicates the status of the request being sent + RemoteHalfClosed(ActiveState), + Closed(CloseReason), +} + +#[derive(Clone, Debug)] +pub(crate) enum CloseReason { + LocalRst, + RemoteRst, + RemoteGoAway, + LocalGoAway, + EndStream, +} + +#[derive(Clone, Debug)] +pub(crate) enum ActiveState { + WaitHeaders, + WaitData, +} + +pub(crate) struct Stream { + pub(crate) recv_window: RecvWindow, + pub(crate) send_window: SendWindow, + pub(crate) state: StreamState, + pub(crate) header: Option, + pub(crate) data: BodyDataRef, +} + +pub(crate) struct RequestWrapper { + pub(crate) header: Frame, + pub(crate) data: BodyDataRef, +} + +pub(crate) struct Streams { + // Records the received goaway last_stream_id. + pub(crate) max_send_id: u32, + // Records the sent goaway last_stream_id. + pub(crate) max_recv_id: u32, + // Currently the client doesn't support push promise, so this value is always 0. + pub(crate) latest_remote_id: u32, + pub(crate) stream_recv_window_size: u32, + pub(crate) stream_send_window_size: u32, + max_concurrent_streams: u32, + current_concurrent_streams: u32, + flow_control: FlowControl, + pending_concurrency: VecDeque, + pending_stream_window: HashSet, + pending_conn_window: VecDeque, + pending_send: VecDeque, + window_updating_streams: VecDeque, + pub(crate) stream_map: HashMap, +} + +impl Streams { + pub(crate) fn new( + recv_window_size: u32, + send_window_size: u32, + flow_control: FlowControl, + ) -> Self { + Self { + max_send_id: INITIAL_MAX_SEND_STREAM_ID, + max_recv_id: INITIAL_MAX_RECV_STREAM_ID, + latest_remote_id: INITIAL_LATEST_REMOTE_ID, + max_concurrent_streams: u32::MAX, + current_concurrent_streams: 0, + stream_recv_window_size: recv_window_size, + stream_send_window_size: send_window_size, + flow_control, + pending_concurrency: VecDeque::new(), + pending_stream_window: HashSet::new(), + pending_conn_window: VecDeque::new(), + pending_send: VecDeque::new(), + window_updating_streams: VecDeque::new(), + stream_map: HashMap::new(), + } + } + + pub(crate) fn decrease_current_concurrency(&mut self) { + self.current_concurrent_streams -= 1; + } + + pub(crate) fn increase_current_concurrency(&mut self) { + self.current_concurrent_streams += 1; + } + + pub(crate) fn reach_max_concurrency(&mut self) -> bool { + self.current_concurrent_streams >= self.max_concurrent_streams + } + + pub(crate) fn apply_max_concurrent_streams(&mut self, num: u32) { + self.max_concurrent_streams = num; + } + + pub(crate) fn apply_send_initial_window_size(&mut self, size: u32) -> Result<(), H2Error> { + let current = self.stream_send_window_size; + self.stream_send_window_size = size; + + match current.cmp(&size) { + Ordering::Less => { + let excess = size - current; + for (_id, stream) in self.stream_map.iter_mut() { + stream.send_window.increase_size(excess)?; + } + for id in self.pending_stream_window.iter() { + // self.push_back_pending_send(*id); + self.pending_send.push_back(*id); + } + self.pending_stream_window.clear(); + } + Ordering::Greater => { + let excess = current - size; + for (_id, stream) in self.stream_map.iter_mut() { + stream.send_window.reduce_size(excess); + } + } + Ordering::Equal => {} + } + Ok(()) + } + + pub(crate) fn apply_recv_initial_window_size(&mut self, size: u32) { + let current = self.stream_recv_window_size; + self.stream_recv_window_size = size; + match current.cmp(&size) { + Ordering::Less => { + for (_id, stream) in self.stream_map.iter_mut() { + let extra = size - current; + stream.recv_window.increase_notification(extra); + stream.recv_window.increase_actual(extra); + } + } + Ordering::Greater => { + for (_id, stream) in self.stream_map.iter_mut() { + stream.recv_window.reduce_notification(current - size); + } + } + Ordering::Equal => {} + } + } + + pub(crate) fn release_stream_recv_window(&mut self, id: u32, 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)); + } + stream.recv_window.recv_data(size); + if stream.recv_window.unreleased_size().is_some() { + self.window_updating_streams.push_back(id); + } + } + Ok(()) + } + + pub(crate) fn release_conn_recv_window(&mut self, size: u32) -> Result<(), H2Error> { + if self.flow_control.recv_notification_size_available() < size { + return Err(H2Error::ConnectionError(ErrorCode::FlowControlError)); + } + self.flow_control.recv_data(size); + Ok(()) + } + + pub(crate) fn is_closed(&self) -> bool { + for (_id, stream) in self.stream_map.iter() { + match stream.state { + StreamState::Closed(_) => {} + _ => { + return false; + } + } + } + true + } + + pub(crate) fn insert(&mut self, id: u32, request: RequestWrapper) { + let send_window = SendWindow::new(self.stream_send_window_size as i32); + let recv_window = RecvWindow::new(self.stream_recv_window_size as i32); + + let stream = Stream::new(recv_window, send_window, request.header, request.data); + self.stream_map.insert(id, stream); + } + + pub(crate) fn push_back_pending_send(&mut self, id: u32) { + self.pending_send.push_back(id); + } + + pub(crate) fn push_pending_concurrency(&mut self, id: u32) { + self.pending_concurrency.push_back(id); + } + + pub(crate) fn next_stream(&mut self) -> Option { + self.pending_send.pop_front() + } + + pub(crate) fn try_consume_pending_concurrency(&mut self) { + while !self.reach_max_concurrency() { + match self.pending_concurrency.pop_front() { + None => { + return; + } + Some(id) => { + self.increase_current_concurrency(); + self.push_back_pending_send(id); + } + } + } + } + + pub(crate) fn increase_conn_send_window(&mut self, size: u32) -> Result<(), H2Error> { + self.flow_control.increase_send_size(size) + } + + pub(crate) fn reassign_conn_send_window(&mut self) { + // Since the data structure of the body is a stream, + // the size of a body cannot be obtained, + // so all streams in pending_conn_window are added to the pending_send queue + // again. + loop { + match self.pending_conn_window.pop_front() { + None => break, + Some(id) => { + self.push_back_pending_send(id); + } + } + } + } + + pub(crate) fn reassign_stream_send_window( + &mut self, + id: u32, + size: u32, + ) -> Result<(), H2Error> { + if let Some(stream) = self.stream_map.get_mut(&id) { + stream.send_window.increase_size(size)?; + } + if self.pending_stream_window.take(&id).is_some() { + self.pending_send.push_back(id); + } + Ok(()) + } + + pub(crate) fn window_update_conn( + &mut self, + sender: &UnboundedSender, + ) -> Result<(), DispatchErrorKind> { + if let Some(window_update) = self.flow_control.check_conn_recv_window_update() { + sender + .send(window_update) + .map_err(|_e| DispatchErrorKind::ChannelClosed)?; + } + Ok(()) + } + + pub(crate) fn window_update_streams( + &mut self, + sender: &UnboundedSender, + ) -> Result<(), DispatchErrorKind> { + loop { + match self.window_updating_streams.pop_front() { + None => return Ok(()), + Some(id) => match self.stream_map.get_mut(&id) { + None => {} + Some(stream) => { + if !stream.is_init_or_active_flow_control() { + return Ok(()); + } + if let Some(window_update) = stream.recv_window.check_window_update(id) { + sender + .send(window_update) + .map_err(|_e| DispatchErrorKind::ChannelClosed)?; + } + } + }, + } + } + } + + pub(crate) fn headers(&mut self, id: u32) -> Result, H2Error> { + match self.stream_map.get_mut(&id) { + None => Err(H2Error::ConnectionError(ErrorCode::IntervalError)), + Some(stream) => match stream.state { + StreamState::Closed(_) => Ok(None), + _ => Ok(stream.header.take()), + }, + } + } + + pub(crate) fn poll_data( + &mut self, + cx: &mut Context<'_>, + id: u32, + ) -> Result { + // TODO Since the Array length needs to be a constant, + // the minimum value is used here, which can be optimized to the MAX_FRAME_SIZE + // updated in SETTINGS + const DEFAULT_MAX_FRAME_SIZE: usize = 16 * 1024; + + match self.stream_map.get_mut(&id) { + None => Err(H2Error::ConnectionError(ErrorCode::IntervalError)), + Some(stream) => match stream.state { + StreamState::Closed(_) => Ok(DataReadState::Closed), + _ => { + let stream_send_vacant = stream.send_window.size_available() as usize; + if stream_send_vacant == 0 { + self.pending_stream_window.insert(id); + return Ok(DataReadState::Pending); + } + let conn_send_vacant = self.flow_control.send_size_available(); + if conn_send_vacant == 0 { + self.pending_conn_window.push_back(id); + return Ok(DataReadState::Pending); + } + + let available = min(stream_send_vacant, conn_send_vacant); + let len = min(available, DEFAULT_MAX_FRAME_SIZE); + + let mut buf = [0u8; DEFAULT_MAX_FRAME_SIZE]; + + match stream.data.poll_read(cx, &mut buf[..len])? { + Poll::Ready(size) => { + if size > 0 { + stream.send_window.send_data(size as u32); + self.flow_control.send_data(size as u32); + let data_vec = Vec::from(&buf[..size]); + let flag = FrameFlags::new(0); + + Ok(DataReadState::Ready(Frame::new( + id as usize, + flag, + Payload::Data(Data::new(data_vec)), + ))) + } else { + let data_vec = vec![]; + let mut flag = FrameFlags::new(1); + flag.set_end_stream(true); + Ok(DataReadState::Finish(Frame::new( + id as usize, + flag, + Payload::Data(Data::new(data_vec)), + ))) + } + } + Poll::Pending => { + self.push_back_pending_send(id); + Ok(DataReadState::Pending) + } + } + } + }, + } + } + + pub(crate) fn get_go_away_streams(&mut self, last_stream_id: u32) -> Vec { + let mut ids = vec![]; + for (id, unsent_stream) in self.stream_map.iter_mut() { + if *id >= last_stream_id { + match unsent_stream.state { + StreamState::Closed(_) => {} + StreamState::Idle => { + unsent_stream.state = StreamState::Closed(CloseReason::RemoteGoAway); + unsent_stream.header = None; + unsent_stream.data.clear(); + } + _ => { + self.current_concurrent_streams -= 1; + unsent_stream.state = StreamState::Closed(CloseReason::RemoteGoAway); + unsent_stream.header = None; + unsent_stream.data.clear(); + } + }; + ids.push(*id); + } + } + ids + } + + pub(crate) fn go_away_all_streams( + &mut self, + senders: &mut HashMap>, + error: DispatchErrorKind, + ) { + for (id, stream) in self.stream_map.iter_mut() { + match stream.state { + StreamState::Closed(_) => {} + _ => { + stream.header = None; + stream.data.clear(); + stream.state = StreamState::Closed(CloseReason::LocalGoAway); + if let Some(sender) = senders.get_mut(id) { + sender.send(RespMessage::OutputExit(error.clone())).ok(); + } + } + } + } + self.window_updating_streams.clear(); + self.pending_stream_window.clear(); + self.pending_send.clear(); + self.pending_conn_window.clear(); + self.pending_concurrency.clear(); + } + + pub(crate) fn send_local_reset(&mut self, id: u32) -> StreamEndState { + return match self.stream_map.get_mut(&id) { + None => StreamEndState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), + Some(stream) => match stream.state { + StreamState::Closed( + CloseReason::LocalRst + | CloseReason::LocalGoAway + | CloseReason::RemoteRst + | CloseReason::RemoteGoAway, + ) => StreamEndState::Ignore, + StreamState::Closed(CloseReason::EndStream) => { + stream.state = StreamState::Closed(CloseReason::LocalRst); + StreamEndState::Ignore + } + _ => { + stream.state = StreamState::Closed(CloseReason::LocalRst); + stream.header = None; + stream.data.clear(); + self.decrease_current_concurrency(); + StreamEndState::OK + } + }, + }; + } + + pub(crate) fn send_headers_frame(&mut self, id: u32, eos: bool) -> FrameRecvState { + match self.stream_map.get_mut(&id) { + None => return FrameRecvState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), + Some(stream) => match &stream.state { + StreamState::Idle => { + stream.state = if eos { + StreamState::LocalHalfClosed(ActiveState::WaitHeaders) + } else { + StreamState::Open { + send: ActiveState::WaitData, + recv: ActiveState::WaitHeaders, + } + }; + } + StreamState::Open { + send: ActiveState::WaitHeaders, + recv, + } => { + stream.state = if eos { + StreamState::LocalHalfClosed(recv.clone()) + } else { + StreamState::Open { + send: ActiveState::WaitData, + recv: recv.clone(), + } + }; + } + StreamState::RemoteHalfClosed(ActiveState::WaitHeaders) => { + stream.state = if eos { + self.current_concurrent_streams -= 1; + StreamState::Closed(CloseReason::EndStream) + } else { + StreamState::RemoteHalfClosed(ActiveState::WaitData) + }; + } + _ => { + return FrameRecvState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)); + } + }, + } + FrameRecvState::OK + } + + pub(crate) fn send_data_frame(&mut self, id: u32, eos: bool) -> FrameRecvState { + match self.stream_map.get_mut(&id) { + None => return FrameRecvState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), + Some(stream) => match &stream.state { + StreamState::Open { + send: ActiveState::WaitData, + recv, + } => { + if eos { + stream.state = StreamState::LocalHalfClosed(recv.clone()); + } + } + StreamState::RemoteHalfClosed(ActiveState::WaitData) => { + if eos { + self.current_concurrent_streams -= 1; + stream.state = StreamState::Closed(CloseReason::EndStream); + } + } + _ => { + return FrameRecvState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)); + } + }, + } + FrameRecvState::OK + } + + pub(crate) fn recv_remote_reset(&mut self, id: u32) -> StreamEndState { + if id > self.max_recv_id { + return StreamEndState::Ignore; + } + return match self.stream_map.get_mut(&id) { + None => StreamEndState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), + Some(stream) => match stream.state { + StreamState::Closed(..) => StreamEndState::Ignore, + _ => { + stream.state = StreamState::Closed(CloseReason::RemoteRst); + stream.header = None; + stream.data.clear(); + self.decrease_current_concurrency(); + StreamEndState::OK + } + }, + }; + } + + pub(crate) fn recv_headers(&mut self, id: u32, eos: bool) -> FrameRecvState { + if id > self.max_recv_id { + return FrameRecvState::Ignore; + } + + match self.stream_map.get_mut(&id) { + None => return FrameRecvState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), + Some(stream) => match &stream.state { + StreamState::Idle => { + stream.state = if eos { + RemoteHalfClosed(ActiveState::WaitHeaders) + } else { + Open { + send: ActiveState::WaitHeaders, + recv: ActiveState::WaitData, + } + }; + } + StreamState::ReservedRemote => { + if eos { + stream.state = StreamState::Closed(CloseReason::EndStream); + // Whether the number of concurrency is required here for PUSH_PROMISE + // frame. + self.decrease_current_concurrency(); + } else { + stream.state = LocalHalfClosed(ActiveState::WaitData); + } + } + StreamState::Open { + send, + recv: ActiveState::WaitHeaders, + } => { + stream.state = if eos { + RemoteHalfClosed(send.clone()) + } else { + Open { + send: send.clone(), + recv: ActiveState::WaitData, + } + } + } + StreamState::LocalHalfClosed(ActiveState::WaitHeaders) => { + if eos { + stream.state = StreamState::Closed(CloseReason::EndStream); + self.decrease_current_concurrency(); + } else { + stream.state = StreamState::LocalHalfClosed(ActiveState::WaitData); + } + } + StreamState::Closed(CloseReason::LocalGoAway | CloseReason::LocalRst) => { + return FrameRecvState::Ignore; + } + _ => { + return FrameRecvState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)); + } + }, + } + FrameRecvState::OK + } + + pub(crate) fn recv_data(&mut self, id: u32, eos: bool) -> FrameRecvState { + if id > self.max_recv_id { + return FrameRecvState::Ignore; + } + match self.stream_map.get_mut(&id) { + None => return FrameRecvState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)), + Some(stream) => match &stream.state { + StreamState::Open { + send, + recv: ActiveState::WaitData, + } => { + if eos { + stream.state = RemoteHalfClosed(send.clone()); + } + } + StreamState::LocalHalfClosed(ActiveState::WaitData) => { + if eos { + stream.state = StreamState::Closed(CloseReason::EndStream); + self.decrease_current_concurrency(); + } + } + StreamState::Closed(CloseReason::LocalGoAway | CloseReason::LocalRst) => { + return FrameRecvState::Ignore; + } + _ => { + return FrameRecvState::Err(H2Error::ConnectionError(ErrorCode::ProtocolError)); + } + }, + } + FrameRecvState::OK + } +} + +impl Stream { + pub(crate) fn new( + recv_window: RecvWindow, + send_window: SendWindow, + headers: Frame, + data: BodyDataRef, + ) -> Self { + Self { + recv_window, + send_window, + state: StreamState::Idle, + header: Some(headers), + data, + } + } + + pub(crate) fn is_init_or_active_flow_control(&self) -> bool { + matches!( + self.state, + StreamState::Idle + | StreamState::Open { + recv: ActiveState::WaitData, + .. + } + | StreamState::LocalHalfClosed(ActiveState::WaitData) + ) + } +} diff --git a/ylong_http_client/src/util/mod.rs b/ylong_http_client/src/util/mod.rs index 4c7aade..c8860eb 100644 --- a/ylong_http_client/src/util/mod.rs +++ b/ylong_http_client/src/util/mod.rs @@ -26,12 +26,17 @@ pub(crate) mod pool; pub(crate) mod proxy; pub(crate) mod redirect; +#[cfg(feature = "async")] +pub(crate) mod request; + #[cfg(feature = "__c_openssl")] pub(crate) mod c_openssl; #[cfg(any(feature = "http1_1", feature = "http2"))] pub(crate) mod dispatcher; +#[cfg(feature = "http2")] +pub(crate) mod h2; #[cfg(all(test, feature = "ylong_base"))] pub(crate) mod test_utils; @@ -43,3 +48,5 @@ pub use c_openssl::{ #[cfg(feature = "__tls")] pub use config::{AlpnProtocol, AlpnProtocolList, CertVerifier, ServerCerts}; pub use config::{Proxy, ProxyBuilder, Redirect, Retry, SpeedLimit, Timeout}; +#[cfg(all(feature = "async", feature = "ylong_base", feature = "http2"))] +pub(crate) use h2::{split, Reader, Writer}; diff --git a/ylong_http_client/src/util/request.rs b/ylong_http_client/src/util/request.rs new file mode 100644 index 0000000..0aa01c1 --- /dev/null +++ b/ylong_http_client/src/util/request.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2023 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. + +//! `ylong_http_client` `Request` reference. + +use std::cell::UnsafeCell; +use std::sync::Arc; + +use crate::async_impl::Request; + +pub(crate) struct ReqCell { + request: UnsafeCell, +} + +impl ReqCell { + pub(crate) fn new(request: Request) -> Self { + Self { + request: UnsafeCell::new(request), + } + } +} + +unsafe impl Sync for ReqCell {} + +pub(crate) struct RequestArc { + pub(crate) cell: Arc, +} + +impl RequestArc { + pub(crate) fn new(request: Request) -> Self { + Self { + cell: Arc::new(ReqCell::new(request)), + } + } + + pub(crate) fn ref_mut(&mut self) -> &mut Request { + // SAFETY: In the case of `HTTP`, only one coroutine gets the handle + // at the same time. + unsafe { &mut *self.cell.request.get() } + } +} + +impl Clone for RequestArc { + fn clone(&self) -> Self { + Self { + cell: self.cell.clone(), + } + } +} diff --git a/ylong_http_client/tests/common/mod.rs b/ylong_http_client/tests/common/mod.rs index 702e595..18608a6 100644 --- a/ylong_http_client/tests/common/mod.rs +++ b/ylong_http_client/tests/common/mod.rs @@ -191,7 +191,7 @@ macro_rules! start_http_server { .expect("Set cert error"); let acceptor = acceptor.build(); - let (stream, _) = listener.accept().await.expect("TCP listener accpet error"); + 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(); // SSL negotiation finished successfully diff --git a/ylong_http_client/tests/sdv_async_http2.rs b/ylong_http_client/tests/sdv_async_http2.rs new file mode 100644 index 0000000..13e0e20 --- /dev/null +++ b/ylong_http_client/tests/sdv_async_http2.rs @@ -0,0 +1,304 @@ +// Copyright (c) 2023 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 = "http2", + feature = "tokio_base", + not(feature = "__tls") +))] + +use std::convert::Infallible; +use std::sync::Arc; + +use hyper::body::HttpBody; +use tokio::sync::mpsc::{Receiver, Sender}; +use ylong_http::body::async_impl::Body as RespBody; +use ylong_http::response::status::StatusCode; +use ylong_http_client::async_impl::{Body, Client, Request}; + +pub struct HttpHandle { + pub port: u16, + + // This channel allows the server to notify the client when it is up and running. + pub server_start: Receiver<()>, + + // This channel allows the client to notify the server when it is ready to shut down. + pub client_shutdown: Sender<()>, + + // This channel allows the server to notify the client when it has shut down. + pub server_shutdown: Receiver<()>, +} + +async fn server_fn( + req: hyper::Request, +) -> Result, Infallible> { + let (parts, mut body) = req.into_parts(); + assert_eq!( + parts.method.to_string(), + "GET", + "Assert request method failed" + ); + assert_eq!( + format!("{:?}", parts.version), + "HTTP/2.0", + "Assert request version failed" + ); + + let mut size = 0; + loop { + match body.data().await { + None => { + break; + } + Some(Ok(bytes)) => { + size += bytes.len(); + } + Some(Err(_e)) => { + panic!("server read request body data occurs error"); + } + } + } + assert_eq!( + size, + 10 * 1024 * 1024, + "Assert request body data length failed" + ); + + let body_data = vec![b'q'; 10 * 1024 * 1024]; + let response = hyper::Response::builder() + .version(hyper::Version::HTTP_2) + .status(hyper::StatusCode::OK) + .body(hyper::Body::from(body_data)) + .expect("build hyper response failed"); + Ok(response) +} + +#[macro_export] +macro_rules! start_http_server { + ( + HTTP; + $server_fn: ident + ) => {{ + use std::convert::Infallible; + + use hyper::service::{make_service_fn, service_fn}; + 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("127.0.0.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, + } + }}; +} + +#[test] +fn sdv_async_h2_client_send_request() { + let rt = tokio::runtime::Builder::new_multi_thread() + .worker_threads(4) + .enable_all() + .build() + .expect("init runtime failed !"); + + 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 body_date = vec![b'q'; 10 * 1024 * 1024]; + + let client = Client::builder() + .http2_prior_knowledge() + .set_stream_recv_window_size(100 * 1024) + .build() + .expect("Build Client failed."); + + let request = Request::builder() + .version("HTTP/2.0") + .url(format!("{}:{}", "127.0.0.1", handle.port).as_str()) + .method("GET") + .body(Body::slice(body_date)) + .expect("Client build Request failed."); + + rt.block_on(async move { + let mut response = client.request(request).await.expect("get response failed"); + assert_eq!(response.status(), StatusCode::OK); + + let mut buf = [0u8; 4096]; + let mut size = 0; + loop { + let read = response + .body_mut() + .data(&mut buf[..]) + .await + .expect("Response body read failed"); + if read == 0 { + break; + } + size += read; + } + assert_eq!( + size, + 10 * 1024 * 1024, + "Assert response body data length failed" + ); + + handle + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }) +} + +#[test] +fn sdv_async_h2_client_send_request_concurrently() { + let rt = tokio::runtime::Builder::new_multi_thread() + .worker_threads(4) + .enable_all() + .build() + .expect("Build Runtime failed."); + + let client = Client::builder() + .http2_prior_knowledge() + .set_stream_recv_window_size(100 * 1024) + .build() + .expect("Build Client failed."); + + 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_interface = Arc::new(client); + let mut shut_downs = vec![]; + + for _i in 0..5 { + let client = client_interface.clone(); + let handle = rt.spawn(async move { + let body_date = vec![b'q'; 1024 * 1024 * 10]; + + let request = Request::builder() + .version("HTTP/2.0") + .url(format!("{}:{}", "127.0.0.1", handle.port).as_str()) + .method("GET") + .body(Body::slice(body_date)) + .expect("Client build Request failed."); + + let mut response = client.request(request).await.expect("Get Response failed."); + let mut buf = [0u8; 4096]; + let mut size = 0; + + loop { + let read = response + .body_mut() + .data(&mut buf[..]) + .await + .expect("Response body read failed"); + if read == 0 { + break; + } + size += read; + } + assert_eq!( + size, + 10 * 1024 * 1024, + "Assert response body data length failed" + ); + }); + + shut_downs.push(handle); + } + + for shut_down in shut_downs { + rt.block_on(shut_down) + .expect("Runtime wait for server shutdown failed"); + } + + rt.block_on(async move { + handle + .client_shutdown + .send(()) + .await + .expect("send client shutdown"); + handle + .server_shutdown + .recv() + .await + .expect("server shutdown"); + }); +} -- Gitee