A Synchronous HTTP framework for Rust
Foxhole is a simple, fast, synchronous framework built for finishing your projects.
- No async. Binary bloat and poor ergonomics
- Minimal dependencies
- Blazing fast performance (~600k req/sec on a ryzen 7 5700x with
wrk
) May be outdated. - Built-in threading system that allows you to efficiently handle requests.
- Minimal build size, ~500kb when stripped.
- Uses
http
, a model library you may already be familiar with. - Magic function handlers! See Getting Started.
- Powerful routing system
- Near full Http1.1 support
- Https support in the works. Available on the under feature "tls". largely Untested!
- Http2 support coming.
Foxhole uses a set of magic handler systems and traits to simplify handling requests and responses.
Here's a starting example of a Hello World server.
use foxhole::{action::Html, App, Http1, Method::Get, Router};
fn get() -> Html {
Html(String::from("<h1> Foxhole! </h1>"))
}
fn main() {
let router = Router::new().add_route("/", Get(get));
println!("Running on '127.0.0.1:8080'");
#[cfg(test)]
App::builder(router).run::<Http1>("127.0.0.1:8080");
}
Let's break this down into its components.
Function parameters can act as both getters and guards preventing a system from running in foxhole
.
Any type that implements the trait Resolve
is capable of acting as a parameter.
foxhole
will try to provide the most common guards and getters you will use but few are implemented currently.
The following is basic implementation of Token
getter.
use foxhole::{Resolve, ResolveGuard, RequestState, Captures};
struct Token(String);
impl Resolve for Token {
type Output<'a> = Self;
fn resolve(
ctx: &RequestState,
_captures: &mut Captures,
) -> ResolveGuard<Self> {
let Some(v) = ctx.request.headers().get("authorization") else {
return ResolveGuard::None;
};
// You should handle the `Err` case in real code
ResolveGuard::Value(Token(v.to_str().unwrap().to_string()))
}
}
fn get(Token(_token): Token) { }
Systems are required to return a value that implements Action
.
Additionally note the existence of IntoResponse
which can be implemented instead for types that are always a response.
If a type returns None
out of Action
a response will not be sent and routing will continue to the fallback. On failure of the fallback, a 500 will be sent to the client.
use foxhole::{IntoResponse, Response};
// This is a reimplementation of the provided `Html` type.
struct Html(String);
impl IntoResponse for Html {
fn response(self) -> Response {
let bytes = self.0.into_bytes();
http::Response::builder()
.status(200)
.header("Content-Type", "text/html; charset=utf-8")
.header("Content-Length", format!("{}", bytes.len()))
.body(bytes)
.unwrap()
}
}
fn page() -> Html {
Html("<h1> Hey Friend </h1>".to_string())
}
Feel free to open an issue or pull request if you have suggestions for features or improvements!
MIT license (LICENSE or https://opensource.org/licenses/MIT)