Skip to content

wellington-vell/http-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HTTP Server from Scratch in Rust

I built this HTTP server from scratch to understand how HTTP actually works. No frameworks, no libraries (except std), just raw TCP streams and manual parsing. It's been really eye-opening to see what happens under the hood when you make a web request.

Why I Built This

I've used HTTP for years but never really understood the details. Frameworks hide all the interesting stuff - how requests are parsed, how headers work, how responses are formatted. So I decided to build a server myself and figure it out.

Turns out HTTP is pretty straightforward once you break it down. You read bytes from a TCP stream, parse the request line (method, path, version), read headers until you hit a blank line, then read the body if there is one. Responses are just text formatted in a specific way.

What's Inside

This server handles the basics:

  • Parsing HTTP requests from raw TCP streams
  • Routing requests to handler functions
  • A simple middleware system
  • Serving static files
  • Proper error handling with HTTP status codes
  • Handling multiple connections with threads

Running It

Just need Rust installed. Then:

cargo run

It'll start on http://127.0.0.1:8080. Watch the console - it logs every request so you can see what's happening.

How It Works

The interesting parts:

Request Parsing (src/http/request.rs): This was the trickiest part. You read the request line (like GET /hello HTTP/1.1), then keep reading lines until you hit a blank line (that's the end of headers), then read the body if Content-Length says there is one. Headers are normalized to lowercase for easy lookup.

Response Building (src/http/response.rs): Responses are just text. Status line, headers, blank line, then body. The format is strict - \r\n for line endings, specific header format, etc.

Router (src/router.rs): Simple hash map that matches method + path to a handler function. No fancy regex or wildcards - just exact matching to keep it clear.

Middleware (src/middleware.rs): Each middleware gets the request and a next function. You can modify the request, call next, then modify the response. The chain executes in order.

Server (src/server.rs): Listens on TCP, spawns a thread for each connection. Each thread parses the request, finds a route, runs middleware, and sends the response back.

The code is pretty straightforward - I tried to keep it simple so you can actually read through it and understand what's happening.

Trying It Out

Use curl or your browser. Here are some examples:

# Basic requests
curl http://127.0.0.1:8080/
curl http://127.0.0.1:8080/hello
curl http://127.0.0.1:8080/api/status

# POST with a body
curl -X POST -H "Content-Type: text/plain" -d "Hello World" http://127.0.0.1:8080/api/data

# PUT and DELETE
curl -X PUT -H "Content-Type: text/plain" -d "Updated" http://127.0.0.1:8080/api/data
curl -X DELETE http://127.0.0.1:8080/api/data

# Auth example (try without the header first, then with it)
curl http://127.0.0.1:8080/api/secret
curl -H "Authorization: Bearer secret-token" http://127.0.0.1:8080/api/secret

# Custom status code (418 I'm a teapot)
curl http://127.0.0.1:8080/api/custom

Or just open http://127.0.0.1:8080/ in your browser. Check the server console to see the request details being logged.

Endpoints

There are a few example endpoints set up in main.rs:

  • GET / - Returns some HTML
  • GET /hello - Plain text response
  • GET /api/status - Just returns "Server is running"
  • POST /api/data - Echoes back whatever you send in the body (returns 201)
  • PUT /api/data - Returns 204 (no content, just status)
  • DELETE /api/data - Also returns 204
  • GET /api/custom - Returns 418 (I'm a teapot, because why not)
  • GET /api/secret - Checks for an Authorization header, returns 401 if missing
  • GET /maintenance - Returns 503

If a route doesn't match, it falls back to trying to serve a static file from the current directory. So you can drop a file in the project folder and access it directly.

Code Structure

src/
  main.rs           # Sets up routes and starts the server
  server.rs         # TCP listener, spawns threads, handles connections
  router.rs         # Maps (method, path) to handler functions
  handlers.rs       # Static file serving (with path traversal checks)
  middleware.rs     # Middleware chain - each one wraps the next
  error.rs          # Error types for parsing and IO
  http/
    request.rs      # Parses HTTP from TCP stream
    response.rs     # Builds HTTP response strings
    method.rs       # GET, POST, etc.
    status.rs       # Status codes like 200, 404, 500

The server logs every request to stdout so you can watch what's happening. You'll see the method, path, version, user agent, and what response was sent back.

What It Doesn't Do

This is a learning project, not something you'd deploy. Missing features:

  • Keep-alive connections (closes after each request)
  • Chunked transfer encoding
  • Query parameters or path variables
  • Async/await (uses threads instead)
  • HTTPS/TLS
  • HTTP/2 or HTTP/3

I kept it simple on purpose. The code is straightforward and you can actually read through it and understand what's happening. If you want to learn more, the HTTP/1.1 RFC (RFC 7230) is the definitive source, though it's pretty dense. Wireshark is also great for seeing the raw packets.

If you want to extend it, good next steps would be adding query parameter parsing, keep-alive support, or switching to async. But honestly, just reading through the code and understanding how requests get parsed and responses get built is the main point.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published