µHTTP 🦀🚀🌎
A tiny, fast, library for writing HTTP servers in Rust designed for humans:
-
Simple: Inspired by Go's standard HTTP server library - using non-async handlers to support blocking calls and simple type signatures.
-
Fast: High performance, multi-threaded implementation that competes with the fastest Rust HTTP servers.
-
Flexible: Simple interface that enables many use cases. It can be used directly or to act as a base for frameworks to build on top of. The
RequestandResponsetypes implement Rust'sReadandWriteinterfaces, allowing for a standardized and generic interaction.
Usage
use io;
use Write;
use Server;
use Request;
use Response;
Installation
Available on crates.io, install with:
cargo add uhttp
[]
= "0.*"
Philosophy
The project was born out of a desire to have an http server library for Rust that is pleasant to use, minimal and extensible.
Most other libraries use async Rust which does help with IO heavy workloads but can be difficult to work with. Additionally, many of the existing libraries make simple tasks, like passing dependencies into handlers or returning streamed responses, quite challenging - and in some cases, are simply incomplete.
µHTTP aims to offer a time-tested interface for writing http servers that is simple, extensible and performant.
It features sensible defaults for flushing responses, detecting content types, and automatic frame encodings.
Using the standard library Read and Write traits for reading and writing to http requests to maximize compatibility and provide familiarity.
Examples
Request Body
use io;
use Write;
use Server;
use Request;
use Response;
use body;
Routing
The URL is passed into the handler as a String and can be used to match request paths to routes. You can use simple if statements or a third party URL matching library to handle routing.
TODO: Adding a basic router
use io;
use Write;
use Server;
use Request;
use Response;
use body;
Serving a File
use io;
use Write;
use fs;
use Server;
use Request;
use Response;
Constants
Provided are some constants to make responses more consistent
use io;
use Write;
use Server;
use Request;
use Response;
use c;
Benchmarks
Benchmarks running on my computer (Fedora Linux, AMD 7950x with 100gb RAM) with 100 concurrent connections
Explaining Performance
Setting Headers Explicitly
Setting the Content-Type, Content-Length or Transfer-Encoding explicitly will improve performance as the server does not need to detect them automatically.
Thread Pool & May
uhttp has two layers, an IO layer that uses the May coroutine library to accept incoming TCP sockets in a non-blocking fashion which forwards the requests to an OS thread-pool for processing
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
It might be jarring to see so many OS threads being spawned however, in 2024, the overhead for OS threads is significantly lower than it has been historically and applications today can use thousands of OS threads with little impact on performance.
While I'd love to use async Rust, I find its usage to be un-ergonomic - for this reason uhttp spawns a large thread-pool at start up and schedules the processing of each http request on its own dedicated OS thread from the thread pool.
This affects start-up time slightly but does not have any meaningful runtime overhead.
TODO
- Provide compressor utils for
Content-Encoding:gzipandbr Transfer-Encoding: chunked- Server Sent Events (use this instead of WebSocket)
- HTTP/2
- More performance improvements
Out of Scope
Though feel free to raise a PR to add support
- WebSocket