servlin 0.1.0

Modular HTTP server library, threaded handlers and async performance
Documentation

Servlin

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

A modular HTTP server library in Rust.

Features

  • forbid(unsafe_code)
  • Threaded request handlers: FnOnce(Request) -> Response + 'static + Clone + Send + Sync
  • Uses async code internally for excellent performance under load
  • JSON
  • Server-Sent Events (SSE)
  • Saves large request bodies to temp files
  • Sends 100-Continue
  • Limits number of threads and connections
  • Modular: roll your own logging, write custom versions of internal methods, etc.
  • No macros or complicated type params
  • Good test coverage (63%)

Limitations

  • New, not proven in production.
  • To do:
  • Request timeouts
  • chunked transfer-encoding for request bodies
  • gzip
  • brotli
  • TLS
  • automatically getting TLS certs via ACME
  • Drop idle connections when approaching connection limit.
  • Denial-of-Service mitigation: source throttling, minimum throughput
  • Complete functional test suite
  • Missing load tests
  • Disk space usage limits

Examples

Complete examples: examples/.

Simple example:

use serde::Deserialize;
use serde_json::json;
use servlin::{
print_log_response,
socket_addr_127_0_0_1,
HttpServerBuilder,
Request,
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, Response> {
#[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, Response> {
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| {
print_log_response(&req, handle_req(state, &req))
};
let cache_dir = TempDir::new().unwrap();
safina_timer::start_timer_thread();
let executor = safina_executor::Executor::new(1, 9).unwrap();
# let permit = permit::Permit::new();
# let server_permit = permit.new_sub();
# std::thread::spawn(move || {
#     std::thread::sleep(std::time::Duration::from_millis(100));
#     drop(permit);
# });
executor.block_on(
HttpServerBuilder::new()
#       .permit(server_permit)
.listen_addr(socket_addr_127_0_0_1(8000))
.max_conns(1000)
.small_body_len(64 * 1024)
.receive_large_bodies(cache_dir.path())
.spawn_and_join(request_handler)
).unwrap();

Cargo Geiger Safety Report

Alternatives

See rust-webserver-comparison.md.

Changelog

  • v0.1.0 - Renamed library to Servlin.

TO DO

  • Fix limitations above
  • Support HEAD responses that have Content-Length set and no body.
  • Update rust-webserver-comparison.md
  • Add missing data
  • Add other servers from https://www.arewewebyet.org/topics/frameworks/
  • Rearrange
  • Generate geiger reports for each web server