use std::net::SocketAddr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use hotaru_core::connection::{ConnMeta, ConnStream, HotaruRead, HotaruWrite};
use hotaru_core::protocol::Channel;
use tokio::sync::Mutex;
use crate::channel::http_channel::HttpChannel;
use crate::message::body::HttpBody;
use crate::message::http_value::StatusCode;
use crate::message::request::HttpRequest;
use crate::message::response::HttpResponse;
use crate::protocol::error::HttpError;
use crate::security::safety::HttpSafety;
pub struct Http1Channel<W: ConnStream> {
reader: Arc<Mutex<<W::ReadHalf as HotaruRead>::Buffered>>,
writer: Arc<Mutex<<W::WriteHalf as HotaruWrite>::Buffered>>,
meta: Arc<W::Meta>,
open: Arc<AtomicBool>,
safety: Arc<HttpSafety>,
}
impl<W: ConnStream> Clone for Http1Channel<W> {
fn clone(&self) -> Self {
Self {
reader: self.reader.clone(),
writer: self.writer.clone(),
meta: self.meta.clone(),
open: self.open.clone(),
safety: self.safety.clone(),
}
}
}
impl<W: ConnStream> Channel for Http1Channel<W> {
fn is_open(&self) -> bool {
self.open.load(Ordering::Acquire)
}
fn close(&self) {
self.open.store(false, Ordering::Release);
}
}
impl<W> HttpChannel for Http1Channel<W>
where
W: ConnStream,
W::ReadHalf: HotaruRead<Error = std::io::Error>,
W::WriteHalf: HotaruWrite<Error = std::io::Error>,
{
async fn parse_request(&self, safety: &HttpSafety) -> Result<HttpRequest, HttpError> {
let mut reader = self.reader.lock().await;
let request = HttpRequest::parse_lazy(&mut *reader, safety, false).await;
if request.meta.path().is_empty() && request.meta.header.is_empty() {
self.open.store(false, Ordering::Release);
return Err(HttpError::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"client closed connection",
)));
}
Ok(request)
}
async fn send_response(&self, response: HttpResponse) -> Result<(), HttpError> {
let mut writer = self.writer.lock().await;
response.send(&mut *writer).await.map_err(HttpError::Io)?;
writer.flush().await.map_err(HttpError::Io)?;
Ok(())
}
async fn send_request(&self, request: HttpRequest) -> Result<(), HttpError> {
let mut writer = self.writer.lock().await;
if let Err(err) = request.send(&mut *writer).await {
self.open.store(false, Ordering::Release);
return Err(HttpError::Io(err));
}
if let Err(err) = writer.flush().await {
self.open.store(false, Ordering::Release);
return Err(HttpError::Io(err));
}
Ok(())
}
async fn parse_response(&self, safety: &HttpSafety) -> Result<HttpResponse, HttpError> {
let mut reader = self.reader.lock().await;
let response = HttpResponse::parse_lazy(&mut *reader, safety, false).await;
if response.meta.start_line.status_code() == StatusCode::OK
&& response.meta.header.is_empty()
&& matches!(response.body, HttpBody::Unparsed)
{
self.open.store(false, Ordering::Release);
return Err(HttpError::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"server closed connection",
)));
}
Ok(response)
}
fn local_addr(&self) -> Option<SocketAddr> {
self.meta.local_addr()
}
fn remote_addr(&self) -> Option<SocketAddr> {
self.meta.remote_addr()
}
}
impl<W> Http1Channel<W>
where
W: ConnStream,
W::ReadHalf: HotaruRead<Error = std::io::Error>,
W::WriteHalf: HotaruWrite<Error = std::io::Error>,
{
pub fn new(
reader: <W::ReadHalf as HotaruRead>::Buffered,
writer: <W::WriteHalf as HotaruWrite>::Buffered,
meta: W::Meta,
safety: Arc<HttpSafety>,
) -> Self {
Self {
reader: Arc::new(Mutex::new(reader)),
writer: Arc::new(Mutex::new(writer)),
meta: Arc::new(meta),
open: Arc::new(AtomicBool::new(true)),
safety,
}
}
pub fn safety(&self) -> &HttpSafety {
&self.safety
}
pub fn safety_arc(&self) -> Arc<HttpSafety> {
self.safety.clone()
}
}