◐ Off-By-One · answer catalog

net-http-server-from-scratch

2 answer(s)pythonpython3pythonpython3

class HTTPRequest: # parse(rawdata) → method, path, headers, body, queryparams

📦 Source in repository (JSON)

Answer 1

File: ~/http_server.py

A complete HTTP/1.1 server built from scratch using only socket (no external libraries). The server implements:

1. HTTP Request Parsing (HTTPRequest.parse)
Parses raw bytes into method, path, query params, headers dict, and body. Supports percent-decoded query strings and Content-Length body extraction.

2. HTTP Response Builder (HTTPResponse.build)
Builds proper HTTP/1.1 response bytes with status line, headers (Content-Type, Content-Length, Server, Date, Connection), and body.

3. Router (Router / Route)
Pattern-based routing with named URL parameters (/users/<id>). Supports decorator-style registration (@server.get("/path")).

4. Static File Server (StaticFileServer)
Serves files from a root directory with: - Path traversal protection (normalizes paths, checks startswith(root_dir)) - If-Modified-Since / 304 Not Modified support - MIME type detection via mimetypes - index.html for directory paths - Method validation (only GET/HEAD)

5. Keep-Alive Connection Handling
- HTTP/1.1 defaults to keep-alive; closes only on Connection: close - HTTP/1.0 defaults to close; keeps alive only on Connection: keep-alive - Multiple requests over a single connection work

6. Error Handling
- 400 for malformed requests - 404 for unknown routes / missing files - 405 for wrong methods on existing static files - 403 for path traversal - 500 for unhandled exceptions - Well-formed error response bodies

# Core architecture (key classes)
class HTTPRequest:    # parse(raw_data) → method, path, headers, body, query_params
class HTTPResponse:   # build() → bytes; set_header(), set_content_type()
class Route:          # match(method, path) → (handler, params)
class Router:         # dispatch(request) → response; add_route() + decorators
class StaticFileServer:  # serve(request) → response with file or error
class HTTPServer:     # start() → accept loop; _handle_client() per connection

Example handlers:

server = HTTPServer(static_dir="./static")

@server.get("/")
def index(request):
    return HTTPResponse(200, "<h1>Hello</h1>", {"Content-Type": "text/html"})

@server.get("/api/info")
def api_info(request):
    import json
    return HTTPResponse(200, json.dumps({"status": "ok"}),
                        {"Content-Type": "application/json"})

server.start()

Evidence & signatures

All **37 tests pass**. The test suite (`test_server.py`) covers:

| Category | Tests | What's Verified |
|---|---|---|
| **Request Parsing** (12) | `test_simple_get`, `test_get_with_query`, `test_get_with_encoded_query`, `test_post_with_body`, `test_post_without_body`, `test_http10/11_keep_alive_*`, `test_invalid_request_line`, `test_empty_request`, `test_multiple_headers_same_name` | Method/path/header extraction, query string decoding, HTTP/1.0 vs 1.1 keep-alive semantics, error handling |
| **Response Building** (5) | `test_basic_response`, `test_404_response`, `test_custom_headers`, `test_from_status`, `test_304_no_body` | Status line format, header injection, status texts, empty body for 304 |
| **Functional Server** (20) | `test_get_index`, `test_404`, `test_echo_with_query`, `test_api_info`, `test_static_file`, `test_static_nested_file`, `test_static_index_in_subdir`, `test_path_traversal_denied`, `test_method_not_allowed`, `test_post_to_data_endpoint`, `test_get_data_endpoint`, `test_server_header`, `test_content_type_for_html/txt`, `test_keep_alive_*` (4), `test_multiple_requests_single_connection`, `test_bad_request` | End-to-end socket communication, all status codes, static serving, path traversal blocking, keep-alive pipelining, connection close semantics |

**Edge cases verified:**
- **Path traversal**: `GET /../../../etc/passwd` → **403 Forbidden**
- **Malformed requests**: `GET invalid-path HTTP/1.1` (no leading `/`) → **400 Bad Request**
- **Method on static file**: `POST /test.txt` → **405 Method Not Allowed**
- **HTTP/1.0 keep-alive**: explicit `Connection: keep-alive` → `keep-alive` in response; default → `close`
- **HTTP/1.1 keep-alive**: default → `keep-alive`; `Connection: close` → `close`
- **Pipelined requests**: Two GET requests on same socket → both responses received
- **Empty body POST**: `POST /data` without body → parses successfully
- **If-Modified-Since**: Static files with matching mtime → **304 Not Modified**

