Skip to content

rferrari/http-crab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HTTP Server

This is a simple HTTP server written in Rust using the hyper library. It includes a basic load testing utility to benchmark the server's performance.

Features

  • Asynchronous HTTP Server: Built with hyper, a fast and correct HTTP library for Rust.
  • Metrics: Tracks the number of in-flight and completed requests.
  • Load Tester: A simple utility to send a large number of concurrent requests to the server.

Getting Started

Prerequisites

  • Rust and Cargo installed on your system.

Running the Server

  1. Clone the repository:
    git clone https://github.com/rferrari/http-crab.git
    cd http-server
  2. Run the server:
    cargo run
    The server will start on http://127.0.0.1:3000.

Running the Load Test

  1. In a separate terminal, run the load test:
    cargo run --bin load_test
    This will send a large number of concurrent requests to the server and report when the test is complete.

Code

Server (src/main.rs)

use hyper::{
    service::{make_service_fn, service_fn},
    Body, Request, Response, Server,
};
use std::{convert::Infallible, net::SocketAddr};
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::Arc;
use tracing::info;

#[derive(Clone)]
struct Metrics {
    in_flight: Arc<AtomicUsize>,
    completed: Arc<AtomicU64>,
}

async fn handler(
    _req: Request<Body>,
    metrics: Metrics,
) -> Result<Response<Body>, Infallible> {
    // request starts
    metrics.in_flight.fetch_add(1, Ordering::Relaxed);

    // simulate work (I/O, CPU, upstream call, etc.)
    tokio::time::sleep(std::time::Duration::from_millis(5)).await;

    let response = Response::new(Body::from("ok"));

    // request ends
    metrics.in_flight.fetch_sub(1, Ordering::Relaxed);
    metrics.completed.fetch_add(1, Ordering::Relaxed);

    Ok(response)

}

#[tokio::main]
async fn main() {
    let metrics = Metrics {
        in_flight: Arc::new(AtomicUsize::new(0)),
        completed: Arc::new(AtomicU64::new(0)),
    };

    let metrics_clone = metrics.clone();

    tokio::spawn(async move {
        let mut interval = tokio::time::interval(std::time::Duration::from_secs(1));

        let mut last_completed = 0u64;

        loop {
            interval.tick().await;

            let completed = metrics_clone.completed.load(Ordering::Relaxed);
            let in_flight = metrics_clone.in_flight.load(Ordering::Relaxed);

            let rps = completed - last_completed;
            last_completed = completed;

            tracing::info!(
                rps,
                in_flight,
                total_completed = completed,
                "server load"
            );
        }
    });



    tracing_subscriber::fmt::init();

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));

    let make_svc = make_service_fn(move |_| {
        let metrics = metrics.clone();

        async move {
            Ok::<_, Infallible>(service_fn(move |req| {
                handler(req, metrics.clone())
            }))
        }
    });


    let server = Server::bind(&addr).serve(make_svc);

    info!("listening on http://{}", addr);

    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}

Load Tester (src/bin/load_test.rs)

use hyper::{Client, Uri};
use std::sync::Arc;
use tokio::task;
use tracing::info;

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let client = Arc::new(Client::new());
    let uri: Uri = "http://127.0.0.1:3000".parse().unwrap();

    let concurrency = 500;
    let requests_per_task = 100;

    info!(
        concurrency,
        requests_per_task,
        "starting load test"
    );

    let mut handles = Vec::new();

    for _ in 0..concurrency {
        let client = client.clone();
        let uri = uri.clone();

        handles.push(task::spawn(async move {
            for _ in 0..requests_per_task {
                let _ = client.get(uri.clone()).await;
            }
        }));
    }

    for h in handles {
        let _ = h.await;
    }

    info!("load test completed");
}

About

A lean and fast HTTP server in Rust, built with Tokio and Hyper. Tracks in-flight requests, exposes metrics, serves static HTML, and includes a simple load-testing client. Ideal for learning async Rust, concurrency patterns, and building small, production-ready HTTP services.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors