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