4个版本

0.1.3 2024年7月19日
0.1.2 2024年7月19日
0.1.1 2024年7月19日
0.1.0 2024年7月19日

#149 in HTTP服务器

Download history 173/week @ 2024-07-13 63/week @ 2024-07-20 7/week @ 2024-07-27

每月下载量 243

MIT许可证

29KB
588

µHTTP 🦀🚀🌎

一个专为人类设计,小巧快速,用于在Rust中编写HTTP服务器的库

  • 简单:受Go标准HTTP服务器库的启发 - 使用非异步处理程序来支持阻塞调用和简单的类型签名。

  • 快速:高性能,多线程实现,可以与最快的Rust HTTP服务器竞争。

  • 灵活:简单的接口,支持多种用例。它可以直接使用,也可以作为构建框架的基础。`Request`和`Response`类型实现了Rust的`Read`和`Write`接口,允许进行标准化和通用的交互。

用法

use std::io;
use std::io::Write;

use uhttp::http1::Server;
use uhttp::Request;
use uhttp::Response;

fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080")
}

fn handler(req: Request, mut res: Response) -> io::Result<()> {
  res.write_all(b"Hello World!")
  res.write_header(200)
}

安装

crates.io上提供,使用以下命令安装

cargo add uhttp
[dependencies]
uhttp = "0.*"

理念

该项目源于希望有一个使用愉快、最小化且可扩展的Rust HTTP服务器库。

大多数其他库使用异步Rust,这有助于处理IO密集型工作负载,但可能难以操作。此外,许多现有库使简单任务变得复杂,如将依赖项传递到处理程序或返回流式响应,在某些情况下甚至不完整。

µHTTP旨在提供一个经过时间考验的简单、可扩展且高效的HTTP服务器编写接口。

它具有合理的默认值,用于刷新响应、检测内容类型和自动帧编码。

使用标准库的`Read`和`Write`特质进行HTTP请求的读写,以最大化兼容性和提供熟悉性。

示例

请求正文

use std::io;
use std::io::Write;

use uhttp::http1::Server;
use uhttp::Request;
use uhttp::Response;
use uhttp::utils::body;

fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080")
}

fn handler(mut req: Request, mut res: Response) -> io::Result<()> {
  // Utility to read incoming bytes
  let body_text = body::utf8(&mut req.body)?;
  println!("{}", body_text);

  res.write_header(201)
}

路由

URL作为`String`传递到处理程序中,并可用于将请求路径与路由匹配。您可以使用简单的if语句或第三方URL匹配库来处理路由。

待办事项:添加基本路由器

use std::io;
use std::io::Write;

use uhttp::http1::Server;
use uhttp::Request;
use uhttp::Response;
use uhttp::utils::body;

fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080")
}

fn handler(mut req: Request, mut res: Response) -> io::Result<()> {
  if req.method == "GET" && req.url == "/" {
    return res.write_all("Hello World!")
  }

  if req.method == "POST" && req.url == "/api/echo" {
    let bytes = body::bytes(&mut req.body)?;
    return res.write_all(bytes)
  }

  res.write_header(404)
}

服务文件

use std::io;
use std::io::Write;
use std::fs;

use uhttp::http1::Server;
use uhttp::Request;
use uhttp::Response;

fn main() -> io::Result<()> {
  http1::Server::new(handler).listen("0.0.0.0:8080")
}

fn handler(req: Request, mut res: Response) -> io::Result<()> {
  let bytes = fs::read("/path/to/index.html")?;
  res.write_all(bytes)
  res.write_header(200)
}

常量

提供了一些常量,使响应更一致

use std::io;
use std::io::Write;

use uhttp::http1::Server;
use uhttp::Request;
use uhttp::Response;
use uhttp::c;

fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080")
}

fn handler(req: Request, mut res: Response) -> io::Result<()> {
  res.headers().set(c::headers::CONTENT_TYPE, c::content_type::TEXT_PLAIN);
  res.write_all(b"Hello World!")
  res.write_header(c::status::OK)
}

基准测试

在我的计算机上运行基准测试(Fedora Linux,AMD 7950x处理器,100GB RAM)且拥有100个并发连接

性能解释

显式设置头信息

显式设置 Content-TypeContent-LengthTransfer-Encoding 可以提升性能,因为服务器不需要自动检测它们。

线程池 & May

uhttp 有两层,一个使用 May 协程库的非阻塞方式接受传入 TCP 套接字的 IO 层,并将请求转发到操作系统线程池进行处理

flowchart BT
  main --> may1["may [1]"]
  main --> may2["may [2]"]
  main --> may3["may [3]"]
  main --> may4["may [4]"]
  
  may1 --> schedular
  may2 --> schedular
  may3 --> schedular
  may4 --> schedular

  schedular --> w1
  schedular --> w2
  schedular --> w3
  schedular --> w4
  schedular --> w5
  schedular --> w6
  schedular --> w7
  schedular --> w0

  subgraph may ["Socket Conn"]
    may1
    may2
    may3
    may4
  end
  
  
  subgraph OS Thread Pool
    w1
    w2
    w3
    w4
    w5
    w6
    w7
    w0["w..."]
  end

尽管看到如此多的操作系统线程可能会让人感到震惊,但截至 2024 年,操作系统线程的开销已经显著低于历史水平,并且今天的应用程序可以使用成千上万的操作系统线程,对性能的影响很小。

虽然我非常想使用异步 Rust,但我发现它的使用并不舒适 - 因此,uhttp 在启动时启动一个大型线程池,并将每个 HTTP 请求的处理调度到线程池中其专用的操作系统线程。

这会略微影响启动时间,但没有任何有意义的运行时开销。

待办事项

  • Content-Encoding 提供 gzipbr 压缩工具
  • 传输-编码:分块
  • 服务器端事件(用此代替 WebSocket)
  • HTTP/2
  • 更多性能改进

范围之外

尽管可以随时提出 PR 以添加支持

  • WebSocket

依赖项

~3–31MB
~430K SLoC