Crate servlin

Source
Expand description

§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::{
    socket_addr_127_0_0_1,
    Error,
    HttpServerBuilder,
    Request,
    Response
};
use servlin::log::log_request_and_response;
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)}))?)
}

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 Safety Report

§Alternatives

See rust-webserver-comparison.md.

§Changelog

  • v0.7.0 2025-06-30
    • Require Rust 2024 edition.
    • log_request_and_response to log duration_ms tag.
    • Fix typo in function name Response::internal_server_errror_500.
    • Close connection on 5xx error.
    • Acceptor thread to log errors, not panic.
    • Add [Request::parse_url].
    • Add Response::too_many_requests_429.
    • Implement Into<TagList> for arrays.
    • Support asterisk request target.
    • Use safina v0.7.
  • v0.6.0 2024-11-02
    • Remove servlin::reexports module.
    • Use safina v0.6.0.
  • v0.5.1 2024-10-26 - Remove dependency on once_cell.
  • v0.5.0 2024-10-21 - Remove LogFileWriterBuilder.
  • v0.4.3 - Implement From<Cow<'_, str>> and From<&Path> for TagValue.
  • v0.4.2 - Implement Seek for BodyReader.
  • v0.4.1
    • Add Request::opt_json.
    • Implement From<LoggerStoppedError> for Error.
  • v0.4.0
    • Changed Response::json to return Result<Response, Error>.
    • Changed log_request_and_response to return Result.
    • Added Response::unprocessable_entity_422.
  • v0.3.2 - Fix bug in Response::include_dir redirects.
  • v0.3.1
    • Add Response::redirect_301
    • Response::include_dir to redirect from /somedir to /somedir/ so relative URLs will work.
  • v0.3.0 - Changed Response::include_dir to take &Request and look for index.html in dirs.
  • v0.2.0
    • Added:
      • log_request_and_response and other logging tooling
      • Response::ok_200()
      • Response::unauthorized_401()
      • Response::forbidden_403()
      • Response::internal_server_errror_500()
      • Response::not_implemented_501()
      • Response::service_unavailable_503()
      • EventSender::is_connected()
      • PORT_env()
    • Removed print_log_response and RequestBody::length_is_known
    • Changed RequestBody::len and is_empty to return Option.
    • Bugfixes
  • v0.1.1 - Add EventSender::unconnected.
  • v0.1.0 - Rename library to Servlin.

§TO DO

  • Fix limitations above
  • Support HEAD responses that have Content-Length set and no body.
  • Add a server-wide limit on upload body size.
  • Limit disk usage for caching uploads.
  • Update rust-webserver-comparison.md

Modules§

internal
This part of the library is not covered by the semver guarantees. If you use these in your program, a minor version upgrade could break your build.
log

Structs§

AsciiString
Wraps a String that contains only US-ASCII chars.
Cookie
Error
EventSender
Header
HeaderList
HttpConn
HttpServerBuilder
Builds an HTTP server.
Request
Response

Enums§

BodyAsyncReader
Struct returned by RequestBody::async_reader and ResponseBody::async_reader.
BodyReader
Struct returned by RequestBody::reader and ResponseBody::reader.
ContentType
Event
RequestBody
ResponseBody
SameSite

Functions§

PORT_env
Reads and parses the PORT environment variable.
socket_addr_127_0_0_1
socket_addr_127_0_0_1_any_port
socket_addr_all_interfaces