7 releases

0.2.2 Sep 1, 2024
0.2.1 Sep 1, 2024
0.1.3 Jul 19, 2024

#523 in HTTP server

Download history 20/week @ 2024-07-22 162/week @ 2024-08-26 214/week @ 2024-09-02 23/week @ 2024-09-16 51/week @ 2024-09-30

365 downloads per month

MIT license

24KB
454 lines

µHTTP 🦀🚀🌎

A fast, tiny library for writing HTTP servers in Rust designed for humans:

  • Simple: Inspired by Go's standard library HTTP server.

  • Fast: High performance, multi-threaded implementation built on top of Tokio 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.

Installation

Available on crates.io, install with:

cargo add uhttp

Benchmarks

Running on my desktop PC with a 7950x

oha -n 500000 -c 500 --latency-correction http://localhost:8080
---
config:
  xyChart:
    showTick: true
---
xychart-beta
    title "Requests Per Second"
    x-axis ["hyper", "uhttp", "Go HTTP", "Node.js (uws library)", "Node.js (std)"]
    y-axis "Requests Per Second" 0 --> 500000
    bar [497432, 493671, 412250, 140929, 79966]

Usage

Get Request

use std::io;

use uhttp::http1::Server;
use uhttp::HttpWriter;
use uhttp::Request;
use uhttp::Response;

#[tokio::main]
async fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080").await
}

async fn handler(mut _req: Request, mut res: Response) -> io::Result<()> {
  res.write_all(b"Hello World!").await?;
  res.write_header(200).await
}

Request Body

use std::io;

use uhttp::http1::Server;
use uhttp::Request;
use uhttp::Response;

#[tokio::main]
async fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080").await
}

async fn handler(mut req: Request, mut res: Response) -> io::Result<()> {
  let body_text = uhttp::utils::body::utf8(&mut req).await?;
  println!("{}", body_text);

  res.write_header(201).await
}

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 std::io;

use uhttp::http1::Server;
use uhttp::HttpWriter;
use uhttp::Request;
use uhttp::Response;

#[tokio::main]
async fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080").await
}

async fn handler(mut req: Request, mut res: Response) -> io::Result<()> {
  if req.method == "GET" && req.url == "/" {
    return res.write_all(b"Hello World!").await
  }

  if req.method == "POST" && req.url == "/api/echo" {
    let bytes = uhttp::utils::body::bytes(&mut req).await?;
    return res.write_all(&bytes).await
  }

  res.write_header(404).await
}

Serving a File

use std::io;
use std::path::PathBuf;

use tokio::fs;

use uhttp::http1::Server;
use uhttp::HttpWriter;
use uhttp::Request;
use uhttp::Response;

const CARGO_HOME: &str = env!("CARGO_MANIFEST_DIR");

#[tokio::main]
async fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080").await
}

async fn handler(_req: Request, mut res: Response) -> io::Result<()> {
  let index_file = PathBuf::from(CARGO_HOME).join("index.html");
  let bytes = fs::read(&index_file).await?;
  res.write_all(&bytes).await?;
  res.write_header(200).await
}

Constants

Provided are some constants to make responses more consistent

use std::io;
use std::io::Write;

use uhttp::http1::Server;
use uhttp::Request;
use uhttp::Response;
use uhttp::c;

fn main() -> io::Result<()> {
  Server::new(handler).listen("0.0.0.0:8080")
}

fn handler(req: Request, mut res: Response) -> io::Result<()> {
  res.headers().set(c::headers::CONTENT_TYPE, c::content_type::TEXT_PLAIN);
  res.write_all(b"Hello World!")
  res.write_header(c::status::OK)
}

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.

TODO

  • Provide compressor utils for Content-Encoding: gzip and br
  • 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

Dependencies

~4–13MB
~133K SLoC