Static HTTP File Server (javascript, written by Codex)
envgap__codex__javascript-t1-28
Written by a coding agent; not on GitHubWritten 2026-03-03
01 / FAILURE SIGNATURE
As the study recorded it
None
Not a benchmark task.
- The project already builds and runs before the fix, so there is nothing to repair.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
package.json- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
codex/javascript-t1 #28 · read the task the agent was given
Codex wrote this javascript project from the task below. It installed and ran 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 JavaScript project for a clean Ubuntu 22.04 machine with only Node.js 20+ (LTS) installed. Include: - Source code - package.json 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
4 files, exactly as written, before any repair.
package-lock.json
{
"name": "static-http-file-server",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "static-http-file-server",
"version": "1.0.0",
"dependencies": {},
"engines": {
"node": ">=20.0.0"
}
}
}
}
package.json
{
"name": "static-http-file-server",
"version": "1.0.0",
"private": true,
"type": "module",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js"
},
"engines": {
"node": ">=20.0.0"
},
"dependencies": {}
}
README.md
# Static HTTP File Server (JavaScript) Serves static files over HTTP/HTTPS with MIME detection, directory listing, cache headers, range support, CORS, and CLF access logs. ## Requirements - Ubuntu 22.04 - Node.js 20+ ## Dependencies - No external runtime dependencies (Node standard library only) - `package-lock.json` is included to pin the dependency graph (empty graph) ## Setup ```bash npm install ``` ## Run Serve current directory: ```bash npm start -- . ``` Custom host/port/cache: ```bash npm start -- . --host 127.0.0.1 --port 9090 --cache 120 ``` Disable listing and enable CORS: ```bash npm start -- . --no-listing --cors "*" ``` Enable access log file: ```bash npm start -- . --log access.log ``` Enable HTTPS: ```bash npm start -- . --ssl --cert ./cert.pem --key ./key.pem ``` Demo mode (no directory argument): creates `sample_static_site` and serves it. ```bash npm start ``` ## Features - Correct MIME types by extension - Directory listings with names/sizes/modified dates/type indicators - Index file support: `index.html`, `index.htm` - Cache headers: - `ETag` (SHA-256 content hash) - `Last-Modified` - `Cache-Control` (`--cache`, default `3600`) - HTTP Range requests (`206 Partial Content`) - Console/file CLF-style access logs with response time - CORS (`--cors <origin>`) - HTTPS (`--ssl --cert --key`) - Symlink traversal prevention and graceful `SIGINT` shutdown ## Example output ```text Static HTTP File Server started URL: http://0.0.0.0:8080 Serving directory: /home/user/project Directory listing: enabled Caching max-age: 3600 CORS: disabled TLS: disabled ```
src/index.js
import crypto from "node:crypto";
import fs from "node:fs";
import http from "node:http";
import https from "node:https";
import path from "node:path";
import process from "node:process";
import { fileURLToPath } from "node:url";
const MIME_TYPES = {
".html": "text/html; charset=utf-8",
".htm": "text/html; charset=utf-8",
".css": "text/css; charset=utf-8",
".js": "application/javascript; charset=utf-8",
".mjs": "application/javascript; charset=utf-8",
".json": "application/json; charset=utf-8",
".xml": "application/xml; charset=utf-8",
".txt": "text/plain; charset=utf-8",
".md": "text/markdown; charset=utf-8",
".csv": "text/csv; charset=utf-8",
".pdf": "application/pdf",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".svg": "image/svg+xml",
".webp": "image/webp",
".ico": "image/x-icon",
".woff": "font/woff",
".woff2": "font/woff2",
".ttf": "font/ttf",
".otf": "font/otf",
".mp4": "video/mp4",
".webm": "video/webm",
".mp3": "audio/mpeg",
".wav": "audio/wav",
".ogg": "audio/ogg"
};
function parseArgs(argv) {
const options = {
host: "0.0.0.0",
port: 8080,
cache: 3600,
noListing: false,
cors: null,
logPath: null,
ssl: false,
cert: null,
key: null,
directory: null
};
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
if (!arg.startsWith("--")) {
if (!options.directory) options.directory = arg;
continue;
}
if (arg === "--no-listing") {
options.noListing = true;
continue;
}
if (arg === "--ssl") {
options.ssl = true;
continue;
}
const key = arg.slice(2);
if (i + 1 >= argv.length) throw new Error(`Missing value for ${arg}`);
const value = argv[i + 1];
i += 1;
if (key === "host") options.host = value;
else if (key === "port") options.port = Number.parseInt(value, 10);
else if (key === "cache") options.cache = Number.parseInt(value, 10);
else if (key === "cors") options.cors = value;
else if (key === "log") options.logPath = value;
else if (key === "cert") options.cert = value;
else if (key === "key") options.key = value;
else throw new Error(`Unknown option: ${arg}`);
}
if (!Number.isInteger(options.port) || options.port <= 0 || options.port > 65535) {
throw new Error("Invalid --port. Expected 1..65535.");
}
if (!Number.isInteger(options.cache) || options.cache < 0) {
throw new Error("Invalid --cache. Expected integer >= 0.");
}
if (options.ssl && (!options.cert || !options.key)) {
throw new Error("When --ssl is enabled, both --cert and --key are required.");
}
return options;
}
function ensureDir(dirPath) {
fs.mkdirSync(dirPath, { recursive: true });
}
function createSampleDirectory(baseDir) {
ensureDir(baseDir);
const cssDir = path.join(baseDir, "assets");
const jsDir = path.join(baseDir, "scripts");
ensureDir(cssDir);
ensureDir(jsDir);
fs.writeFileSync(
path.join(baseDir, "index.html"),
`<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Sample Static Server</title>
<link rel="stylesheet" href="/assets/style.css" />
</head>
<body>
<h1>Sample Static Server</h1>
<p>If you can read this page, static serving works.</p>
<img src="/sample.svg" alt="sample image" width="180" />
<p><a href="/hello.txt">Open text file</a></p>
<script src="/scripts/app.js"></script>
</body>
</html>
`,
"utf8"
);
fs.writeFileSync(
path.join(cssDir, "style.css"),
"body { font-family: sans-serif; margin: 2rem; } h1 { color: #1a5d8f; }\n",
"utf8"
);
fs.writeFileSync(path.join(jsDir, "app.js"), "console.log('Sample JS loaded');\n", "utf8");
fs.writeFileSync(path.join(baseDir, "hello.txt"), "Hello from sample static directory.\n", "utf8");
fs.writeFileSync(
path.join(baseDir, "sample.svg"),
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 120">
<rect width="320" height="120" fill="#e9f3fb"/>
<circle cx="60" cy="60" r="32" fill="#1a5d8f"/>
<text x="110" y="68" font-size="22" fill="#1a5d8f">Static Server</text>
</svg>
`,
"utf8"
);
}
function getMimeType(filePath) {
return MIME_TYPES[path.extname(filePath).toLowerCase()] || "application/octet-stream";
}
function iconFor(entryName, isDir) {
if (isDir) return "[DIR]";
const ext = path.extname(entryName).toLowerCase();
if ([".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp"].includes(ext)) return "[IMG]";
if ([".mp4", ".webm", ".mp3", ".wav", ".ogg"].includes(ext)) return "[MED]";
if ([".html", ".htm", ".css", ".js", ".json", ".xml", ".txt", ".md"].includes(ext)) return "[TXT]";
return "[FIL]";
}
function htmlEscape(value) {
return value
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
function formatSize(size) {
const units = ["B", "KB", "MB", "GB"];
let value = size;
let i = 0;
while (value >= 1024 && i < units.length - 1) {
value /= 1024;
i += 1;
}
return `${value.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
}
function formatDate(d) {
return d.toISOString().replace("T", " ").replace("Z", " UTC");
}
function clfTimestamp(date) {
const day = String(date.getDate()).padStart(2, "0");
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const month = months[date.getMonth()];
const year = date.getFullYear();
const hh = String(date.getHours()).padStart(2, "0");
const mm = String(date.getMinutes()).padStart(2, "0");
const ss = String(date.getSeconds()).padStart(2, "0");
const tzOffset = -date.getTimezoneOffset();
const sign = tzOffset >= 0 ? "+" : "-";
const tzAbs = Math.abs(tzOffset);
const tzH = String(Math.floor(tzAbs / 60)).padStart(2, "0");
const tzM = String(tzAbs % 60).padStart(2, "0");
return `${day}/${month}/${year}:${hh}:${mm}:${ss} ${sign}${tzH}${tzM}`;
}
function computeEtag(filePath) {
const data = fs.readFileSync(filePath);
return `"${crypto.createHash("sha256").update(data).digest("hex")}"`;
}
function normalizeClientIp(value) {
if (!value) return "-";
return value.startsWith("::ffff:") ? value.slice(7) : value;
}
function sendError(res, statusCode, message, corsOrigin) {
if (corsOrigin) {
res.setHeader("Access-Control-Allow-Origin", corsOrigin);
res.setHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "*");
}
res.statusCode = statusCode;
res.setHeader("Content-Type", "text/plain; charset=utf-8");
const body = `${statusCode} ${message}\n`;
res.setHeader("Content-Length", Buffer.byteLength(body));
res.end(body);
}
function directoryListingHtml(requestPath, directoryPath) {
const entries = fs.readdirSync(directoryPath, { withFileTypes: true }).sort((a, b) => {
if (a.isDirectory() && !b.isDirectory()) return -1;
if (!a.isDirectory() && b.isDirectory()) return 1;
return a.name.localeCompare(b.name);
});
const rows = [];
if (requestPath !== "/") {
const parent = path.posix.dirname(requestPath.replace(/\/$/, "")) || "/";
rows.push(
`<tr><td>[UP]</td><td><a href="${htmlEscape(parent.endsWith("/") ? parent : `${parent}/`)}">..</a></td><td>-</td><td>-</td></tr>`
);
}
for (const entry of entries) {
const fullPath = path.join(directoryPath, entry.name);
const stat = fs.statSync(fullPath);
const slash = entry.isDirectory() ? "/" : "";
const href = path.posix.join(requestPath, entry.name).replace(/\\/g, "/") + slash;
rows.push(
`<tr><td>${iconFor(entry.name, entry.isDirectory())}</td><td><a href="${htmlEscape(href)}">${htmlEscape(
entry.name + slash
)}</a></td><td>${entry.isDirectory() ? "-" : formatSize(stat.size)}</td><td>${formatDate(stat.mtime)}</td></tr>`
);
}
return `<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Index of ${htmlEscape(requestPath)}</title>
<style>
body { font-family: sans-serif; margin: 1.2rem; }
table { border-collapse: collapse; width: 100%; }
td, th { border-bottom: 1px solid #ddd; padding: 0.5rem; text-align: left; }
</style>
</head>
<body>
<h1>Index of ${htmlEscape(requestPath)}</h1>
<table>
<thead><tr><th>Type</th><th>Name</th><th>Size</th><th>Modified</th></tr></thead>
<tbody>${rows.join("")}</tbody>
</table>
</body>
</html>`;
}
function parseRange(rangeHeader, totalSize) {
const match = /^bytes=(\d*)-(\d*)$/.exec(rangeHeader ?? "");
if (!match) return null;
let start = match[1] === "" ? null : Number.parseInt(match[1], 10);
let end = match[2] === "" ? null : Number.parseInt(match[2], 10);
if ((start !== null && Number.isNaN(start)) || (end !== null && Number.isNaN(end))) return null;
if (start === null && end === null) return null;
if (start === null) {
const length = end;
if (length <= 0) return null;
start = Math.max(0, totalSize - length);
end = totalSize - 1;
} else if (end === null || end >= totalSize) {
end = totalSize - 1;
}
if (start < 0 || end < start || start >= totalSize) return null;
return { start, end };
}
function createLogger(logPath) {
const stream = logPath ? fs.createWriteStream(logPath, { flags: "a" }) : null;
return {
write(line) {
process.stdout.write(`${line}\n`);
if (stream) stream.write(`${line}\n`);
},
close() {
if (stream) stream.end();
}
};
}
function safeResolve(rootReal, requestPath) {
const decoded = decodeURIComponent(requestPath);
const resolved = path.resolve(rootReal, `.${decoded}`);
if (resolved === rootReal || resolved.startsWith(`${rootReal}${path.sep}`)) {
return resolved;
}
throw new Error("Path traversal blocked.");
}
function startServer(options) {
let rootDir = options.directory;
const cwd = process.cwd();
if (!rootDir) {
rootDir = path.join(cwd, "sample_static_site");
createSampleDirectory(rootDir);
}
if (!fs.existsSync(rootDir) || !fs.statSync(rootDir).isDirectory()) {
throw new Error(`Directory does not exist or is not a directory: ${rootDir}`);
}
const rootReal = fs.realpathSync(rootDir);
const logger = createLogger(options.logPath);
const requestHandler = (req, res) => {
const started = process.hrtime.bigint();
let statusCode = 500;
let responseSize = 0;
const parsedUrl = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
const corsOrigin = options.cors;
const finishLog = () => {
const elapsedMs = Number(process.hrtime.bigint() - started) / 1_000_000;
const ip = normalizeClientIp(req.socket?.remoteAddress);
const line = `${ip} - - [${clfTimestamp(new Date())}] "${req.method || "-"} ${parsedUrl.pathname} HTTP/${req.httpVersion}" ${statusCode} ${responseSize} ${elapsedMs.toFixed(2)}ms`;
logger.write(line);
};
try {
if (corsOrigin) {
res.setHeader("Access-Control-Allow-Origin", corsOrigin);
res.setHeader("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "*");
}
if (req.method === "OPTIONS") {
statusCode = 204;
res.statusCode = 204;
res.end();
finishLog();
return;
}
if (req.method !== "GET" && req.method !== "HEAD") {
statusCode = 405;
sendError(res, 405, "Method Not Allowed", corsOrigin);
responseSize = 23;
finishLog();
return;
}
let resolvedPath = safeResolve(rootReal, parsedUrl.pathname);
if (!fs.existsSync(resolvedPath)) {
statusCode = 404;
sendError(res, 404, "Not Found", corsOrigin);
responseSize = 14;
finishLog();
return;
}
const entryReal = fs.realpathSync(resolvedPath);
if (!(entryReal === rootReal || entryReal.startsWith(`${rootReal}${path.sep}`))) {
statusCode = 403;
sendError(res, 403, "Forbidden", corsOrigin);
responseSize = 14;
finishLog();
return;
}
resolvedPath = entryReal;
let stat = fs.statSync(resolvedPath);
if (stat.isDirectory()) {
const indexHtml = path.join(resolvedPath, "index.html");
const indexHtm = path.join(resolvedPath, "index.htm");
if (fs.existsSync(indexHtml) && fs.statSync(indexHtml).isFile()) {
resolvedPath = indexHtml;
stat = fs.statSync(resolvedPath);
} else if (fs.existsSync(indexHtm) && fs.statSync(indexHtm).isFile()) {
resolvedPath = indexHtm;
stat = fs.statSync(resolvedPath);
} else {
if (options.noListing) {
statusCode = 403;
sendError(res, 403, "Directory Listing Disabled", corsOrigin);
responseSize = 31;
finishLog();
return;
}
const withSlash = parsedUrl.pathname.endsWith("/") ? parsedUrl.pathname : `${parsedUrl.pathname}/`;
const html = directoryListingHtml(withSlash, resolvedPath);
const bodyBytes = Buffer.byteLength(html);
statusCode = 200;
responseSize = req.method === "HEAD" ? 0 : bodyBytes;
res.statusCode = 200;
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.setHeader("Content-Length", bodyBytes);
if (req.method === "HEAD") res.end();
else res.end(html);
finishLog();
return;
}
}
if (!stat.isFile()) {
statusCode = 403;
sendError(res, 403, "Forbidden", corsOrigin);
responseSize = 14;
finishLog();
return;
}
const etag = computeEtag(resolvedPath);
const lastModified = stat.mtime.toUTCString();
res.setHeader("ETag", etag);
res.setHeader("Last-Modified", lastModified);
res.setHeader("Cache-Control", `public, max-age=${options.cache}`);
res.setHeader("Accept-Ranges", "bytes");
res.setHeader("Content-Type", getMimeType(resolvedPath));
const ifNoneMatch = req.headers["if-none-match"];
const ifModifiedSince = req.headers["if-modified-since"];
if (
(typeof ifNoneMatch === "string" && ifNoneMatch === etag) ||
(typeof ifModifiedSince === "string" && new Date(ifModifiedSince) >= stat.mtime)
) {
statusCode = 304;
res.statusCode = 304;
res.end();
finishLog();
return;
}
const range = parseRange(typeof req.headers.range === "string" ? req.headers.range : null, stat.size);
if (req.headers.range && !range) {
statusCode = 416;
res.statusCode = 416;
res.setHeader("Content-Range", `bytes */${stat.size}`);
res.end();
finishLog();
return;
}
let start = 0;
let end = stat.size - 1;
if (range) {
start = range.start;
end = range.end;
statusCode = 206;
res.statusCode = 206;
res.setHeader("Content-Range", `bytes ${start}-${end}/${stat.size}`);
} else {
statusCode = 200;
res.statusCode = 200;
}
const size = end - start + 1;
responseSize = req.method === "HEAD" ? 0 : size;
res.setHeader("Content-Length", size);
if (req.method === "HEAD") {
res.end();
finishLog();
return;
}
const stream = fs.createReadStream(resolvedPath, { start, end });
stream.on("error", () => {
if (!res.headersSent) sendError(res, 500, "Internal Server Error", corsOrigin);
});
res.on("finish", finishLog);
stream.pipe(res);
} catch (error) {
statusCode = statusCode === 500 ? 500 : statusCode;
responseSize = 0;
sendError(res, 500, "Internal Server Error", corsOrigin);
finishLog();
}
};
let server;
if (options.ssl) {
const cert = fs.readFileSync(options.cert);
const key = fs.readFileSync(options.key);
server = https.createServer({ cert, key }, requestHandler);
} else {
server = http.createServer(requestHandler);
}
server.on("error", (err) => {
if (err && err.code === "EADDRINUSE") {
console.error(`Error: Port ${options.port} is already in use.`);
} else if (err && err.code === "EACCES") {
console.error(`Error: Permission denied on ${options.host}:${options.port}.`);
} else {
console.error(`Server error: ${err.message}`);
}
process.exitCode = 1;
});
server.listen(options.port, options.host, () => {
const protocol = options.ssl ? "https" : "http";
const url = `${protocol}://${options.host}:${options.port}`;
console.log("Static HTTP File Server started");
console.log(`URL: ${url}`);
console.log(`Serving directory: ${rootReal}`);
console.log(`Directory listing: ${options.noListing ? "disabled" : "enabled"}`);
console.log(`Caching max-age: ${options.cache}`);
console.log(`CORS: ${options.cors ? `enabled (${options.cors})` : "disabled"}`);
console.log(`TLS: ${options.ssl ? "enabled" : "disabled"}`);
if (!options.directory) {
console.log(`Sample URLs: ${url}/ ${url}/assets/style.css ${url}/scripts/app.js ${url}/sample.svg ${url}/hello.txt`);
}
});
process.on("SIGINT", () => {
console.log("\nReceived SIGINT, shutting down gracefully...");
server.close(() => {
logger.close();
process.exit(0);
});
});
}
function main() {
try {
const args = parseArgs(process.argv.slice(2));
startServer(args);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
main();