#hyper #http #router #performance

thruster

一个基于异步 HTTP 网络服务器的中间件

96 个版本 (38 个稳定版本)

1.3.12 2024年7月26日
1.3.10 2023年10月9日
1.3.7 2023年9月14日
1.3.6 2023年7月20日
0.3.4 2018年3月28日

#21HTTP 服务器

Download history 25/week @ 2024-04-28 29/week @ 2024-05-05 32/week @ 2024-05-12 75/week @ 2024-05-19 35/week @ 2024-05-26 32/week @ 2024-06-02 58/week @ 2024-06-09 25/week @ 2024-06-16 37/week @ 2024-06-23 6/week @ 2024-06-30 7/week @ 2024-07-07 42/week @ 2024-07-14 269/week @ 2024-07-21 327/week @ 2024-07-28 14/week @ 2024-08-04 24/week @ 2024-08-11

每月下载量 635
8 个软件包 中使用

MIT 许可证

210KB
4.5K SLoC

Thruster 最新版本 下载量 在线

在我们的网站上通过示例和教程开始使用吧!

快速直观的 Rust 网络框架

没有时间阅读文档?查看

✅ 稳定运行 ✅ 运行速度快 ✅ 不使用不安全代码

文档

功能

动机

Thruster 是一个网络框架,旨在让开发者在项目和团队之间保持高效和一致性。其目标是

  • 高性能
  • 简单
  • 直观

Thruster 还

  • 不使用 unsafe
  • 在稳定 Rust 中运行

快速

Thruster 可以与不同的服务器后端一起运行,并代表了一个很好的封装层。这意味着它可以跟上 Hyper、Actix 或甚至 ThrusterServer(一个自家的 HTTP 引擎)等最新和最好的变化。

直观

基于 Koa 和 Express 等框架,Thruster 致力于成为一个开发者的愉悦选择。

示例

要运行示例,请执行以下命令:cargo run --example <example-name>。例如,cargo run --example hello_world 然后打开 http://127.0.0.1:4321/

基于中间件

使新的异步 await 代码正常工作,核心部分是使用 #[middleware_fn] 属性(该属性标记中间件,使其与 Thruster 所构建的稳定 futures 版本兼容)来指定中间件函数,然后在实际的路由中使用 m! 宏。

异步 await 的一个简单示例:

use std::boxed::Box;
use std::future::Future;
use std::pin::Pin;
use std::time::Instant;

use thruster::{App, BasicContext as Ctx, Request};
use thruster::{m, middleware_fn, MiddlewareNext, MiddlewareResult, Server, ThrusterServer};

#[middleware_fn]
async fn profile(context: Ctx, next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    let start_time = Instant::now();

    context = next(context).await;

    let elapsed_time = start_time.elapsed();
    println!(
        "[{}μs] {} -- {}",
        elapsed_time.as_micros(),
        context.request.method(),
        context.request.path()
    );

    Ok(context)
}

#[middleware_fn]
async fn plaintext(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    let val = "Hello, World!";
    context.body(val);
    Ok(context)
}

#[middleware_fn]
async fn four_oh_four(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    context.status(404);
    context.body("Whoops! That route doesn't exist!");
    Ok(context)
}

#[tokio::main]
fn main() {
    println!("Starting server...");

    let mut app = App::<Request, Ctx, ()>::new_basic();

    app.get("/plaintext", m![profile, plaintext]);
    app.set404(m![four_oh_four]);

    let server = Server::new(app);
    server.build("0.0.0.0", 4321).await;
}

错误处理

这里有一个很好的示例

use thruster::errors::ThrusterError as Error;
use thruster::proc::{m, middleware_fn};
use thruster::{map_try, App, BasicContext as Ctx, Request};
use thruster::{MiddlewareNext, MiddlewareResult, MiddlewareReturnValue, Server, ThrusterServer};

#[middleware_fn]
async fn plaintext(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    let val = "Hello, World!";
    context.body(val);
    Ok(context)
}

#[middleware_fn]
async fn error(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    let res = "Hello, world".parse::<u32>()
        .map_err(|_| {
            let mut context = Ctx::default();
            
            context.status(400);

            ThrusterError {
                context,
                message: "Custom error message".to_string(),
                cause: None,
            }
        }?;

    context.body(&format!("{}", non_existent_param));

    Ok(context)
}

#[tokio::main]
fn main() {
    println!("Starting server...");

    let app = App::<Request, Ctx, ()>::new_basic()
        .get("/plaintext", m![plaintext])
        .get("/error", m![error]);

    let server = Server::new(app);
    server.build("0.0.0.0", 4321).await;
}

测试

Thruster 提供了一个简单的测试套件来测试您的端点,只需像下面一样包含 testing 模块

let mut app = App::<Request, Ctx, ()>::new_basic();

...

app.get("/plaintext", m![plaintext]);

...

let result = testing::get(app, "/plaintext");

assert!(result.body == "Hello, World!");

创建您自己的中间件模块

中间件非常容易创建!只需创建一个函数,并在模块级别导出它。下面,您将看到一段允许请求分析的中间件代码

#[middleware_fn]
async fn profiling<C: 'static + Context + Send>(
    mut context: C,
    next: MiddlewareNext<C>,
) -> MiddlewareResult<C> {
    let start_time = Instant::now();

    context = next(context).await?;

    let elapsed_time = start_time.elapsed();
    info!("[{}μs] {}", elapsed_time.as_micros(), context.route());

    Ok(context)
}

您可能会发现,您想要允许在上下文中存储更具体的数据,例如,也许您想要能够将查询参数填充到哈希表中,供其他中间件稍后使用。为了做到这一点,您可以创建一个额外的特质,供下游中间件必须遵守的上下文。查看提供的 query_params 中间件 作为示例。

其他或自定义后端

Thruster 能够仅在某种服务器之上提供路由层,例如,在上面的 Hyper 示例中。只要服务器实现了 ThrusterServer,这就可以应用于任何后端。

use async_trait::async_trait;

#[async_trait]
pub trait ThrusterServer {
    type Context: Context + Send;
    type Response: Send;
    type Request: RequestWithParams + Send;

    fn new(App<Self::Request, Self::Context>) -> Self;
    async fn build(self, host: &str, port: u16);
}

需要的是

  • 创建服务器的一种简单方法。
  • 一个构建服务器并将其加载到异步运行时的 future 的函数。

build 函数内部,服务器实现应该

  • 启动某种类型的连接监听器
  • 调用 let matched = app.resolve_from_method_and_path(<some method>, <some path>);(这是提供实际路由。)
  • 调用 app.resolve(<传入请求>, matched)(这是运行链式中间件。)

为什么你应该使用 Thruster

  • 随意更改您的后端。开箱即用,Thruster 现在可以在以下后端上使用:actix-webhyper自定义后端
  • Thruster 支持从框架级别进行 测试
  • @trezm 当没有人提交 PR 或打开问题时,会感到孤独。
  • Thruster 对于更注重中间件的概念(如路由守卫)来说更加简洁。以下是在 actix 中限制 IP 的示例
fn ip_guard(head: &RequestHead) -> bool {
    // Check for the cloudflare IP header
    let ip = if let Some(val) = head.headers().get(CF_IP_HEADER) {
        val.to_str().unwrap_or("").to_owned()
    } else if let Some(val) = head.peer_addr {
        val.to_string()
    } else {
        return false;
    };

    "1.2.3.4".contains(&ip)
}

#[actix_web::post("/ping")]
async fn ping() -> Result<HttpResponse, UserPersonalError> {
    Ok(HttpResponse::Ok().body("pong"))
}

...
        web::scope("/*")
            // This is confusing, but we catch all routes that _aren't_
            // ip guarded and return an error.
            .guard(guard::Not(ip_guard))
            .route("/*", web::to(HttpResponse::Forbidden)),
    )
    .service(ping);
...

这是 Thruster


#[middleware_fn]
async fn ip_guard(mut context: Ctx, next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    if "1.2.3.4".contains(&context.headers().get("Auth-Token").unwrap_or("")) {
        context = next(context).await?;

        Ok(context)
    } else {
        Err(Error::unauthorized_error(context))
    }

}

#[middleware_fn]
async fn ping(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> {
    context.body("pong");
    Ok(context)
}

...
    app.get("/ping", m![ip_guard, plaintext]);
...

更直接一点是很好的!

为什么你不应该使用 Thruster

  • 维护者很少(几乎只有一个人。)
  • 还有其他项目经过了更多的实战考验。Thruster正在生产中使用,但并不在你所知道或关心的地方。
  • 它还没有被聪明绝顶的人优化过。@trezm 尽力而为,但总是被他的狗(们)分散注意力。
  • 说真的,这个框架可能是 确实是很棒,但它肯定没有像其他框架那样被彻底检查。你的帮助将有助于使其更加安全和健壮,但我们可能还没有达到那个阶段。

如果你已经读到这儿,感谢你的阅读!随时欢迎联系。

依赖项

~7–20MB
~281K SLoC