From 322c33a69b58d284684c49bb4fab9d73aa43aad4 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 13 Apr 2021 16:13:49 -0300 Subject: [PATCH 1/3] add basic support for multiple files in multipart --- src/main.zig | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/main.zig b/src/main.zig index bf19559..fa219fb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -228,21 +228,43 @@ const Multipart = struct { // we can use the fact that we know the reader is FixedBufferStream // to extract the remaining body, then trim the boundary! // - // THIS ASSUMES ONLY ONE FILE IS IN THE WIRE. + // + // when we find a marker, we also need to know if its an ending marker, + // because the multipart files are prefixed with the boundary, not suffixed. + // + // bc of that we need to set the reader so that its directly on top of + // the next boundary marker for the next file to work const remaining_body = self.stream.buffer[self.stream.pos..self.stream.buffer.len]; - var end_boundary_buf: [512]u8 = undefined; - const boundary_end_marker = try std.fmt.bufPrint(&end_boundary_buf, "{s}--\r\n", .{self.boundary}); - const actual_body = std.mem.trim(u8, remaining_body, boundary_end_marker); + // read body until we find the boundary end marker (--{s} OR --{s}--) + var it = std.mem.split(remaining_body, self.boundary); + const body = it.next() orelse return error.MissingPartBody; - std.debug.warn("ctype out of this: '{s}'", .{content_type.?}); + const next_boundary_pos = self.stream.pos + body.len; + const next_boundary_body = self.stream.buffer[next_boundary_pos..self.stream.buffer.len]; + + // check out on the next 2 chars + const possible_end = it.next() orelse return error.MissingNextPrefixOrEndSuffix; + + if (std.mem.startsWith(u8, possible_end, "--")) { + return Part{ + .allocator = allocator, + .disposition = content_disposition.?, + .content_type = content_type.?, + .body = body, + }; + } + + // there is a next file, the reader should be shifted forward to the + // boundary marker + self.stream.pos = self.stream.pos + body.len; return Part{ .allocator = allocator, .disposition = content_disposition.?, .content_type = content_type.?, - .body = actual_body, + .body = body, }; } }; @@ -317,12 +339,13 @@ fn fetchFile(response: *http.Response, request: http.Request, filename: []const pub const log_level: std.log.Level = .debug; test "multipart" { + const PART1_REAL_BODY = "Hello!\n"; const body = "--1234\r\n" ++ "Content-Type: text/plain\r\n" ++ "Content-Disposition: form-data; name=file1; filename=ab.txt\r\n" ++ "\r\n" ++ - "Hello!\n" ++ + PART1_REAL_BODY ++ "--1234\r\n" ++ "Content-Type: application/json\r\n" ++ // TODO: add 'content-type' support to content-disposition as well @@ -350,7 +373,7 @@ test "multipart" { std.testing.expectEqualSlices(u8, "text/plain", part1.content_type); std.testing.expectEqualSlices(u8, "file1", part1.disposition.name); std.testing.expectEqualSlices(u8, "ab.txt", part1.disposition.filename); - std.testing.expectEqualSlices(u8, "Hello!", part1.body); + std.testing.expectEqualSlices(u8, PART1_REAL_BODY, part1.body); // var part2 = (try multipart.next(&hzzp_buffer)).?; } From 28c6126ff8842de4ecd993a8ee9b7570349ecba1 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 13 Apr 2021 16:15:54 -0300 Subject: [PATCH 2/3] add test for 2nd part --- src/main.zig | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main.zig b/src/main.zig index fa219fb..5408615 100644 --- a/src/main.zig +++ b/src/main.zig @@ -339,7 +339,12 @@ fn fetchFile(response: *http.Response, request: http.Request, filename: []const pub const log_level: std.log.Level = .debug; test "multipart" { - const PART1_REAL_BODY = "Hello!\n"; + const PART1_REAL_BODY = + "Hello!\n"; + + const PART2_REAL_BODY = + "{\"status\": \"OK\"}\n"; + const body = "--1234\r\n" ++ "Content-Type: text/plain\r\n" ++ @@ -351,7 +356,7 @@ test "multipart" { // TODO: add 'content-type' support to content-disposition as well "Content-Disposition: form-data; name=file2; filename=data.json\r\n" ++ "\r\n" ++ - "{\"status\": \"OK\"}\n" ++ + PART2_REAL_BODY ++ "--1234--\r\n"; var buf: [512]u8 = undefined; @@ -375,5 +380,16 @@ test "multipart" { std.testing.expectEqualSlices(u8, "ab.txt", part1.disposition.filename); std.testing.expectEqualSlices(u8, PART1_REAL_BODY, part1.body); - // var part2 = (try multipart.next(&hzzp_buffer)).?; + var part2 = (try multipart.next(&hzzp_buffer, std.testing.allocator)).?; + defer part2.deinit(); + + std.debug.warn( + "\npart2={}\n", + .{part2}, + ); + + std.testing.expectEqualSlices(u8, "application/json", part2.content_type); + std.testing.expectEqualSlices(u8, "file2", part2.disposition.name); + std.testing.expectEqualSlices(u8, "data.json", part2.disposition.filename); + std.testing.expectEqualSlices(u8, PART2_REAL_BODY, part2.body); } From 11079f2c4d27ac5aebe930f1cbe0bdd0fba9d827 Mon Sep 17 00:00:00 2001 From: Luna Date: Tue, 13 Apr 2021 16:20:32 -0300 Subject: [PATCH 3/3] add support for stream end --- src/main.zig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 5408615..9b81ecb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -149,7 +149,7 @@ const Multipart = struct { 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, '\n')).?; + const maybe_boundary_raw = (try reader.readUntilDelimiterOrEof(&boundary_buffer, '\n')) orelse return null; const maybe_boundary_strip1 = std.mem.trimRight(u8, maybe_boundary_raw, "\n"); const maybe_boundary_strip2 = std.mem.trimRight(u8, maybe_boundary_strip1, "\r"); @@ -248,6 +248,9 @@ const Multipart = struct { const possible_end = it.next() orelse return error.MissingNextPrefixOrEndSuffix; if (std.mem.startsWith(u8, possible_end, "--")) { + // we just got the ending boundary marker. the reader should be disabled + // for future reads. + self.stream.pos = self.stream.buffer.len; return Part{ .allocator = allocator, .disposition = content_disposition.?, @@ -392,4 +395,10 @@ test "multipart" { std.testing.expectEqualSlices(u8, "file2", part2.disposition.name); std.testing.expectEqualSlices(u8, "data.json", part2.disposition.filename); std.testing.expectEqualSlices(u8, PART2_REAL_BODY, part2.body); + + // stop the loop (if there were any) afterwards + std.testing.expectEqual( + @as(?Part, null), + try multipart.next(&hzzp_buffer, std.testing.allocator), + ); }