#async-http #threaded #web #server #http #request-response

servlin

模块化 HTTP 服务器库,多线程处理程序和异步性能

11 个不稳定版本 (3 个破坏性更新)

0.4.3 2024 年 1 月 11 日
0.4.2 2023 年 12 月 30 日
0.4.0 2023 年 11 月 4 日
0.3.2 2023 年 10 月 9 日
0.1.1 2022 年 6 月 27 日

HTTP 服务器 中排名 146

Download history

每月下载量 103
用于 applin

MIT/Apache

205KB
5K SLoC

Servlin

crates.io version license: Apache 2.0 unsafe forbidden pipeline status

Rust 的模块化 HTTP 服务器库。

功能

  • 禁止(不安全代码)
  • 多线程请求处理程序
    FnOnce(请求) ->响应+ 'static + Clone + Send + Sync
  • 内部使用异步代码以实现负载下的优秀性能
  • JSON
  • 服务器端事件 (SSE)
  • 将大请求体保存到临时文件
  • 发送 100-Continue
  • 限制线程和连接数
  • 模块化:自定义日志、编写内部方法的自定义版本等。
  • 没有宏或复杂的类型参数
  • 良好的测试覆盖率(63%)

限制

  • 较新,尚未在生产环境中得到验证。
  • 待办事项
    • 请求超时
    • chunked 请求体传输编码
    • gzip
    • brotli
    • TLS
    • 通过 ACME 自动获取 TLS 证书
    • 接近连接限制时丢弃空闲连接。
    • 拒绝服务缓解:源节流、最小吞吐量
    • 完整的功能测试套件
    • 缺少负载测试
    • 磁盘空间使用限制

示例

完整示例: examples/.

简单示例

use serde::Deserialize;
use serde_json::json;
use servlin::{
    socket_addr_127_0_0_1,
    Error,
    HttpServerBuilder,
    Request,
    Response
};
use servlin::log::log_request_and_response;
use servlin::reexport::{safina_executor, safina_timer};
use std::sync::Arc;
use temp_dir::TempDir;

struct State {}

fn hello(_state: Arc<State>, req: Request) -> Result<Response, Error> {
    #[derive(Deserialize)]
    struct Input {
        name: String,
    }
    let input: Input = req.json()?;
    Ok(Response::json(200, json!({"message": format!("Hello, {}!", input.name)}))
    .unwrap())
}

fn handle_req(state: Arc<State>, req: Request) -> Result<Response, Error> {
    match (req.method(), req.url().path()) {
        ("GET", "/ping") => Ok(Response::text(200, "ok")),
        ("POST", "/hello") => hello(state, req),
        _ => Ok(Response::text(404, "Not found")),
    }
}

let state = Arc::new(State {});
let request_handler = move |req: Request| {
    log_request_and_response(req, |req| handle_req(state, req)).unwrap()
};
let cache_dir = TempDir::new().unwrap();
safina_timer::start_timer_thread();
let executor = safina_executor::Executor::new(1, 9).unwrap();
executor.block_on(
    HttpServerBuilder::new()
        .listen_addr(socket_addr_127_0_0_1(8271))
        .max_conns(1000)
        .small_body_len(64 * 1024)
        .receive_large_bodies(cache_dir.path())
        .spawn_and_join(request_handler)
).unwrap();

Cargo Geiger 安全报告


Metric output format: x/y
    x = unsafe code used by the build
    y = total unsafe code found in the crate

Symbols: 
    🔒  = No `unsafe` usage found, declares #![forbid(unsafe_code)]= No `unsafe` usage found, missing #![forbid(unsafe_code)]
    ☢️  = `unsafe` usage found

Functions  Expressions  Impls  Traits  Methods  Dependency