---
{"model": "claude-3.5-sonnet", "problem_class": "net-http-server-from-scratch", "result": "passed", "tests": 37}

Answer 2

File: ~/http_server.py

A complete HTTP/1.1 server built from scratch using only socket (no external libraries). The server implements:

1. HTTP Request Parsing (HTTPRequest.parse)
Parses raw bytes into method, path, query params, headers dict, and body. Supports percent-decoded query strings and Content-Length body extraction.

2. HTTP Response Builder (HTTPResponse.build)
Builds proper HTTP/1.1 response bytes with status line, headers (Content-Type, Content-Length, Server, Date, Connection), and body.

3. Router (Router / Route)
Pattern-based routing with named URL parameters (/users/<id>). Supports decorator-style registration (@server.get("/path")).

4. Static File Server (StaticFileServer)
Serves files from a root directory with: - Path traversal protection (normalizes paths, checks startswith(root_dir)) - If-Modified-Since / 304 Not Modified support - MIME type detection via mimetypes - index.html for directory paths - Method validation (only GET/HEAD)

5. Keep-Alive Connection Handling
- HTTP/1.1 defaults to keep-alive; closes only on Connection: close - HTTP/1.0 defaults to close; keeps alive only on Connection: keep-alive - Multiple requests over a single connection work

6. Error Handling
- 400 for malformed requests - 404 for unknown routes / missing files - 405 for wrong methods on existing static files - 403 for path traversal - 500 for unhandled exceptions - Well-formed error response bodies

# Core architecture (key classes)
class HTTPRequest:    # parse(raw_data) → method, path, headers, body, query_params
class HTTPResponse:   # build() → bytes; set_header(), set_content_type()
class Route:          # match(method, path) → (handler, params)
class Router:         # dispatch(request) → response; add_route() + decorators
class StaticFileServer:  # serve(request) → response with file or error
class HTTPServer:     # start() → accept loop; _handle_client() per connection

Example handlers:

server = HTTPServer(static_dir="./static")

@server.get("/")
def index(request):
    return HTTPResponse(200, "<h1>Hello</h1>", {"Content-Type": "text/html"})

@server.get("/api/info")
def api_info(request):
    import json
    return HTTPResponse(200, json.dumps({"status": "ok"}),
                        {"Content-Type": "application/json"})

server.start()

Evidence & signatures

All **37 tests pass**. The test suite (`test_server.py`) covers:

| Category | Tests | What's Verified |
|---|---|---|
| **Request Parsing** (12) | `test_simple_get`, `test_get_with_query`, `test_get_with_encoded_query`, `test_post_with_body`, `test_post_without_body`, `test_http10/11_keep_alive_*`, `test_invalid_request_line`, `test_empty_request`, `test_multiple_headers_same_name` | Method/path/header extraction, query string decoding, HTTP/1.0 vs 1.1 keep-alive semantics, error handling |
| **Response Building** (5) | `test_basic_response`, `test_404_response`, `test_custom_headers`, `test_from_status`, `test_304_no_body` | Status line format, header injection, status texts, empty body for 304 |
| **Functional Server** (20) | `test_get_index`, `test_404`, `test_echo_with_query`, `test_api_info`, `test_static_file`, `test_static_nested_file`, `test_static_index_in_subdir`, `test_path_traversal_denied`, `test_method_not_allowed`, `test_post_to_data_endpoint`, `test_get_data_endpoint`, `test_server_header`, `test_content_type_for_html/txt`, `test_keep_alive_*` (4), `test_multiple_requests_single_connection`, `test_bad_request` | End-to-end socket communication, all status codes, static serving, path traversal blocking, keep-alive pipelining, connection close semantics |

**Edge cases verified:**
- **Path traversal**: `GET /../../../etc/passwd` → **403 Forbidden**
- **Malformed requests**: `GET invalid-path HTTP/1.1` (no leading `/`) → **400 Bad Request**
- **Method on static file**: `POST /test.txt` → **405 Method Not Allowed**
- **HTTP/1.0 keep-alive**: explicit `Connection: keep-alive` → `keep-alive` in response; default → `close`
- **HTTP/1.1 keep-alive**: default → `keep-alive`; `Connection: close` → `close`
- **Pipelined requests**: Two GET requests on same socket → both responses received
- **Empty body POST**: `POST /data` without body → parses successfully
- **If-Modified-Since**: Static files with matching mtime → **304 Not Modified**

---
{"model": "claude-3.5-sonnet", "problem_class": "net-http-server-from-scratch", "result": "passed", "tests": 37}
Generated from the verified corpus · MIT licensedBack to the catalog