mod app;
mod connect;
mod error;
mod stream;
use std::sync::Arc;
use sciparse::address::ip_socket_addr::ScionSocketIpAddr;
use tokio::sync::Mutex;
use self::{app::Http3ClientApp, connect::connect};
pub use self::{
error::{EstablishError, RequestError},
stream::{H3DuplexStream, H3ResponseBody, RequestBodyWriter, ResponseFut},
};
pub use crate::h3::common::H3Error;
use crate::{
quic::{config::QuicConfig, connection::ConnectionHandle},
socket::GenericScionUdpSocket,
};
pub struct Http3Client {
remote: ScionSocketIpAddr,
socket: Arc<dyn GenericScionUdpSocket>,
server_name: Option<String>,
config: QuicConfig,
current: Mutex<Option<ConnectionHandle<Http3ClientApp>>>,
}
impl Http3Client {
pub fn new(
remote: ScionSocketIpAddr,
socket: Arc<dyn GenericScionUdpSocket>,
server_name: Option<String>,
) -> Self {
Self::with_config(remote, socket, server_name, QuicConfig::default())
}
pub fn with_config(
remote: ScionSocketIpAddr,
socket: Arc<dyn GenericScionUdpSocket>,
server_name: Option<String>,
config: QuicConfig,
) -> Self {
Self {
remote,
socket,
server_name,
config,
current: Mutex::new(None),
}
}
pub async fn request(
&self,
req: http::Request<()>,
) -> Result<(ResponseFut, RequestBodyWriter), RequestError> {
let handle = self.get_connection().await?;
stream::initiate_request(&handle, req)
}
pub async fn connect(&self) -> Result<(), EstablishError> {
self.get_connection().await?;
Ok(())
}
async fn get_connection(&self) -> Result<ConnectionHandle<Http3ClientApp>, EstablishError> {
let mut guard = self.current.lock().await;
if let Some(handle) = guard.as_ref() {
let closed = handle.lock().inner.is_closed();
if !closed {
return Ok(handle.clone());
}
}
let quiche_config = self
.config
.to_quiche_config()
.map_err(EstablishError::Quic)?;
let handle = connect(
self.remote,
self.socket.clone(),
self.server_name.clone(),
quiche_config,
self.config.handshake_timeout,
)
.await?;
*guard = Some(handle.clone());
Ok(handle)
}
#[doc(hidden)]
pub async fn tracked_stream_state(&self) -> usize {
let guard = self.current.lock().await;
let Some(handle) = guard.as_ref() else {
return 0;
};
let conn = handle.lock();
conn.app.streams.len() + conn.app.response_heads.len()
}
}