0/0        0/0          0/0    0/0     0/0      🔒  servlin 0.4.0
0/0        0/4          0/0    0/0     0/2      ❓  ├── async-fs 1.6.0
0/4        0/230        0/40   0/0     0/12     ❓  │   ├── async-lock 2.8.0
0/0        0/116        0/8    0/0     0/0      ❓  │   │   └── event-listener 2.5.3
0/0        0/28         0/4    0/0     0/0      ❓  │   ├── blocking 1.3.1
0/0        0/0          0/0    0/0     0/0      🔒  │   │   ├── async-channel 1.9.0
0/0        0/168        0/2    0/0     0/1      ❓  │   │   │   ├── concurrent-queue 2.2.0
0/4        0/94         0/16   0/0     0/3      ❓  │   │   │   │   └── crossbeam-utils 0.8.16
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   │       └── cfg-if 1.0.0
0/0        0/116        0/8    0/0     0/0      ❓  │   │   │   ├── event-listener 2.5.3
0/0        0/37         0/2    0/0     0/0      ❓  │   │   │   └── futures-core 0.3.28
0/4        0/230        0/40   0/0     0/12     ❓  │   │   ├── async-lock 2.8.0
0/1        0/858        0/4    0/0     0/12     ❓  │   │   ├── async-task 4.4.0
0/0        0/33         0/2    0/0     0/0      ❓  │   │   ├── atomic-waker 1.1.1
0/0        0/0          0/0    0/0     0/0      🔒  │   │   ├── fastrand 1.9.0
0/0        0/0          0/0    0/0     0/0      ❓  │   │   ├── futures-lite 1.13.0
0/0        0/0          0/0    0/0     0/0      🔒  │   │   │   ├── fastrand 1.9.0
0/0        0/37         0/2    0/0     0/0      ❓  │   │   │   ├── futures-core 0.3.28
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   ├── futures-io 0.3.28
0/41       0/2501       0/2    0/0     0/147    ❓  │   │   │   ├── memchr 2.6.3
0/2        0/20         0/1    0/0     0/0      ❓  │   │   │   │   └── log 0.4.20
0/0        0/5          0/0    0/0     0/0      ❓  │   │   │   │       └── serde 1.0.188
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   │           └── serde_derive 1.0.188
0/0        0/15         0/0    0/0     0/3      ❓  │   │   │   │               ├── proc-macro2 1.0.67
0/0        0/4          0/0    0/0     0/0      ❓  │   │   │   │               │   └── unicode-ident 1.0.12
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   │               ├── quote 1.0.33
0/0        0/15         0/0    0/0     0/3      ❓  │   │   │   │               │   └── proc-macro2 1.0.67
0/0        0/79         0/3    0/0     0/2      ❓  │   │   │   │               └── syn 2.0.37
0/0        0/15         0/0    0/0     0/3      ❓  │   │   │   │                   ├── proc-macro2 1.0.67
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   │                   ├── quote 1.0.33
0/0        0/4          0/0    0/0     0/0      ❓  │   │   │   │                   └── unicode-ident 1.0.12
0/0        0/0          0/0    0/0     0/0      🔒  │   │   │   ├── parking 2.1.0
0/0        0/191        0/0    0/0     0/2      ❓  │   │   │   ├── pin-project-lite 0.2.13
0/0        0/21         0/0    0/0     0/4      ❓  │   │   │   └── waker-fn 1.1.0
0/2        0/20         0/1    0/0     0/0      ❓  │   │   └── log 0.4.20
0/0        0/0          0/0    0/0     0/0      ❓  │   └── futures-lite 1.13.0[build-dependencies]
0/0        0/0          0/0    0/0     0/0      ❓  │   └── autocfg 1.1.0
0/0        0/0          0/0    0/0     0/0      🔒  ├── async-net 1.7.0[build-dependencies]
0/0        0/0          0/0    0/0     0/0      ❓  │   └── autocfg 1.1.0
0/0        0/4          0/0    0/0     0/0      ❓  │   ├── async-io 1.13.0
0/4        0/230        0/40   0/0     0/12     ❓  │   │   ├── async-lock 2.8.0
0/0        0/0          0/0    0/0     0/0      ❓  │   │   ├── cfg-if 1.0.0
0/0        0/168        0/2    0/0     0/1      ❓  │   │   ├── concurrent-queue 2.2.0
0/0        0/0          0/0    0/0     0/0      ❓  │   │   ├── futures-lite 1.13.0
0/2        0/20         0/1    0/0     0/0      ❓  │   │   ├── log 0.4.20
0/0        0/0          0/0    0/0     0/0      🔒  │   │   ├── parking 2.1.0
0/1        0/250        0/16   0/4     0/5      ❓  │   │   ├── polling 2.8.0
                                                       │   │   │   [build-dependencies]
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   └── autocfg 1.1.0
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   ├── cfg-if 1.0.0
0/60       0/502        0/2    0/0     0/50     ❓  │   │   │   ├── libc 0.2.148
0/2        0/20         0/1    0/0     0/0      ❓  │   │   │   └── log 0.4.20
0/371      0/6690       0/2    0/0     0/22     ❓  │   │   ├── rustix 0.37.23
                                                       │   │   │   [build-dependencies]
