use super::*;
pub(crate) async fn serve_h2_connection<T>(
env: ConnectionEnv,
peers: StreamPeers,
connection: Result<ConnectionH2<T>, String>,
) where
T: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
{
let mut connection: ConnectionH2<T> = match connection {
Ok(connection) => connection,
Err(_) => return,
};
loop {
match connection.accept().await {
Some(Ok((request, send_response))) => {
spawn(h2_request_task(
H2RequestEnv {
server: env.server.clone(),
request_config: env.request_config,
peers,
},
request,
send_response,
));
}
Some(Err(_)) => return,
None => return,
}
}
}
pub(crate) async fn h2_request_task(
env: H2RequestEnv,
request: ProtocolRequest<H2RecvStream>,
send_response: h2::server::SendResponse<Bytes>,
) {
let H2RequestEnv {
server,
request_config,
peers,
} = env;
let (parts, mut recv_stream): (http::request::Parts, H2RecvStream) = request.into_parts();
let mut framer: H2Framer = H2Framer::new(send_response);
let mut body: Vec<u8> = Vec::new();
if let Err(error) =
collect_h2_body(&mut recv_stream, &request_config, &parts.headers, &mut body).await
{
h2_fail_request(
&H2RequestEnv {
server,
request_config,
peers,
},
&mut framer,
HttpVersion::Http2,
error,
)
.await;
return;
}
let built: Result<Request, RequestError> = build_protocol_request(
&request_config,
ProtocolRequestHead {
version: HttpVersion::Http2,
method: parts.method.clone(),
parts,
body,
},
);
let request: Request = match built {
Ok(request) => request,
Err(error) => {
h2_fail_request(
&H2RequestEnv {
server,
request_config,
peers,
},
&mut framer,
HttpVersion::Http2,
error,
)
.await;
return;
}
};
let is_head: bool = request.get_method().is_head();
let (stream, peer): (Stream, DuplexStream) = build_duplex_transport(request_config, peers);
let lifecycle: ServerLifecycle = ServerLifecycle::new(server, request);
let joined: Result<((), ()), ResponseError> = futures::try_join!(
async {
lifecycle.run(stream).await;
Ok::<(), ResponseError>(())
},
pump_response(&mut framer, peer, RESPONSE_HEAD_MAX, is_head)
);
let pump_result: Result<(), ResponseError> = joined.map(|(_, pumped): ((), ())| pumped);
if pump_result.is_err() {
framer.reset();
}
}
pub(crate) async fn h2_fail_request(
env: &H2RequestEnv,
framer: &mut H2Framer,
version: HttpVersion,
error: RequestError,
) {
let mut ctx: Context = Context::default();
let mut request: Request = Request::default();
request.set_version(version.clone());
ctx.set_request(request);
ctx.get_mut_response().reset().set_version(version);
let (mut stream, peer): (Stream, DuplexStream) =
build_duplex_transport(env.request_config, env.peers);
let is_head: bool = false;
let server: Arc<Server> = env.server.clone();
let (_, pump_result): ((), Result<(), ResponseError>) = join!(
async move {
server
.handle_request_error(&mut stream, &mut ctx, &error)
.await;
drop(stream);
},
pump_response(framer, peer, RESPONSE_HEAD_MAX, is_head)
);
if pump_result.is_err() {
framer.reset();
}
}
pub(crate) async fn collect_h2_body(
recv_stream: &mut H2RecvStream,
request_config: &RequestConfig,
headers: &ProtocolHeaders,
body: &mut Vec<u8>,
) -> Result<(), RequestError> {
let max_body_size: usize = request_config.get_max_body_size();
let declared: Option<usize> = declared_content_length(headers, max_body_size)?;
let read_timeout_ms: u64 = request_config.get_read_timeout_ms();
if read_timeout_ms == DEFAULT_LOW_SECURITY_READ_TIMEOUT_MS {
collect_h2_body_until_end(recv_stream, max_body_size, body).await?;
} else {
let bound: Duration = Duration::from_millis(read_timeout_ms);
match timeout(
bound,
collect_h2_body_until_end(recv_stream, max_body_size, body),
)
.await
{
Ok(collected) => collected?,
Err(_) => return Err(RequestError::ReadTimeout(HttpStatus::RequestTimeout)),
}
}
match declared {
None => Ok(()),
Some(declared) => match body.len().cmp(&declared) {
Ordering::Equal => Ok(()),
_ => Err(RequestError::Request(CONTENT_LENGTH_MISMATCH.to_owned())),
},
}
}
async fn collect_h2_body_until_end(
recv_stream: &mut H2RecvStream,
max_body_size: usize,
body: &mut Vec<u8>,
) -> Result<(), RequestError> {
loop {
let received: Option<Result<Bytes, H2Error>> = recv_stream.data().await;
let Some(received) = received else {
return Ok(());
};
let data: Bytes =
received.map_err(|error: H2Error| RequestError::Request(error.to_string()))?;
let data_len: usize = data.len();
let collected: Result<(), RequestError> = push_bounded_body(max_body_size, body, data);
let _: Result<(), H2Error> = recv_stream.flow_control().release_capacity(data_len);
collected?;
}
}