diff --git a/src/main.zig b/src/main.zig index 132b65e..ec281c0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,14 +1,16 @@ const std = @import("std"); const http = @import("apple_pie"); +const hzzp = @import("hzzp"); const images_dir_path = "./images"; pub fn main() anyerror!void { - std.log.info("All your codebase are belong to us.", .{}); + std.log.info("welcome to webscale", .{}); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); + // TODO: configurable addr via env var const bind_addr = try std.net.Address.parseIp("0.0.0.0", 8080); std.log.info("serving on {}", .{bind_addr}); @@ -46,10 +48,20 @@ fn generateImageId(buffer: []u8) []const u8 { return buffer[0..i]; } -const Part = struct {}; +const StreamT = std.io.FixedBufferStream([]const u8); + +const ContentDisposition = struct { + name: []const u8, + filename: []const u8, +}; + +const Part = struct { + disposition: ContentDisposition, + content_type: []const u8, +}; const Multipart = struct { - body: []const u8, + stream: StreamT, boundary: []const u8, cursor: usize = 0, @@ -70,11 +82,68 @@ const Multipart = struct { _ = boundary_it.next(); const boundary_value = boundary_it.next() orelse return error.InvalidBoundary; - return Self{ .body = body, .boundary = boundary_value }; + return Self{ + .stream = StreamT{ .buffer = body, .pos = 0 }, + .boundary = boundary_value, + }; } - pub fn next(self: *Self) !?Part { - return null; + pub fn next(self: *Self, hzzp_buffer: []u8) !?Part { + var reader = self.stream.reader(); + // first self.boundary.len+2 bytes MUST be boundary + \r + \n + var boundary_buffer: [512]u8 = undefined; + const maybe_boundary_raw = (try reader.readUntilDelimiterOrEof(&boundary_buffer, '\r')).?; + + const maybe_boundary_strip1 = std.mem.trimRight(u8, maybe_boundary_raw, "\n"); + const maybe_boundary_strip2 = std.mem.trimRight(u8, maybe_boundary_strip1, "\r"); + if (!std.mem.eql(u8, maybe_boundary_strip2, self.boundary)) { + std.log.err("expected '{s}', got '{s}'", .{ self.boundary, maybe_boundary_strip2 }); + return error.InvalidBoundaryBody; + } + + // from there ownwards, its just http! + var parser = hzzp.parser.request.create(hzzp_buffer, reader); + + // This is a hack so that it doesnt try to parse an http header. + parser.state = .header; + + var content_disposition: ?ContentDisposition = null; + var content_type: ?[]const u8 = null; + + while (try parser.next()) |event| { + switch (event) { + .status => unreachable, + .header => |header| { + // TODO lowercase header name + if (std.mem.eql(u8, header.name, "Content-Disposition")) { + // parse disposition + var disposition_it = std.mem.split(header.value, ";"); + _ = disposition_it.next(); + + var dispo_name: []const u8 = undefined; + var dispo_filename: []const u8 = undefined; + + while (disposition_it.next()) |disposition_part_raw| { + const disposition_part = std.mem.trim(u8, disposition_part_raw, " "); + } + + content_disposition = ContentDisposition{ + .name = dispo_name, + .filename = dispo_filename, + }; + } else if (std.mem.eql(u8, header.name, "Content-Type")) { + content_type = header.value; + } + }, + .end => break, + else => @panic("shit"), + } + } + + return Part{ + .disposition = content_disposition.?, + .content_type = content_type.?, + }; } }; @@ -94,8 +163,9 @@ fn uploadFile(response: *http.Response, request: http.Request) !void { // parse multipart data var multipart = try Multipart.init(request.body, content_type.?); + var hzzp_buffer: [1024]u8 = undefined; - while (try multipart.next()) |part| { + while (try multipart.next(&hzzp_buffer)) |part| { std.log.info("part: {}", .{part}); } diff --git a/zig.mod b/zig.mod index 51c8307..36e3131 100644 --- a/zig.mod +++ b/zig.mod @@ -6,3 +6,7 @@ dev_dependencies: - src: git https://github.com/Luukdegram/apple_pie name: apple_pie main: src/apple_pie.zig + + - src: git https://github.com/truemedian/hzzp + name: hzzp + main: src/main.zig