This is a starting point for Python solutions to the "Build Your Own HTTP server" Challenge.
HTTP is the protocol that powers the web. In this challenge, you'll build a HTTP/1.1 server that is capable of serving multiple clients.
Along the way you'll learn about TCP servers, HTTP request syntax, and more.
Note: If you're viewing this repo on GitHub, head over to codecrafters.io to try the challenge.
A basic asynchronous HTTP server implemented in Python using the asyncio
library. The server handles GET and POST requests for echoing text, retrieving user-agent information, and managing files in a specified directory.
- Root Endpoint (
/
): Returns a200 OK
response. - Echo Endpoint (
/echo/{text}
): Echoes back the provided text with optional gzip compression if supported by the client. - User-Agent Endpoint (
/user-agent
): Returns the client'sUser-Agent
header value. - File Endpoints (
/files/{filename}
):- GET: Retrieves a file from the specified directory, with optional gzip compression.
- POST: Creates or overwrites a file in the specified directory with the request body content.
- Error Handling: Returns
404 Not Found
for invalid endpoints or missing files. - Logging: Logs client connections, requests, and errors with timestamps.
- Python 3.10+
- No external dependencies required (uses standard library modules:
asyncio
,socket
,gzip
,pathlib
,logging
)
- Clone or download the repository.
- Ensure Python 3.10 or higher is installed.
Run the server using the command below. Optionally, specify a directory for file operations:
./your_program.sh [--directory <path>]
- Without directory: The
/files/*
endpoints will return404
for GET requests and raise an error for POST requests. - With directory: Provide a path for file storage/retrieval (e.g.,
/tmp/files
).
Example:
./your_program.sh --directory /tmp/files
The server listens on localhost:4221
by default.
-
Root:
curl http://localhost:4221/
Response:
HTTP/1.1 200 OK
-
Echo:
curl http://localhost:4221/echo/hello
Response:
hello
(withContent-Type: text/plain
)With compression:
curl -H "Accept-Encoding: gzip" http://localhost:4221/echo/hello --compressed
-
User-Agent:
curl -A "Mozilla/5.0" http://localhost:4221/user-agent
Response:
Mozilla/5.0
-
File GET (with
--directory /tmp/files
):curl http://localhost:4221/files/example.txt
Response: Contents of
/tmp/files/example.txt
or404
if not found. -
File POST (with
--directory /tmp/files
):curl -X POST --data "Hello, World!" http://localhost:4221/files/example.txt
Response:
HTTP/1.1 201 Created
(creates/tmp/files/example.txt
).
- HttpReqLine: Parses the HTTP request line (e.g.,
GET /path HTTP/1.1
). - HttpHeaders: Handles HTTP headers as a dictionary-like object.
- HttpRequest: Represents a parsed HTTP request with method, path, headers, and body.
- HttpResponse: Constructs HTTP responses with status, headers, and body (supports gzip compression).
- Handlers:
echo_handler
: Returns the provided text.user_agent_handler
: Returns the client's User-Agent.files_get_handler
: Retrieves a file.files_post_handler
: Saves a file.
- Main Loop: Uses
asyncio
to handle client connections concurrently.
- HOST:
localhost
(default) - PORT:
4221
(default) - BUFF_SIZE:
1024
bytes for receiving data - Directory: Optional, specified via
--directory
argument
The server logs:
- Client connections and disconnections
- Received requests
- Errors (with stack traces)
Example log output:
2025-04-11 18:09:10,420 [SERVER STARTED] Listening on localhost:4221
2025-04-11 18:09:21,940 [CONNECTED] 127.0.0.1:47920
2025-04-11 18:09:21,940 [RECEIVED from 127.0.0.1:47920] b'GET /echo/abc HTTP/1.1\r\nHost: localhost:4221\r\nUser-Agent: curl/8.12.1\r\nAccept: */*\r\nAccept-Encoding: invalid-encoding\r\n\r\n'
2025-04-11 18:09:21,941 [DISCONNECTED] 127.0.0.1:47920
- Minimal error handling for malformed requests.
- No support for advanced HTTP features (e.g., chunked encoding, keep-alive).
- File operations are synchronous and may block the event loop for large files.
- Single-threaded; relies on
asyncio
for concurrency.
Test the server using tools like curl
, wget
, or a browser. For automated testing, consider a framework like pytest
with an HTTP client library (e.g., aiohttp
).
Example test command:
curl http://localhost:4221/echo/test
Contributions are welcome! Please:
- Fork the repository.
- Create a feature branch.
- Submit a pull request with clear descriptions.
This project is licensed under the MIT License. See the LICENSE
file for details.