Skip to content

Instantly share code, notes, and snippets.

@wegry
Last active August 4, 2019 22:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wegry/d1bbb515fe754d80feb68de29df37551 to your computer and use it in GitHub Desktop.
Save wegry/d1bbb515fe754d80feb68de29df37551 to your computer and use it in GitHub Desktop.
Basic Rust 2018 Static File Server
use colored::*;
use futures::{future, Future};
use hyper::header::{HeaderName, HeaderValue};
use hyper::service::service_fn;
use hyper::{Body, Request, Response, Server};
use hyper::{Method, StatusCode};
use lazy_static::lazy_static;
use maplit::btreemap;
use std::collections::BTreeMap;
use std::path::Path;
lazy_static! {
static ref MIME_BY_EXTENSION: BTreeMap<String, String> = {
let owned_version = btreemap![
"css" => "text/css",
"js" => "text/javascript",
"wasm" => "application/wasm",
"woff2" => "font/woff2"
];
owned_version
.iter()
.map(|(key, val)| (String::from(*key), String::from(*val)))
.collect()
};
}
// Just a simple type alias
type BoxFut = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send>;
fn poor_mans_static_server(req: Request<Body>) -> BoxFut {
let mut response = Response::new(Body::empty());
match (req.method(), req.uri().path()) {
(&Method::GET, path) => {
// first, we serve static files
let fs_path = match path {
"" | "/" => String::from("index.html"),
_ => format!(".{}", path),
};
// Cribbed from https://github.com/sparkles-web/sparkles/blob/e3e10dc2e684f4b6c9749b784f34ff788cf379d2/src/server.rs#L144-L172
// ... you trying to do something bad?
// TODO: Disallow current dir too...
if fs_path.contains("../") {
*response.status_mut() = StatusCode::NOT_FOUND;
// GET OUT
return Box::new(future::ok(response));
}
// Set content type here...
let path_creator = fs_path.clone();
let as_path = Path::new(&path_creator);
if as_path.is_file() {
let text = vec![std::fs::read(fs_path).unwrap()];
if let Some(extension) = as_path.extension() {
if let Some(non_html_mime) = MIME_BY_EXTENSION.get(extension.to_str().unwrap())
{
(*response.headers_mut()).insert(
HeaderName::from_static("content-type"),
HeaderValue::from_static(non_html_mime),
);
};
} else {
eprintln!("Content type unset for {:?}", as_path);
}
*response.body_mut() =
Body::wrap_stream(futures::stream::iter_ok::<_, ::std::io::Error>(text));
}
}
_ => {
*response.status_mut() = StatusCode::NOT_FOUND;
}
};
Box::new(future::ok(response))
}
pub fn static_files() {
// This is our socket address...
let addr = ([127, 0, 0, 1], 3000).into();
let server = Server::bind(&addr)
.serve(|| service_fn(poor_mans_static_server))
.map_err(|e| eprintln!("server error: {}", e));
println!(
"{}",
format!("🚀 Listening at http://{}", addr).bright_blue()
);
hyper::rt::run(server)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment