add itty bitty http server for running wasm

This commit is contained in:
Judah Caruso 2026-02-19 23:40:33 -07:00
parent 22ee3e232c
commit f13054ee32

116
thirdparty/tinyhttp.zig vendored Normal file
View file

@ -0,0 +1,116 @@
const std = @import("std");
pub fn main() !void {
const addr = try std.net.Address.parseIp("0.0.0.0", 8080);
var server = try addr.listen(.{ .reuse_address = true });
defer server.deinit();
std.debug.print(":: Listening at http://localhost:8080\n", .{});
while (true) {
const conn = server.accept() catch continue;
defer conn.stream.close();
handle_request(conn.stream) catch continue;
}
}
fn handle_request(stream: std.net.Stream) !void {
var req_buf: [4096]u8 = undefined;
const n = try stream.read(&req_buf);
if (n == 0) return error.Empty;
const request = req_buf[0..n];
// Parse "GET /path HTTP/1.x\r\n..."
const path = blk: {
if (!std.mem.startsWith(u8, request, "GET ")) return send_error(stream, "405 Method Not Allowed");
const rest = request[4..];
const end = std.mem.indexOfScalar(u8, rest, ' ') orelse return error.BadRequest;
break :blk rest[0..end];
};
const raw = if (std.mem.eql(u8, path, "/")) "/p2601.html" else path;
// Strip leading '/' and reject anything suspicious
const rel = std.mem.trimLeft(u8, raw, "/");
if (rel.len == 0 or
std.mem.indexOf(u8, rel, "..") != null or
std.mem.indexOfScalar(u8, rel, '\\') != null or
rel[0] == '/')
{
return send_error(stream, "403 Forbidden");
}
// Open from ./out/
const dir = std.fs.cwd().openDir("out", .{}) catch return send_error(stream, "500 Internal Server Error");
const file = dir.openFile(rel, .{}) catch return send_error(stream, "404 Not Found");
defer file.close();
const stat = try file.stat();
const size = stat.size;
const mime = mime_from_path(rel);
// Send header
var hdr_buf: [512]u8 = undefined;
const header = std.fmt.bufPrint(&hdr_buf,
"HTTP/1.1 200 OK\r\n" ++
"Content-Type: {s}\r\n" ++
"Content-Length: {d}\r\n" ++
"Connection: close\r\n" ++
"\r\n",
.{ mime, size },
) catch return error.HeaderTooLarge;
stream.writeAll(header) catch return;
// Stream the file in chunks
var buf: [8192]u8 = undefined;
while (true) {
const bytes = file.read(&buf) catch return;
if (bytes == 0) break;
stream.writeAll(buf[0..bytes]) catch return;
}
}
fn send_error(stream: std.net.Stream, comptime status: []const u8) error{SendFailed} {
const body = status;
var hdr_buf: [256]u8 = undefined;
const header = std.fmt.bufPrint(&hdr_buf,
"HTTP/1.1 " ++ status ++ "\r\n" ++
"Content-Type: text/plain\r\n" ++
"Content-Length: {d}\r\n" ++
"Connection: close\r\n" ++
"\r\n",
.{body.len},
) catch return error.SendFailed;
stream.writeAll(header) catch {};
stream.writeAll(body) catch {};
return error.SendFailed;
}
fn mime_from_path(path: []const u8) []const u8 {
const ext = std.fs.path.extension(path);
const map = .{
.{ ".html", "text/html;charset=utf-8" },
.{ ".css", "text/css;charset=utf-8" },
.{ ".js", "application/javascript;charset=utf-8" },
.{ ".json", "application/json" },
.{ ".png", "image/png" },
.{ ".jpg", "image/jpeg" },
.{ ".jpeg", "image/jpeg" },
.{ ".gif", "image/gif" },
.{ ".svg", "image/svg+xml" },
.{ ".ico", "image/x-icon" },
.{ ".woff", "font/woff" },
.{ ".woff2", "font/woff2" },
.{ ".ttf", "font/ttf" },
.{ ".wasm", "application/wasm" },
.{ ".pdf", "application/pdf" },
.{ ".txt", "text/plain;charset=utf-8" },
.{ ".xml", "application/xml" },
};
inline for (map) |entry| {
if (std.mem.eql(u8, ext, entry[0])) return entry[1];
}
return "application/octet-stream";
}