0/1        0/232        0/2    0/0     0/4      ❓  │   │   │   └── cc 1.0.83
0/60       0/502        0/2    0/0     0/50     ❓  │   │   │       └── libc 0.2.148
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   ├── bitflags 1.3.2
0/0        0/100        0/0    0/0     0/0      ❓  │   │   │   ├── errno 0.3.3
0/60       0/502        0/2    0/0     0/50     ❓  │   │   │   │   └── libc 0.2.148
0/0        0/666        0/36   0/2     0/14     ❓  │   │   │   ├── io-lifetimes 1.0.11
0/60       0/502        0/2    0/0     0/50     ❓  │   │   │   │   ├── libc 0.2.148
0/6        0/673        0/4    0/0     0/4      ❓  │   │   │   │   └── socket2 0.4.9
0/60       0/502        0/2    0/0     0/50     ❓  │   │   │   │       └── libc 0.2.148
0/0        0/7          0/0    0/0     0/0      ❓  │   │   │   ├── itoa 1.0.9
0/60       0/502        0/2    0/0     0/50     ❓  │   │   │   └── libc 0.2.148
0/0        0/24         0/0    0/0     0/3      ❓  │   │   ├── slab 0.4.9
                                                       │   │   │   [build-dependencies]
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   └── autocfg 1.1.0
0/0        0/5          0/0    0/0     0/0      ❓  │   │   │   └── serde 1.0.188
0/6        0/673        0/4    0/0     0/4      ❓  │   │   ├── socket2 0.4.9
0/0        0/21         0/0    0/0     0/4      ❓  │   │   └── waker-fn 1.1.0
                                                       │   │   [build-dependencies]
0/0        0/0          0/0    0/0     0/0      ❓  │   │   └── autocfg 1.1.0
0/0        0/28         0/4    0/0     0/0      ❓  │   ├── blocking 1.3.1
0/0        0/0          0/0    0/0     0/0      ❓  │   └── futures-lite 1.13.0
0/0        0/0          0/0    0/0     0/0      🔒  ├── fixed-buffer 0.5.0
0/0        0/0          0/0    0/0     0/0      ❓  │   └── futures-io 0.3.28
0/0        0/0          0/0    0/0     0/0      ❓  ├── futures-io 0.3.28
0/0        0/0          0/0    0/0     0/0      ❓  ├── futures-lite 1.13.0
0/0        0/0          0/0    0/0     0/0      ❓  ├── include_dir 0.7.3
0/0        0/0          0/0    0/0     0/0      ❓  │   └── include_dir_macros 0.7.3
0/0        0/15         0/0    0/0     0/3      ❓  │       ├── proc-macro2 1.0.67
0/0        0/0          0/0    0/0     0/0      ❓  │       └── quote 1.0.33
0/0        0/121        0/9    0/0     0/4      ❓  ├── once_cell 1.18.0
0/0        0/0          0/0    0/0     0/0      🔒  ├── permit 0.2.1
0/0        0/32         0/0    0/0     0/0      ❓  ├── rand 0.8.5
0/60       0/502        0/2    0/0     0/50     ❓  │   ├── libc 0.2.148
0/2        0/20         0/1    0/0     0/0      ❓  │   ├── log 0.4.20
0/0        0/0          0/0    0/0     0/0      ❓  │   ├── rand_chacha 0.3.1
0/2        0/712        0/0    0/0     0/25     ❓  │   │   ├── ppv-lite86 0.2.17
0/0        0/2          0/0    0/0     0/0      ❓  │   │   ├── rand_core 0.6.4
0/7        0/228        0/1    0/0     0/3      ❓  │   │   │   ├── getrandom 0.2.10
0/0        0/0          0/0    0/0     0/0      ❓  │   │   │   │   ├── cfg-if 1.0.0
0/60       0/502        0/2    0/0     0/50     ❓  │   │   │   │   └── libc 0.2.148
0/0        0/5          0/0    0/0     0/0      ❓  │   │   │   └── serde 1.0.188
0/0        0/5          0/0    0/0     0/0      ❓  │   │   └── serde 1.0.188
0/0        0/2          0/0    0/0     0/0      ❓  │   ├── rand_core 0.6.4
0/0        0/5          0/0    0/0     0/0      ❓  │   └── serde 1.0.188
0/0        0/0          0/0    0/0     0/0      🔒  ├── safe-regex 0.2.5
0/0        0/0          0/0    0/0     0/0      🔒  │   └── safe-regex-macro 0.2.5
0/0        0/0          0/0    0/0     0/0      🔒  │       ├── safe-proc-macro2 1.0.67
0/0        0/4          0/0    0/0     0/0      ❓  │       │   └── unicode-ident 1.0.12
0/0        0/0          0/0    0/0     0/0      🔒  │       └── safe-regex-compiler 0.2.5
0/0        0/0          0/0    0/0     0/0      🔒  │           ├── safe-proc-macro2 1.0.67
0/0        0/0          0/0    0/0     0/0      🔒  │           └── safe-quote 1.0.15
0/0        0/0          0/0    0/0     0/0      🔒  │               └── safe-proc-macro2 1.0.67
0/0        0/0          0/0    0/0     0/0      🔒  ├── safina-executor 0.3.3
0/0        0/0          0/0    0/0     0/0      🔒  │   ├── safina-sync 0.2.4
0/0        0/0          0/0    0/0     0/0      🔒  │   └── safina-threadpool 0.2.4
0/0        0/0          0/0    0/0     0/0      🔒  ├── safina-sync 0.2.4
0/0        0/0          0/0    0/0     0/0      🔒  ├── safina-timer 0.1.11
0/0        0/121        0/9    0/0     0/4      ❓  │   └── once_cell 1.18.0
0/0        0/5          0/0    0/0     0/0      ❓  ├── serde 1.0.188
0/0        0/7          0/0    0/0     0/0      ❓  ├── serde_json 1.0.107
0/0        0/7          0/0    0/0     0/0      ❓  │   ├── itoa 1.0.9
0/9        0/715        0/0    0/0     0/2      ❓  │   ├── ryu 1.0.15
0/0        0/5          0/0    0/0     0/0      ❓  │   └── serde 1.0.188
0/0        0/0          0/0    0/0     0/0      🔒  ├── serde_urlencoded 0.7.1
0/0        0/2          0/0    0/0     0/0      ❓  │   ├── form_urlencoded 1.2.0
0/0        0/8          0/0    0/0     0/0      ❓  │   │   └── percent-encoding 2.3.0
0/0        0/7          0/0    0/0     0/0      ❓  │   ├── itoa 1.0.9
0/9        0/715        0/0    0/0     0/2      ❓  │   ├── ryu 1.0.15
0/0        0/5          0/0    0/0     0/0      ❓  │   └── serde 1.0.188
0/0        0/0          0/0    0/0     0/0      🔒  ├── temp-dir 0.1.11
0/0        0/0          0/0    0/0     0/0      🔒  ├── temp-file 0.1.7
0/0        0/0          0/0    0/0     0/0      ❓  └── url 2.4.1
0/0        0/2          0/0    0/0     0/0      ❓      ├── form_urlencoded 1.2.0
0/0        0/0          0/0    0/0     0/0      ❓      ├── idna 0.4.0
0/0        0/5          0/0    0/0     0/0      ❓      │   ├── unicode-bidi 0.3.13
0/0        0/5          0/0    0/0     0/0      ❓      │   │   └── serde 1.0.188
0/0        0/20         0/0    0/0     0/0      ❓      │   └── unicode-normalization 0.1.22
0/0        0/0          0/0    0/0     0/0      🔒      │       └── tinyvec 1.6.0
0/0        0/5          0/0    0/0     0/0      ❓      │           ├── serde 1.0.188
0/0        0/0          0/0    0/0     0/0      🔒      │           └── tinyvec_macros 0.1.1
0/0        0/8          0/0    0/0     0/0      ❓      ├── percent-encoding 2.3.0
0/0        0/5          0/0    0/0     0/0      ❓      └── serde 1.0.188

