add itty bitty http server for running wasm
This commit is contained in:
parent
22ee3e232c
commit
f13054ee32
1 changed files with 116 additions and 0 deletions
116
thirdparty/tinyhttp.zig
vendored
Normal file
116
thirdparty/tinyhttp.zig
vendored
Normal 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";
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue