Static HTTP File Server (python, written by Claude Code)
envgap__claude-code__python-t1-28
Written by a coding agent; not on GitHubWritten 2026-02-27
01 / FAILURE SIGNATURE
As the study recorded it
SyntaxError: global declaration after variable use
Not a benchmark task.
- Its repair changed source code, so it is not an environment task.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
requirements.txt- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/python-t1 #28 · read the task the agent was given
Claude Code wrote this python project from the task below. It does not run on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: Static HTTP File Server Write a program that serves static files over HTTP from a local directory, supporting directory listings, MIME type detection, caching headers, range requests for large files, and access logging. FUNCTIONAL REQUIREMENTS: - Accept a directory path to serve as a command-line argument (default: current directory) - Start an HTTP server on a configurable port via --port flag (default: 8080) and bind address via --host flag (default: 0.0.0.0) - Serve files with correct MIME types auto-detected from file extensions (HTML, CSS, JS, images, fonts, JSON, XML, PDF, video, audio, etc.) - Generate directory listing pages when accessing a directory URL, showing file names, sizes, modification dates, and file type icons, with a --no-listing flag to disable this - Support index files: automatically serve index.html or index.htm if present in a directory - Implement HTTP caching headers: ETag based on file content hash, Last-Modified from file timestamp, and Cache-Control with configurable max-age via --cache flag (default: 3600 seconds) - Support HTTP Range requests for partial content delivery (needed for video/audio streaming and resumable downloads) - Log all requests to console in Common Log Format (CLF): remote address, timestamp, method, path, status code, response size, and response time - Save access logs to a file via --log flag - Support CORS headers via --cors flag to enable cross-origin requests (configurable allowed origins) - Support HTTPS via --ssl flag with --cert and --key flags for certificate and private key file paths - Print server startup information: URL, served directory, features enabled - If no directory is given, create a sample directory with HTML, CSS, JS, image, and text files, then start serving them and print the URL to access each - Handle errors: port already in use, permission denied, symlink traversal prevention, and graceful shutdown on SIGINT Create a complete Python project for a clean Ubuntu 22.04 machine with only Python 3.10+ installed. Include: - Source code - requirements.txt with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
README.md
# Static HTTP File Server - Python (Flask) A static HTTP file server built with Flask that serves files with MIME type detection, directory listing, caching, range requests, CORS, and optional HTTPS support. ## Dependencies - Flask 3.0.1 ## Setup ```bash pip install -r requirements.txt ``` ## Usage ```bash # Basic usage (serves ./public on port 8080) python server.py # Custom directory and port python server.py --dir /path/to/files --port 3000 # With HTTPS python server.py --ssl-cert cert.pem --ssl-key key.pem # Custom CORS origin python server.py --cors-origin "https://example.com" ``` ## Features - **MIME Detection**: Automatic content type detection based on file extension - **Directory Listing**: HTML directory browser with file sizes and modification dates - **Caching**: ETag, Last-Modified, and Cache-Control headers - **Range Requests**: Partial content delivery for large files and media streaming - **CORS**: Configurable cross-origin resource sharing - **HTTPS**: Optional TLS/SSL support via certificate files ## Environment Variables - `SERVE_DIR`: Directory to serve (default: `./public`) - `CACHE_MAX_AGE`: Cache max-age in seconds (default: `3600`) - `CORS_ORIGIN`: Allowed CORS origin (default: `*`)
requirements.txt
flask==3.0.1
server.py
"""
Static HTTP File Server using Flask.
Features:
- Serves static files with automatic MIME type detection
- Directory listing with HTML interface
- Cache control headers (ETag, Last-Modified, Cache-Control)
- Range request support for partial content delivery
- CORS support for cross-origin requests
- Optional HTTPS via SSL certificates
"""
import os
import hashlib
import mimetypes
import ssl
import argparse
from datetime import datetime, timezone
from pathlib import Path
from email.utils import formatdate, parsedate_to_datetime
from flask import (
Flask,
send_file,
request,
Response,
abort,
render_template_string,
)
app = Flask(__name__)
# Configuration
SERVE_DIRECTORY = os.environ.get("SERVE_DIR", os.path.join(os.getcwd(), "public"))
CACHE_MAX_AGE = int(os.environ.get("CACHE_MAX_AGE", 3600))
CORS_ORIGIN = os.environ.get("CORS_ORIGIN", "*")
# Ensure mimetypes are initialized
mimetypes.init()
# Directory listing HTML template
DIRECTORY_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Index of {{ path }}</title>
<style>
body { font-family: monospace; margin: 40px; background: #f5f5f5; }
h1 { color: #333; border-bottom: 1px solid #ccc; padding-bottom: 10px; }
table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; padding: 8px 16px; }
th { background: #e0e0e0; }
tr:nth-child(even) { background: #fff; }
tr:hover { background: #e8e8ff; }
a { color: #0066cc; text-decoration: none; }
a:hover { text-decoration: underline; }
.size { text-align: right; }
</style>
</head>
<body>
<h1>Index of {{ path }}</h1>
<table>
<thead>
<tr><th>Name</th><th>Size</th><th>Last Modified</th></tr>
</thead>
<tbody>
{% if path != '/' %}
<tr><td><a href="../">..</a></td><td>-</td><td>-</td></tr>
{% endif %}
{% for entry in entries %}
<tr>
<td><a href="{{ entry.href }}">{{ entry.name }}</a></td>
<td class="size">{{ entry.size }}</td>
<td>{{ entry.modified }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
"""
def format_size(size_bytes):
"""Format file size into human-readable string."""
if size_bytes < 1024:
return f"{size_bytes} B"
elif size_bytes < 1024 * 1024:
return f"{size_bytes / 1024:.1f} KB"
elif size_bytes < 1024 * 1024 * 1024:
return f"{size_bytes / (1024 * 1024):.1f} MB"
else:
return f"{size_bytes / (1024 * 1024 * 1024):.1f} GB"
def compute_etag(file_path):
"""Compute ETag based on file modification time and size."""
stat = os.stat(file_path)
raw = f"{stat.st_mtime}-{stat.st_size}-{file_path}"
return hashlib.md5(raw.encode()).hexdigest()
def add_cors_headers(response):
"""Add CORS headers to the response."""
response.headers["Access-Control-Allow-Origin"] = CORS_ORIGIN
response.headers["Access-Control-Allow-Methods"] = "GET, HEAD, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "Range, Content-Type"
response.headers["Access-Control-Expose-Headers"] = (
"Content-Length, Content-Range, Accept-Ranges"
)
return response
@app.after_request
def after_request(response):
"""Apply CORS headers to all responses."""
return add_cors_headers(response)
@app.route("/", defaults={"req_path": ""})
@app.route("/<path:req_path>")
def serve(req_path):
"""Main handler for serving files and directory listings."""
# Handle OPTIONS preflight
if request.method == "OPTIONS":
response = Response()
return add_cors_headers(response)
# Resolve the absolute path, preventing directory traversal
abs_path = os.path.normpath(os.path.join(SERVE_DIRECTORY, req_path))
if not abs_path.startswith(os.path.normpath(SERVE_DIRECTORY)):
abort(403)
# Check if path exists
if not os.path.exists(abs_path):
abort(404)
# Directory listing
if os.path.isdir(abs_path):
return handle_directory(abs_path, req_path)
# Serve file
return handle_file(abs_path)
def handle_directory(abs_path, req_path):
"""Generate a directory listing page."""
# Check for index.html
index_path = os.path.join(abs_path, "index.html")
if os.path.isfile(index_path):
return handle_file(index_path)
entries = []
try:
items = sorted(os.listdir(abs_path))
except PermissionError:
abort(403)
for item in items:
item_path = os.path.join(abs_path, item)
try:
stat = os.stat(item_path)
except OSError:
continue
is_dir = os.path.isdir(item_path)
entries.append(
{
"name": item + ("/" if is_dir else ""),
"href": item + ("/" if is_dir else ""),
"size": "-" if is_dir else format_size(stat.st_size),
"modified": datetime.fromtimestamp(
stat.st_mtime, tz=timezone.utc
).strftime("%Y-%m-%d %H:%M:%S"),
}
)
display_path = "/" + req_path if req_path else "/"
html = render_template_string(
DIRECTORY_TEMPLATE, path=display_path, entries=entries
)
return Response(html, mimetype="text/html")
def handle_file(abs_path):
"""Serve a file with caching, MIME detection, and range request support."""
# MIME type detection
mime_type, _ = mimetypes.guess_type(abs_path)
if mime_type is None:
mime_type = "application/octet-stream"
stat = os.stat(abs_path)
file_size = stat.st_size
last_modified = datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc)
etag = compute_etag(abs_path)
# Check If-None-Match (ETag)
if_none_match = request.headers.get("If-None-Match")
if if_none_match and if_none_match.strip('"') == etag:
return Response(status=304)
# Check If-Modified-Since
if_modified_since = request.headers.get("If-Modified-Since")
if if_modified_since:
try:
ims_date = parsedate_to_datetime(if_modified_since)
if last_modified <= ims_date:
return Response(status=304)
except (ValueError, TypeError):
pass
# Common headers
headers = {
"ETag": f'"{etag}"',
"Last-Modified": formatdate(
timeval=stat.st_mtime, localtime=False, usegmt=True
),
"Cache-Control": f"public, max-age={CACHE_MAX_AGE}",
"Accept-Ranges": "bytes",
}
# Handle range requests
range_header = request.headers.get("Range")
if range_header:
return handle_range_request(abs_path, file_size, mime_type, headers, range_header)
# Full file response
response = send_file(abs_path, mimetype=mime_type)
for key, value in headers.items():
response.headers[key] = value
response.headers["Content-Length"] = file_size
return response
def handle_range_request(abs_path, file_size, mime_type, headers, range_header):
"""Handle HTTP Range requests for partial content delivery."""
try:
range_spec = range_header.replace("bytes=", "")
parts = range_spec.split("-")
start = int(parts[0]) if parts[0] else 0
end = int(parts[1]) if parts[1] else file_size - 1
if start >= file_size or end >= file_size or start > end:
return Response(
"Range Not Satisfiable",
status=416,
headers={"Content-Range": f"bytes */{file_size}"},
)
content_length = end - start + 1
with open(abs_path, "rb") as f:
f.seek(start)
data = f.read(content_length)
response = Response(
data,
status=206,
mimetype=mime_type,
headers={
**headers,
"Content-Range": f"bytes {start}-{end}/{file_size}",
"Content-Length": content_length,
},
)
return response
except (ValueError, IndexError):
abort(400)
def create_app():
"""Application factory for creating the Flask app."""
os.makedirs(SERVE_DIRECTORY, exist_ok=True)
return app
def main():
"""Main entry point with CLI argument parsing."""
parser = argparse.ArgumentParser(description="Static HTTP File Server")
parser.add_argument(
"--host", default="0.0.0.0", help="Host to bind to (default: 0.0.0.0)"
)
parser.add_argument(
"--port", type=int, default=8080, help="Port to listen on (default: 8080)"
)
parser.add_argument(
"--dir", default=SERVE_DIRECTORY, help="Directory to serve files from"
)
parser.add_argument(
"--ssl-cert", default=None, help="Path to SSL certificate for HTTPS"
)
parser.add_argument(
"--ssl-key", default=None, help="Path to SSL private key for HTTPS"
)
parser.add_argument(
"--cors-origin", default=CORS_ORIGIN, help="CORS allowed origin (default: *)"
)
args = parser.parse_args()
global SERVE_DIRECTORY, CORS_ORIGIN
SERVE_DIRECTORY = os.path.abspath(args.dir)
CORS_ORIGIN = args.cors_origin
os.makedirs(SERVE_DIRECTORY, exist_ok=True)
ssl_context = None
if args.ssl_cert and args.ssl_key:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(args.ssl_cert, args.ssl_key)
print(f"HTTPS enabled with cert: {args.ssl_cert}")
protocol = "https" if ssl_context else "http"
print(f"Serving files from: {SERVE_DIRECTORY}")
print(f"Server running at {protocol}://{args.host}:{args.port}")
app.run(
host=args.host,
port=args.port,
ssl_context=ssl_context,
debug=False,
)
if __name__ == "__main__":
main()