maj-async-std #4
|
@ -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(),
|
|
||||||
_ => Ok(maj::Response {
|
|
||||||
status: maj::StatusCode::NotFound,
|
|
||||||
meta: "Not found".to_string(),
|
|
||||||
body: vec![],
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
route!(req.url.path(), {
|
||||||
|
(/) => index(req);
|
||||||
|
(/"majc") => majc(req);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Response::not_found())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) => {
|
||||||
println!("ok");
|
if let ResponseError::ResponseMetaTooLong = why {
|
||||||
} else {
|
println!("ok");
|
||||||
panic!("wanted ResponseError::ResponseMetaTooLong")
|
} else {
|
||||||
},
|
panic!("wanted ResponseError::ResponseMetaTooLong")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue