-
-
Notifications
You must be signed in to change notification settings - Fork 79
/
bindataformpost.zig
118 lines (105 loc) · 4.58 KB
/
bindataformpost.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
const std = @import("std");
const zap = @import("zap");
const Handler = struct {
var alloc: std.mem.Allocator = undefined;
pub fn on_request(r: zap.Request) void {
// parse for FORM (body) parameters first
r.parseBody() catch |err| {
std.log.err("Parse Body error: {any}. Expected if body is empty", .{err});
};
if (r.body) |body| {
std.log.info("Body length is {any}\n", .{body.len});
}
// parse potential query params (for ?terminate=true)
r.parseQuery();
const param_count = r.getParamCount();
std.log.info("param_count: {}", .{param_count});
// iterate over all params
//
// HERE WE HANDLE THE BINARY FILE
//
const params = r.parametersToOwnedList(Handler.alloc, false) catch unreachable;
defer params.deinit();
for (params.items) |kv| {
if (kv.value) |v| {
std.debug.print("\n", .{});
std.log.info("Param `{s}` in owned list is {any}\n", .{ kv.key.str, v });
switch (v) {
// single-file upload
zap.Request.HttpParam.Hash_Binfile => |*file| {
const filename = file.filename orelse "(no filename)";
const mimetype = file.mimetype orelse "(no mimetype)";
const data = file.data orelse "";
std.log.debug(" filename: `{s}`\n", .{filename});
std.log.debug(" mimetype: {s}\n", .{mimetype});
std.log.debug(" contents: {any}\n", .{data});
},
// multi-file upload
zap.Request.HttpParam.Array_Binfile => |*files| {
for (files.*.items) |file| {
const filename = file.filename orelse "(no filename)";
const mimetype = file.mimetype orelse "(no mimetype)";
const data = file.data orelse "";
std.log.debug(" filename: `{s}`\n", .{filename});
std.log.debug(" mimetype: {s}\n", .{mimetype});
std.log.debug(" contents: {any}\n", .{data});
}
files.*.deinit();
},
else => {
// might be a string param, we don't care
// let's just get it as string
// always_alloc param = false -> the string will be a slice from the request buffer
// --> no deinit necessary
if (r.getParamStr(Handler.alloc, kv.key.str, false)) |maybe_str| {
const value: []const u8 = if (maybe_str) |s| s.str else "(no value)";
// above, we didn't defer s.deinit because the string is just a slice from the request buffer
std.log.debug(" {s} = {s}", .{ kv.key.str, value });
} else |err| {
std.log.err("Error: {any}\n", .{err});
}
},
}
}
}
// check if we received a terminate=true parameter
if (r.getParamStr(Handler.alloc, "terminate", false)) |maybe_str| {
if (maybe_str) |*s| {
std.log.info("?terminate={s}\n", .{s.str});
if (std.mem.eql(u8, s.str, "true")) {
zap.stop();
}
}
} else |err| {
std.log.err("cannot check for terminate param: {any}\n", .{err});
}
r.sendJson("{ \"ok\": true }") catch unreachable;
}
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{
.thread_safe = true,
}){};
const allocator = gpa.allocator();
Handler.alloc = allocator;
// setup listener
var listener = zap.HttpListener.init(
.{
.port = 3000,
.on_request = Handler.on_request,
.log = true,
.max_clients = 10,
.max_body_size = 10 * 1024 * 1024,
.public_folder = ".",
},
);
zap.enableDebugLog();
try listener.listen();
std.log.info("\n\nURL is http://localhost:3000\n", .{});
std.log.info("\ncurl -v --request POST -F [email protected] http://127.0.0.1:3000\n", .{});
std.log.info("\n\nTerminate with CTRL+C or by sending query param terminate=true\n", .{});
zap.start(.{
.threads = 1,
.workers = 1,
});
}