A lightweight HTTP/1.1 parser and server built in Rust from scratch, with a focus on security and understanding how web servers work.
- Features
- Build & Run
- Example Endpoints
- Testing
- Dependencies
- Project Structure
- Process
- What I Have Learned
- License
- Full HTTP/1.1 support with chunked transfer encoding
- Parses HTTP requests including headers, body, and query parameters
- Clean error handling with helpful error messages
- Handles multiple connections at the same time using threads
- Built-in security against request smuggling and DoS attacks
- Header size limits and connection timeouts to prevent abuse
- Host header validation to block malicious requests
Ensure you have Rust installed.
To build the project:
cargo buildTo run the server:
cargo runThe server will start listening on 127.0.0.1:8080.
Once the server is running, you can test the following endpoints:
GET /- Returns "Hello from rawhttp"GET /status- Returns "Server is running"GET /query?message=hello- Returns "Message: hello"POST /echo- Echoes the request bodyGET /valid-host- Validates Host header against whitelist
To run the automated handler tests, use the provided shell script:
./test_handlers.shThis script sends various requests to the server and verifies the response status codes.
The project uses the following external crates:
- anyhow: Flexible concrete Error type built on
std::error::Error. - thiserror: Convenient derivation of the
Errortrait.
The project is organized into modular components:
src/main.rs: Entry point. Defines theWebsiteHandlerwhich implements the application logic and routing.src/server.rs: Contains theServerstruct andHandlertrait. Manages the TCP listener and incoming connections.src/http/: Library module for HTTP parsing.request.rs: Parses raw bytes intoRequeststructs.response.rs: FormatsResponsestructs into bytes.method.rs,query.rs,body.rs: Helper modules for specific HTTP components.
- TCP Listener: The server binds to a TCP address and listens for incoming connections.
- Connection Handling: For each connection, a new thread is spawned (basic multi-threading).
- Request Parsing: The raw byte stream is read and parsed into a structured
Requestobject. - Routing: The
Handler(implemented inmain.rs) matches the request method and path to the appropriate logic. - Response Generation: A
Responseobject is created and written back to the TCP stream.
- How to use
std::net::TcpListenerto accept connections andTcpStreamto read and write data. - Understanding the structure of HTTP requests and how to parse them manually.
- Implementing chunked transfer encoding with proper validation.
- Preventing request smuggling attacks by detecting duplicate Transfer-Encoding headers.
- Protecting against DoS attacks using header size limits and connection timeouts.
- Managing ownership when passing streams to threads and sharing handlers using
Arc. - Defining a
Handlertrait to separate server infrastructure from application logic. - Using
anyhowandthiserrorfor error handling in Rust.
This project is licensed under the MIT License - see the LICENSE file for details.