response building helpers

This commit is contained in:
Cadey Ratio 2020-07-27 20:50:42 -04:00
parent dd6f7f4e7d
commit b47c1a69a2
2 changed files with 86 additions and 44 deletions

View File

@ -1,4 +1,9 @@
use async_std::task; use async_std::task;
use maj::{
route, seg,
server::{Error, Handler as MajHandler, Request},
split, Response,
};
use rustls::internal::pemfile::{certs, rsa_private_keys}; use rustls::internal::pemfile::{certs, rsa_private_keys};
use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig};
use std::fs::File; use std::fs::File;
@ -50,7 +55,12 @@ fn main() -> Result<(), maj::server::Error> {
let certs = load_certs(&opts.cert)?; let certs = load_certs(&opts.cert)?;
let mut keys = load_keys(&opts.key)?; let mut keys = load_keys(&opts.key)?;
log::info!("{:?}", opts); log::info!(
"serving gemini://{} on {}:{}",
opts.hostname,
opts.host,
opts.port
);
let mut config = ServerConfig::new(NoClientAuth::new()); let mut config = ServerConfig::new(NoClientAuth::new());
config config
@ -73,50 +83,35 @@ struct Handler {
hostname: String, hostname: String,
} }
fn index() -> Result<maj::Response, maj::server::Error> { fn index(_req: Request) -> Result<maj::Response, maj::server::Error> {
let msg = include_bytes!("index.gmi"); let msg = include_bytes!("index.gmi");
Ok(Response::gemini(msg.to_vec()))
Ok(maj::Response {
status: maj::StatusCode::Success,
meta: "text/gemini".to_string(),
body: msg.to_vec(),
})
} }
fn majc() -> Result<maj::Response, maj::server::Error> { fn majc(_req: Request) -> Result<maj::Response, maj::server::Error> {
let msg = include_bytes!("majc.gmi"); let msg = include_bytes!("majc.gmi");
Ok(Response::gemini(msg.to_vec()))
Ok(maj::Response {
status: maj::StatusCode::Success,
meta: "text/gemini".to_string(),
body: msg.to_vec(),
})
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl maj::server::Handler for Handler { impl MajHandler for Handler {
async fn handle(&self, r: maj::server::Request) -> Result<maj::Response, maj::server::Error> { async fn handle(&self, req: Request) -> Result<Response, Error> {
if r.url.has_host() && r.url.host_str().unwrap().to_string() != self.hostname { if req.url.has_host() && req.url.host_str().unwrap().to_string() != self.hostname {
return Ok(maj::Response { return Ok(Response::no_proxy());
status: maj::StatusCode::ProxyRequestRefused,
meta: "Wrong host".to_string(),
body: vec![],
});
} }
match r.url.path() { if req.url.path() == "" {
"" => Ok(maj::Response { return Ok(Response::perm_redirect(format!(
status: maj::StatusCode::PermanentRedirect, "gemini://{}/",
meta: format!("gemini://{}/", self.hostname), self.hostname
body: vec![], )));
}), }
"/" => index(),
"/majc" => majc(), route!(req.url.path(), {
_ => Ok(maj::Response { (/) => index(req);
status: maj::StatusCode::NotFound, (/"majc") => majc(req);
meta: "Not found".to_string(), });
body: vec![],
}), Ok(Response::not_found())
}
} }
} }

View File

@ -1,6 +1,6 @@
use crate::StatusCode; use crate::{gemini, StatusCode};
use num::FromPrimitive; use num::FromPrimitive;
use std::io::{prelude::*, ErrorKind, self}; use std::io::{self, prelude::*, ErrorKind};
/// A Gemini response as specified in [the spec](https://gemini.circumlunar.space/docs/specification.html). /// A Gemini response as specified in [the spec](https://gemini.circumlunar.space/docs/specification.html).
#[derive(Default)] #[derive(Default)]
@ -10,6 +10,51 @@ pub struct Response {
pub body: Vec<u8>, pub body: Vec<u8>,
} }
impl Response {
pub fn gemini(body: Vec<u8>) -> Response {
Response {
status: StatusCode::Success,
meta: "text/gemini".to_string(),
body: body,
}
}
pub fn render(body: Vec<gemini::Node>) -> Response {
let mut buf: Vec<u8> = vec![];
gemini::render(body, &mut buf).unwrap();
Response {
status: StatusCode::Success,
meta: "text/gemini".to_string(),
body: buf,
}
}
pub fn perm_redirect(to: String) -> Response {
Response {
status: StatusCode::PermanentRedirect,
meta: to,
body: vec![],
}
}
pub fn no_proxy() -> Response {
Response {
status: StatusCode::ProxyRequestRefused,
meta: "Wrong host".to_string(),
body: vec![],
}
}
pub fn not_found() -> Response {
Response {
status: StatusCode::NotFound,
meta: "Not found".to_string(),
body: vec![],
}
}
}
/// The parser state. /// The parser state.
#[derive(Debug)] #[derive(Debug)]
enum State { enum State {
@ -103,7 +148,7 @@ impl Response {
} }
_ => { _ => {
if data.len() == 1024 { if data.len() == 1024 {
return Err(Error::ResponseMetaTooLong) return Err(Error::ResponseMetaTooLong);
} }
data.push(buf[0]); data.push(buf[0]);
} }
@ -168,11 +213,13 @@ mod tests {
match Response::parse(&mut fin) { match Response::parse(&mut fin) {
Ok(_) => panic!("wanted error but didn't get one"), Ok(_) => panic!("wanted error but didn't get one"),
Err(why) => if let ResponseError::ResponseMetaTooLong = why { Err(why) => {
if let ResponseError::ResponseMetaTooLong = why {
println!("ok"); println!("ok");
} else { } else {
panic!("wanted ResponseError::ResponseMetaTooLong") panic!("wanted ResponseError::ResponseMetaTooLong")
}, }
}
} }
} }
} }