Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

germankuber/rust_web_server

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

7 Commits

Repository files navigation

Rust Web Server

A multithreaded HTTP/1.1 web server implementation built from scratch in Rust, demonstrating core networking concepts, concurrent programming, and clean architecture principles.

Table of Contents

Overview

This project is a pedagogical implementation of an HTTP web server written in Rust. It showcases fundamental concepts in network programming, including TCP socket handling, HTTP protocol parsing, concurrent request processing, and modular software design. The server handles multiple simultaneous connections using Rust's threading model and implements a routing system based on the Handler trait pattern.

Architecture

High-Level Architecture

┌─────────────┐
│ Client │
└──────┬──────┘
 │ TCP Connection
 ▼
┌─────────────────────────────────────┐
│ TcpListener │
│ (127.0.0.1:8080) │
└──────┬──────────────────────────────┘
 │
 ▼
┌─────────────────────────────────────┐
│ Thread Pool │
│ (Worker threads spawn per request) │
└──────┬──────────────────────────────┘
 │
 ▼
┌─────────────────────────────────────┐
│ Connection Handler │
│ - Parse HTTP Request │
│ - Extract Headers │
│ - Route to Handler │
└──────┬──────────────────────────────┘
 │
 ▼
┌─────────────────────────────────────┐
│ Router │
│ (Path → Handler mapping) │
└──────┬──────────────────────────────┘
 │
 ▼
┌─────────────────────────────────────┐
│ Handler Trait Implementations │
│ - HomeHandler │
│ - AboutHandler │
│ - SearchHandler │
│ - UserAgentHandler │
│ - HeadersHandler │
│ - AcceptHandler │
└─────────────────────────────────────┘

Features

1. TCP Socket Programming

The server uses Rust's std::net::TcpListener to bind to a network address and accept incoming TCP connections. Each connection is handled independently, demonstrating the fundamentals of network socket programming.

Key Concepts:

  • Socket binding and listening
  • Connection acceptance loop
  • Stream-based I/O operations

2. HTTP/1.1 Protocol Implementation

The server implements a subset of the HTTP/1.1 protocol, including:

  • Request Line Parsing: Extracts HTTP method, path, and version
  • Header Parsing: Reads and stores all HTTP headers in a key-value structure
  • Query Parameter Parsing: Extracts URL query parameters (e.g., ?q=search)
  • Response Generation: Constructs valid HTTP responses with status codes and headers

HTTP Request Structure Handled:

GET /path?query=value HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: curl/7.64.1
Accept: */*

3. Concurrent Request Handling

The server employs thread-based concurrency to handle multiple simultaneous client connections. Each incoming connection spawns a new thread, allowing the server to process multiple requests in parallel without blocking.

Concurrency Model:

  • One thread per connection
  • Shared router state using Arc (Atomic Reference Counting)
  • Thread-safe handler execution

4. Routing System

A flexible routing system maps URL paths to specific handler implementations using the Handler trait pattern. This design follows the Open/Closed Principle, allowing new routes to be added without modifying existing code.

Router Features:

  • HashMap-based path resolution
  • Trait-based handler abstraction
  • Dynamic handler registration
  • Automatic 404 handling for unmapped routes

5. Error Handling

Comprehensive error handling using the anyhow crate provides:

  • Result-based error propagation
  • Context-aware error messages
  • Graceful failure recovery
  • Proper logging of error conditions

6. Logging System

Structured logging using log and env_logger crates enables:

  • Multiple log levels (info, debug, error)
  • Runtime-configurable verbosity
  • Request/response lifecycle tracking
  • Error diagnostics

Technical Implementation

HTTP Request Parsing

The parse_request function implements a multi-stage parsing pipeline:

  1. Request Line Parsing: Splits the first line into method, path, and version
  2. URL Parsing: Separates path from query parameters
  3. Query Parameter Extraction: Parses key-value pairs from query string
  4. Header Extraction: Iterates through remaining lines to extract headers
  5. URL Decoding: Handles percent-encoded characters in URLs
pub fn parse_request(request_lines: &[String]) -> anyhow::Result<HttpRequest> {
 // Validates request structure
 // Parses request line components
 // Extracts and normalizes headers
 // Builds HttpRequest object
}

Handler Trait Pattern

The Handler trait defines a contract for request processing:

pub trait Handler: Send + Sync {
 fn handle(&self, request: &HttpRequest) -> anyhow::Result<HttpResponse>;
}

Design Benefits:

  • Polymorphism: Different handlers implement the same interface
  • Testability: Handlers can be tested in isolation
  • Extensibility: New handlers added without modifying router
  • Type Safety: Compile-time verification of handler implementations

Status Code Enum

Type-safe HTTP status codes prevent invalid status code usage:

pub enum StatusCode {
 Ok, // 200
 NotFound, // 404
 InternalServerError, // 500
}

This ensures only valid status codes can be used, eliminating runtime errors from invalid values.

Project Structure

rust_web_server/
├── src/
│ ├── main.rs # Entry point, server initialization
│ ├── http.rs # HTTP protocol implementation
│ ├── handler.rs # Connection handling logic
│ ├── router/
│ │ └── mod.rs # Router and Handler trait
│ └── handlers/
│ ├── mod.rs # Handler module declarations
│ ├── home.rs # Home page handler
│ ├── about.rs # About page handler (with 5s delay)
│ ├── contact.rs # Contact information handler
│ ├── search.rs # Search with query parameters
│ ├── user_agent.rs # User-Agent header inspection
│ ├── headers.rs # All headers display
│ └── accept.rs # Accept/Host/Content-Type display
├── Cargo.toml # Project dependencies
└── README.md # This file

Module Responsibilities

  • main.rs: Server bootstrapping, router configuration, request loop
  • http.rs: HTTP data structures, parsing logic, response building
  • handler.rs: Connection lifecycle, request/response I/O
  • router/: Route registration and handler dispatch
  • handlers/: Individual request handlers implementing business logic

Getting Started

Prerequisites

  • Rust 1.70+ (edition 2024)
  • Cargo package manager

Installation

# Clone the repository
git clone <repository-url>
cd rust_web_server
# Build the project
cargo build --release
# Run the server with logging
RUST_LOG=info cargo run

Configuration

The server binds to 127.0.0.1:8080 by default. To change this, modify the bind address in main.rs:

let listener = TcpListener::bind("127.0.0.1:8080")?;

Logging Levels

Control log verbosity with the RUST_LOG environment variable:

RUST_LOG=error cargo run # Errors only
RUST_LOG=info cargo run # Info, errors
RUST_LOG=debug cargo run # Debug, info, errors

API Endpoints

GET /

Returns a welcome message.

curl http://127.0.0.1:8080/
# Response: Welcome to the home page!

GET /about

Returns about page information (simulates slow response with 5-second delay).

curl http://127.0.0.1:8080/about
# Response: This is the about page (after 5 seconds)

GET /contact

Returns contact information.

curl http://127.0.0.1:8080/contact
# Response: Contact us at contact@example.com

GET /search?q=query

Search endpoint demonstrating query parameter extraction.

curl http://127.0.0.1:8080/search?q=rust
# Response: You searched for: rust
curl http://127.0.0.1:8080/search
# Response: No search query provided

GET /user-agent

Inspects and responds based on the User-Agent header.

curl http://127.0.0.1:8080/user-agent
# Response: Hello curl user! Your User-Agent is: curl/7.64.1
curl -H "User-Agent: MyCustomBot/1.0" http://127.0.0.1:8080/user-agent
# Response: Hello Bot! Your User-Agent is: MyCustomBot/1.0

GET /headers

Displays all HTTP headers received in the request.

curl http://127.0.0.1:8080/headers
# Response:
# HTTP Headers:
#
# host: 127.0.0.1:8080
# user-agent: curl/7.64.1
# accept: */*

