From dc203cd7001b611f808b43473efaf30ede147304 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Thu, 9 Jul 2026 02:05:42 +0200 Subject: [PATCH] web: expand example executable --- packages/web/src/FileDescriptor.zig | 13 +++++ packages/web/src/http/Parser.zig | 8 +-- packages/web/src/main.zig | 78 +++++++++++++++++++++-------- 3 files changed, 74 insertions(+), 25 deletions(-) diff --git a/packages/web/src/FileDescriptor.zig b/packages/web/src/FileDescriptor.zig index 98027c4..c7caa9a 100644 --- a/packages/web/src/FileDescriptor.zig +++ b/packages/web/src/FileDescriptor.zig @@ -28,6 +28,19 @@ pub const FileDescriptor = enum(i32) { }; } + pub fn open(path: [*:0]const u8, flags: linux.O, perm: linux.mode_t) !FileDescriptor { + while (true) { + const rc = linux.open(path, flags, perm); + switch (errno(rc)) { + .SUCCESS => return @enumFromInt(@as(i32, @intCast(rc))), + .INTR => continue, + .NOENT => return error.FileNotFound, + .ACCESS => return error.AccessDenied, + else => return error.SystemError, + } + } + } + pub fn close(self: FileDescriptor) void { _ = linux.close(@intFromEnum(self)); } diff --git a/packages/web/src/http/Parser.zig b/packages/web/src/http/Parser.zig index 752d7d1..60e4f20 100644 --- a/packages/web/src/http/Parser.zig +++ b/packages/web/src/http/Parser.zig @@ -34,10 +34,10 @@ //! //! The parser is not involved in any HTTP semantics, only its syntax. It is up //! to the user of this parser to respect all of the HTTP standards (if they -//! even choose to). For example, none of the header field valuess are verified. -//! The only exception is `Content-Length`. The parser must know the value to -//! determine the length of the request body. If the value fails to parse as a -//! decimal non-negative integer, a syntax error is returned. Note that +//! even choose to). For example, none of the header field values are verified, +//! except for `Content-Length`. The parser must know the value of +//! `Content-Length` to determine the length of the request body. If the value +//! fails to parse as a decimal non-negative integer, a syntax error is //! according to [RFC 9110, Section 8.6: HTTP Semantics](https://datatracker.ietf.org/doc/html/rfc9110#section-8.6), //! `Content-Length` header field value consisting of the same decimal value //! repeated as a comma-separated list (e.g. `Content-Length: 42, 42`) MAY be diff --git a/packages/web/src/main.zig b/packages/web/src/main.zig index 445d21d..bfa1c4a 100644 --- a/packages/web/src/main.zig +++ b/packages/web/src/main.zig @@ -4,10 +4,11 @@ const web = @import("web"); const linux = std.os.linux; const errno = linux.E.init; const ssl = web.openssl; -const UUID = web.UUID; var running: std.atomic.Value(bool) = .init(true); +const body_write_buffer_huge_pages = 1; + fn interruptionHandler(sig: linux.SIG) callconv(.c) void { var buf: [32]u8 = undefined; @@ -27,6 +28,28 @@ fn interruptionHandler(sig: linux.SIG) callconv(.c) void { } const Handler = struct { + fn notFound(response: *web.Response) !void { + try response.body_writer.writeAll("Not Found\n"); + + try response.header_writer.writeAll(web.http.status.not_found); + try response.header_writer.writeAll("Content-Type: text/plain; charset=utf-8\r\n"); + try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); + try response.header_writer.writeAll("\r\n"); + + response.sendHeadersAndBody(); + } + + fn methodNotAllowed(response: *web.Response) !void { + try response.body_writer.writeAll("Method Not Allowed\n"); + + try response.header_writer.writeAll(web.http.status.method_not_allowed); + try response.header_writer.writeAll("Content-Type: text/plain; charset=utf-8\r\n"); + try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); + try response.header_writer.writeAll("\r\n"); + + response.sendHeadersAndBody(); + } + fn handle(_: *anyopaque, request: *web.Request, response: *web.Response) !void { std.log.info("{f} | {s} {s}", .{ response.connection.address, @@ -34,38 +57,50 @@ const Handler = struct { request.pathname, }); - if (!std.mem.eql(u8, request.pathname, "/")) { - try response.body_writer.writeAll("Not Found\n"); + if (std.mem.eql(u8, request.pathname, "/")) { + if (request.method != .GET) { + try methodNotAllowed(response); + return; + } - try response.header_writer.writeAll(web.http.status.not_found); - try response.header_writer.writeAll("Content-Type: text/plain; charset=utf-8\r\n"); + try response.body_writer.writeAll("{\"ok\":true}\n"); + + try response.header_writer.writeAll(web.http.status.ok); + try response.header_writer.writeAll("Content-Type: application/json\r\n"); try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); try response.header_writer.writeAll("\r\n"); response.sendHeadersAndBody(); - return; - } + } else if (std.mem.startsWith(u8, request.pathname, "/zeroes/")) { + const bytes = std.fmt.parseUnsigned(usize, request.pathname[8..], 10) catch { + try notFound(response); + return; + }; - if (request.method != .GET) { - try response.body_writer.writeAll("Method Not Allowed\n"); + if (bytes > body_write_buffer_huge_pages * 2 * 1024 * 1024) { + try notFound(response); + return; + } - try response.header_writer.writeAll(web.http.status.method_not_allowed); - try response.header_writer.writeAll("Content-Type: text/plain; charset=utf-8\r\n"); + if (request.method != .GET) { + try methodNotAllowed(response); + return; + } + + const buffer = [_]u8{0}; + var data = [_][]const u8{&buffer}; + + try response.body_writer.writeSplatAll(&data, bytes); + + try response.header_writer.writeAll(web.http.status.ok); + try response.header_writer.writeAll("Content-Type: application/octet-stream\r\n"); try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); try response.header_writer.writeAll("\r\n"); response.sendHeadersAndBody(); - return; + } else { + try notFound(response); } - - try response.body_writer.writeAll("{\"ok\":true}\n"); - - try response.header_writer.writeAll(web.http.status.ok); - try response.header_writer.writeAll("Content-Type: application/json\r\n"); - try response.header_writer.print("Content-Length: {d}\r\n", .{response.body_writer.end}); - try response.header_writer.writeAll("\r\n"); - - response.sendHeadersAndBody(); } fn interface() web.RequestHandler { @@ -102,6 +137,7 @@ pub fn main(init: std.process.Init) !void { var server = try web.Server.init(allocator, init.io, .{ .request_handler = Handler.interface(), + .body_write_buffer_huge_pages = body_write_buffer_huge_pages, .ssl_ctx = ssl_ctx, }); defer server.deinit(allocator);