0/509      0/15404      0/156  0/6     0/324  

替代方案

请参阅 rust-webserver-comparison.md

变更日志

  • v0.4.3 - 实现 From<Cow<'_, str>>From<&Path> 用于 TagValue
  • v0.4.2 - 实现 Seek 对于 BodyReader
  • v0.4.1
    • 添加 Request::opt_json
    • Error 实现 From<LoggerStoppedError>
  • v0.4.0
    • Response::json 的返回值改为 Result<Response, Error>
    • log_request_and_response 的返回值改为 Result
    • 添加了 Response::unprocessable_entity_422
  • v0.3.2 - 修复了 Response::include_dir 重定向中的错误。
  • v0.3.1
    • 添加了 Response::redirect_301
    • Response::include_dir 修改为从 /somedir 重定向到 /somedir/,以便相对URL能够正常工作。
  • v0.3.0 - 将 Response::include_dir 修改为接受 &Request 并在目录中查找 index.html
  • v0.2.0
    • 添加了
      • log_request_and_response 和其他日志工具
      • 响应::ok_200()
      • 响应::unauthorized_401()
      • 响应::forbidden_403()
      • 响应::internal_server_errror_500()
      • 响应::not_implemented_501()
      • 响应::service_unavailable_503()
      • EventSender::is_connected()
      • PORT_env()
    • 移除了 print_log_responseRequestBody::length_is_known
    • RequestBody::lenis_empty 的返回值改为 Option
    • 错误修复
  • v0.1.1 - 添加了 EventSender::unconnected
  • v0.1.0 - 将库重命名为 Servlin。

待办事项

  • 修复上述限制
  • 支持设置 Content-Length 并没有主体的 HEAD 响应。
  • 添加对上传体大小的服务器端限制。
  • 限制缓存上传的磁盘使用。
  • 更新 rust-webserver-comparison.md

许可证:MIT OR Apache-2.0

依赖项

~6–18MB
~219K SLoC