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
每月下载量 103
用于 applin
205KB
5K SLoC
Servlin
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_response
和RequestBody::length_is_known
- 将
RequestBody::len
和is_empty
的返回值改为Option
。 - 错误修复
- 添加了
- v0.1.1 - 添加了
EventSender::unconnected
。 - v0.1.0 - 将库重命名为 Servlin。
待办事项
- 修复上述限制
- 支持设置 Content-Length 并没有主体的 HEAD 响应。
- 添加对上传体大小的服务器端限制。
- 限制缓存上传的磁盘使用。
- 更新
rust-webserver-comparison.md
- 添加缺失的数据
- 添加来自 https://www.arewewebyet.org/topics/frameworks/ 的其他服务器
- 重新排列
- 为每个 Web 服务器生成 geiger 报告
许可证:MIT OR Apache-2.0
依赖项
~6–18MB
~219K SLoC