Parse form params
This commit is contained in:
parent
938ee61477
commit
4a98b6a9c4
1 changed files with 68 additions and 15 deletions
|
@ -1,20 +1,70 @@
|
|||
const std = @import("std");
|
||||
const util = @import("util");
|
||||
|
||||
const max_boundary = 70;
|
||||
|
||||
const FormField = struct {
|
||||
//name: []const u8,
|
||||
//disposition: []const u8,
|
||||
//filename: ?[]const u8,
|
||||
//charset: ?[]const u8,
|
||||
value: []const u8,
|
||||
};
|
||||
|
||||
const FormFieldResult = struct {
|
||||
field: FormField,
|
||||
value: []const u8,
|
||||
params: FormDataParams,
|
||||
more: bool,
|
||||
};
|
||||
|
||||
const ParamIter = struct {
|
||||
str: []const u8,
|
||||
index: usize = 0,
|
||||
|
||||
const Param = struct {
|
||||
name: []const u8,
|
||||
value: []const u8,
|
||||
};
|
||||
|
||||
pub fn from(str: []const u8) ParamIter {
|
||||
return .{ .str = str, .index = std.mem.indexOfScalar(u8, str, ';') orelse str.len };
|
||||
}
|
||||
|
||||
pub fn next(self: *ParamIter) ?Param {
|
||||
if (self.index >= self.str.len) return null;
|
||||
|
||||
const start = self.index + 1;
|
||||
const new_start = std.mem.indexOfScalarPos(u8, self.str, start, ';') orelse self.str.len;
|
||||
self.index = new_start;
|
||||
|
||||
const param = std.mem.trim(u8, self.str[start..new_start], " \t");
|
||||
var split = std.mem.split(u8, param, "=");
|
||||
const name = split.first();
|
||||
const value = std.mem.trimLeft(u8, split.rest(), " \t");
|
||||
// TODO: handle quoted values
|
||||
// TODO: handle parse errors
|
||||
|
||||
return Param{
|
||||
.name = name,
|
||||
.value = value,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const FormDataParams = struct {
|
||||
name: ?[]const u8 = null,
|
||||
filename: ?[]const u8 = null,
|
||||
charset: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
fn parseParams(alloc: std.mem.Allocator, comptime T: type, str: []const u8) !T {
|
||||
var result = T{};
|
||||
errdefer util.deepFree(alloc, result);
|
||||
|
||||
var iter = ParamIter.from(str);
|
||||
while (iter.next()) |param| {
|
||||
inline for (comptime std.meta.fieldNames(T)) |f| {
|
||||
if (std.mem.eql(u8, param.name, f)) {
|
||||
@field(result, f) = try util.deepClone(alloc, param.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn isFinalPart(peek_stream: anytype) !bool {
|
||||
const reader = peek_stream.reader();
|
||||
var buf: [2]u8 = undefined;
|
||||
|
@ -32,7 +82,7 @@ fn isFinalPart(peek_stream: anytype) !bool {
|
|||
else => return err,
|
||||
};
|
||||
|
||||
if (std.mem.indexOfScalar(u8, " \r\n", b) == null) {
|
||||
if (std.mem.indexOfScalar(u8, " \t\r\n", b) == null) {
|
||||
try peek_stream.putBackByte(b);
|
||||
break;
|
||||
}
|
||||
|
@ -48,8 +98,6 @@ fn parseFormField(boundary: []const u8, peek_stream: anytype, alloc: std.mem.All
|
|||
var headers = try @import("./request/parser.zig").parseHeaders(alloc, reader);
|
||||
defer headers.deinit();
|
||||
|
||||
std.debug.print("disposition: {?s}\n", .{headers.get("Content-Disposition")});
|
||||
|
||||
var value = std.ArrayList(u8).init(alloc);
|
||||
errdefer value.deinit();
|
||||
|
||||
|
@ -82,9 +130,11 @@ fn parseFormField(boundary: []const u8, peek_stream: anytype, alloc: std.mem.All
|
|||
}
|
||||
|
||||
const terminal = try isFinalPart(peek_stream);
|
||||
const disposition = headers.get("Content-Disposition") orelse return error.NoDisposition;
|
||||
|
||||
return FormFieldResult{
|
||||
.field = .{ .value = value.toOwnedSlice() },
|
||||
.value = value.toOwnedSlice(),
|
||||
.params = try parseParams(alloc, FormDataParams, disposition),
|
||||
.more = !terminal,
|
||||
};
|
||||
}
|
||||
|
@ -106,7 +156,9 @@ pub fn parseFormData(boundary: []const u8, reader: anytype, alloc: std.mem.Alloc
|
|||
|
||||
while (true) {
|
||||
const field = try parseFormField(boundary, &stream, alloc);
|
||||
alloc.free(field.field.value);
|
||||
defer util.deepFree(alloc, field);
|
||||
|
||||
std.debug.print("{any}\n", .{field});
|
||||
|
||||
if (!field.more) return;
|
||||
}
|
||||
|
@ -135,7 +187,7 @@ fn toCrlf(comptime str: []const u8) []const u8 {
|
|||
test "parseFormData" {
|
||||
const body = toCrlf(
|
||||
\\--abcd
|
||||
\\Content-Disposition: form-data; name=first
|
||||
\\Content-Disposition: form-data; name=first; charset=utf8
|
||||
\\
|
||||
\\content
|
||||
\\--abcd
|
||||
|
@ -149,6 +201,7 @@ test "parseFormData" {
|
|||
\\--abcd--
|
||||
\\
|
||||
);
|
||||
|
||||
var stream = std.io.StreamSource{ .const_buffer = std.io.fixedBufferStream(body) };
|
||||
try parseFormData("abcd", stream.reader(), std.testing.allocator);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue