#http-framework #同步 #响应 #系统 #项目 #请求 #性能

foxhole

一个闪电般的 HTTP 框架

3 个版本 (重大更新)

0.4.0 2024年3月25日
0.3.0 2023年10月10日
0.2.2 2023年10月9日

168HTTP 服务器

每月下载 22

MIT 许可证

580KB
1.5K SLoC

狐狸洞

一个用于 Rust 的同步 HTTP 框架

Minimum Supported Rust Version Crates.io Docs.rs Code Size Maintained License

狐狸洞是一个简单、快速、同步的框架,旨在帮助您完成项目。

有偏见的决策

  • 无异步。二进制膨胀和差的用户体验
  • 最小化依赖

功能

  • 闪电般的性能 (~600k req/sec 在 ryzen 7 5700x 上使用 wrk) 可能已过时。
  • 内置的线程系统,允许您高效地处理请求。
  • 最小构建大小,剥离后约为 ~500kb。
  • 使用 http,您可能已经熟悉的模型库。
  • 魔法函数处理程序!请参阅 入门指南
  • 独特的强大路由系统
  • 几乎完全支持 Http1.1
  • 正在开发中的 Https 支持。在“功能”下可用,大部分未测试!
  • 即将到来的 Http2 支持。

入门指南

狐狸洞使用一组处理程序系统和路由模块来处理请求和响应。
以下是一个 Hello World 服务器的入门示例。

use foxhole::{action::Html, connection::Http1, resolve::Get, App, sys, Scope};

fn get(_get: Get) -> Html {
    Html(String::from("<h1> Foxhole! </h1>"))
}

fn main() {
    let scope = Scope::new(sys![get]);

    println!("Running on '127.0.0.1:8080'");

    #[cfg(test)]
    App::builder(scope)
        .run::<Http1>("127.0.0.1:8080");
} 

让我们将其分解为其组件。

路由

作用域树将按照其部分逐步遍历 URL,首先从根开始。它将尝试按顺序运行它经过的每个节点的所有系统。一旦收到响应,它将停止遍历 URL 并立即响应。

假设我们有树 Scope::new(sys![auth]).route("page", sys![get_page]) 和请求 /page

在这个例子中,路由器首先会在树的根处调用auth。如果auth返回响应,比如用户未被授权并且我们希望尽早响应,那么我们就在这里停止并响应401。否则,我们将继续到下一个节点get_page

如果到树的末尾都没有返回响应,服务器将自动返回404。这将在未来可配置。

参数/守卫

函数参数可以在foxhole中作为获取器和守卫。

在上面的例子中,Get作为守卫确保系统只在GET请求上运行。

任何实现了Resolve特质的类型都可以用作参数。

foxhole将尝试提供最常用的守卫和获取器,但目前实现得很少。

示例

use foxhole::{http::Method, PathIter, RequestState, resolve::{Resolve, ResolveGuard}};

pub struct Get;

impl<'a> Resolve<'a> for Get {
    type Output = Self;

    fn resolve(ctx: &'a RequestState, _path_iter: &mut PathIter) -> ResolveGuard<Self::Output> {
        if ctx.request.method() == Method::GET {
            ResolveGuard::Value(Get)
        } else {
            ResolveGuard::None
        }
    }
}

返回类型

系统必须返回一个实现Action的值。

另外请注意存在IntoResponse,它可以替代实现,用于总是返回响应的类型。

如果一个类型从Action返回None,则不会发送响应,并且路由将继续到更远的节点。这可能会成为对WebSocket支持的扩展枚举。

示例

use foxhole::{http::Version, IntoResponse, Response};

pub struct Html(pub String);

impl IntoResponse for Html {
    fn response(self) -> Response {
        let bytes = self.0.into_bytes();

        http::Response::builder()
            .version(Version::HTTP_11)
            .status(200)
            .header("Content-Type", "text/html; charset=utf-8")
            .header("Content-Length", format!("{}", bytes.len()))
            .body(bytes)
            .unwrap()
    }
}

贡献

如果您对功能或改进有建议,请随时提交问题或拉取请求!

许可证

MIT许可证(LICENSE或https://opensource.org/licenses/MIT

依赖项

~8–15MB
~275K SLoC