maj/src/server/mod.rs

155 lines
4.8 KiB
Rust
Raw Normal View History

2020-07-26 00:16:07 +00:00
use crate::{Response, StatusCode};
use async_trait::async_trait;
use rustls::{Certificate, Session};
2020-07-26 12:21:02 +00:00
use std::{error::Error as StdError, net::SocketAddr, sync::Arc};
2020-07-26 00:16:07 +00:00
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::{net::TcpListener, stream::StreamExt};
use tokio_rustls::TlsAcceptor;
2020-07-25 15:39:23 +00:00
use url::Url;
2020-07-25 21:51:37 +00:00
/// A Gemini request and its associated metadata.
2020-07-26 00:16:07 +00:00
#[allow(dead_code)]
2020-07-25 21:51:37 +00:00
pub struct Request {
2020-07-26 00:16:07 +00:00
pub url: Url,
pub certs: Option<Vec<Certificate>>,
2020-07-25 21:51:37 +00:00
}
pub type Error = Box<dyn StdError + Sync + Send>;
2020-07-25 21:48:09 +00:00
#[allow(dead_code, unused_assignments, unused_mut, unused_variables)]
mod routes;
pub use routes::*;
2020-07-26 00:16:07 +00:00
#[async_trait]
2020-07-25 15:39:23 +00:00
pub trait Handler {
2020-07-26 00:16:07 +00:00
async fn handle(&self, r: Request) -> Result<Response, Error>;
2020-07-25 15:39:23 +00:00
}
2020-07-26 12:21:02 +00:00
pub async fn serve(
h: &(dyn Handler + Sync),
cfg: rustls::ServerConfig,
host: String,
port: u16,
) -> Result<(), Error>
2020-07-26 00:16:07 +00:00
where
{
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();
2020-07-26 12:21:02 +00:00
let fut = async {
let acceptor = acceptor.clone();
let result = acceptor.accept(stream).await;
if result.is_err() {
return;
}
let mut stream = result.unwrap();
let mut rd = BufReader::new(&mut stream);
let mut u = String::new();
if let Err(why) = rd.read_line(&mut u).await {
log::error!("can't read request from {}: {:?}", addr, why);
let _ = stream
.write(format!("{} Invalid URL", StatusCode::BadRequest as u8).as_bytes())
.await;
return;
}
u = u.trim().to_string();
if u.len() >= 1025 {
let _ = stream
.write(format!("{} URL too long", StatusCode::BadRequest as u8).as_bytes())
.await;
return;
}
2020-07-26 00:16:07 +00:00
2020-07-26 12:21:02 +00:00
if u.starts_with("//") {
u = format!("gemini:{}", u);
2020-07-26 00:16:07 +00:00
}
2020-07-26 12:21:02 +00:00
match Url::parse(&u) {
Err(why) => {
let _ = stream
.write(
format!("{} bad URL: {:?}", StatusCode::BadRequest as u8, why)
.as_bytes(),
)
.await;
}
Ok(u) => {
if u.scheme() != "gemini" {
let _ = stream
.write(
format!(
"{} Cannot handle that kind of url",
StatusCode::ProxyRequestRefused as u8
)
.as_bytes(),
)
.await;
return;
}
if let Some(u_port) = u.port() {
if port != u_port {
let _ = stream
.write(
format!(
"{} Cannot handle that kind of url",
StatusCode::ProxyRequestRefused as u8
)
.as_bytes(),
)
.await;
return;
}
}
tokio::join!(handle(
h,
Request {
url: u.clone(),
certs: stream.get_ref().1.get_peer_certificates(),
},
&mut stream,
addr,
));
}
2020-07-26 00:16:07 +00:00
}
};
2020-07-26 12:21:02 +00:00
tokio::join!(fut);
2020-07-26 00:16:07 +00:00
}
2020-07-25 15:39:23 +00:00
Ok(())
}
2020-07-26 12:21:02 +00:00
async fn handle<T>(h: &(dyn Handler + Sync), req: Request, stream: &mut T, addr: SocketAddr)
2020-07-26 00:16:07 +00:00
where
2020-07-26 12:21:02 +00:00
T: AsyncWriteExt + Unpin,
2020-07-26 00:16:07 +00:00
{
2020-07-26 12:21:02 +00:00
let u = req.url.clone();
match h.handle(req).await {
Ok(resp) => {
2020-07-26 00:16:07 +00:00
stream
2020-07-26 12:21:02 +00:00
.write(format!("{} {}\r\n", resp.status as u8, resp.meta).as_bytes())
.await
.unwrap();
stream.write(&resp.body).await.unwrap();
log::info!("{}: {} {:?}", addr, u, resp.status);
2020-07-26 00:16:07 +00:00
}
2020-07-26 12:21:02 +00:00
Err(why) => {
stream
.write(format!("{} {:?}\r\n", StatusCode::PermanentFailure as u8, why).as_bytes())
.await
.unwrap();
log::error!("{}: {}: {:?}", addr, u, why);
}
};
2020-07-25 15:39:23 +00:00
}