use crate::{Response, StatusCode}; use async_trait::async_trait; use rustls::{Certificate, Session}; use std::{error::Error as StdError, sync::Arc}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::{net::TcpListener, stream::StreamExt}; use tokio_rustls::TlsAcceptor; use url::Url; /// A Gemini request and its associated metadata. #[allow(dead_code)] pub struct Request { pub url: Url, pub certs: Option>, } pub type Error = Box; #[allow(dead_code, unused_assignments, unused_mut, unused_variables)] mod routes; pub use routes::*; #[async_trait] pub trait Handler { async fn handle(&self, r: Request) -> Result; } pub async fn serve(h: T, cfg: rustls::ServerConfig, host: String, port: u16) -> Result<(), Error> where T: Handler, { let cfg = Arc::new(cfg); let mut listener = TcpListener::bind(&format!("{}:{}", host, port)).await?; let mut incoming = listener.incoming(); let acceptor = TlsAcceptor::from(cfg.clone()); while let Some(stream) = incoming.next().await { let stream = stream?; let addr = stream.peer_addr().unwrap(); let acceptor = acceptor.clone(); let mut stream = acceptor.accept(stream).await?; let mut rd = BufReader::new(&mut stream); let mut u = String::new(); rd.read_line(&mut u).await?; if u.len() > 1025 { stream .write(format!("{} URL too long", StatusCode::BadRequest as u8).as_bytes()) .await?; continue; } let u = Url::parse(&u)?; match h .handle(Request { url: u.clone(), certs: stream.get_ref().1.get_peer_certificates(), }) .await { Ok(resp) => { stream .write(format!("{} {}\r\n", resp.status as u8, resp.meta).as_bytes()) .await?; stream.write(&resp.body).await?; log::info!("{}: {} {:?}", addr, u, resp.status); } Err(why) => { stream .write(format!("{} {:?}\r\n", StatusCode::PermanentFailure as u8, why).as_bytes()) .await?; log::error!("{}: {}: {:?}", addr, u, why); } }; } Ok(()) } pub async fn serve_plain(h: T, host: String, port: u16) -> Result<(), Error> where T: Handler, { let mut listener = TcpListener::bind(&format!("{}:{}", host, port)).await?; let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { let mut stream = stream?; let mut rd = BufReader::new(&mut stream); let mut u = String::new(); rd.read_line(&mut u).await?; if u.len() > 1025 { stream .write(format!("{} URL too long", StatusCode::BadRequest as u8).as_bytes()) .await?; continue; } let u = Url::parse(&u)?; match h .handle(Request { url: u.clone(), certs: None, }) .await { Ok(resp) => { stream .write(format!("{} {}", resp.status as u8, resp.meta).as_bytes()) .await?; stream.write(&resp.body).await?; log::info!("{}: {} {:?}", stream.peer_addr().unwrap(), u, resp.status); } Err(why) => { stream .write(format!("{} {:?}", StatusCode::PermanentFailure as u8, why).as_bytes()) .await?; log::error!("{}: {}: {:?}", stream.peer_addr().unwrap(), u, why); } }; } Ok(()) }