GET /accept

Shows specific headers: Host, Accept, and Content-Type.

curl -H "Accept: application/json" http://127.0.0.1:8080/accept
# Response:
# Request Details:
#
# Host: 127.0.0.1:8080
# Accept: application/json
# Content-Type: none

Design Principles

This implementation adheres to SOLID principles:

Single Responsibility Principle (SRP)

Each module has a single, well-defined purpose:

  • http.rs: HTTP protocol concerns only
  • handler.rs: Connection handling only
  • Each handler file: One specific route's logic

Open/Closed Principle (OCP)

The router is open for extension (new handlers) but closed for modification. Adding a new route requires:

  1. Creating a new handler implementing the Handler trait
  2. Registering it in main.rs

No changes to existing router or handler code are needed.

Liskov Substitution Principle (LSP)

All handler implementations can be substituted for the Handler trait without altering program correctness. The router treats all handlers uniformly.

Interface Segregation Principle (ISP)

The Handler trait is minimal and focused, requiring only one method: handle(). Handlers aren't forced to implement unnecessary methods.

Dependency Inversion Principle (DIP)

  • High-level router depends on the Handler abstraction
  • Low-level handlers implement the abstraction
  • No direct dependencies between concrete implementations

Dependencies

Runtime Dependencies

  • anyhow (1.0): Flexible error handling with context
  • log (0.4): Logging facade providing abstraction over logging implementations
  • env_logger (0.11): Logger implementation configured via environment variables
  • derive_builder (0.20): Generates builder pattern implementations for structs

Why These Dependencies?

  • anyhow: Simplifies error handling by providing a unified error type with context
  • log + env_logger: Industry-standard logging solution with runtime configurability
  • derive_builder: Reduces boilerplate for constructing complex objects

Future Enhancements

Potential improvements for educational purposes:

  1. HTTP Methods: Support POST, PUT, DELETE
  2. Request Body Parsing: Handle request bodies and form data
  3. Static File Serving: Serve HTML, CSS, JS files
  4. Middleware System: Request/response interceptors
  5. Thread Pool: Fixed-size thread pool instead of unbounded threads
  6. Keep-Alive: Support persistent connections
  7. TLS/HTTPS: Secure connections with OpenSSL
  8. Async/Await: Migration to tokio for async I/O
  9. HTTP/2: Implement HTTP/2 protocol features
  10. Compression: Gzip/Brotli response compression

Learning Outcomes

By studying this implementation, you will understand:

  • TCP socket programming in Rust
  • HTTP protocol structure and parsing
  • Concurrent programming with threads
  • Error handling patterns in Rust
  • Trait-based polymorphism
  • Builder pattern for complex object construction
  • Logging and observability practices
  • Clean architecture and SOLID principles
  • Module organization in Rust projects

License

MIT License - Feel free to use this for educational purposes.

Contributing

This is an educational project. Contributions that improve clarity, add documentation, or demonstrate additional concepts are welcome.


Built with Rust 🦀 - A language empowering everyone to build reliable and efficient software.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /