Skip to content

Commit fe5b6e6

Browse files
committed
feat: add response header support
1 parent d0dc6d4 commit fe5b6e6

File tree

2 files changed

+55
-6
lines changed

2 files changed

+55
-6
lines changed

src/http.zig

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,46 @@
11
const std = @import("std");
22
const Memory = @import("Memory.zig");
3+
const extism = @import("ffi.zig");
4+
5+
pub const Headers = struct {
6+
allocator: std.mem.Allocator,
7+
raw: []const u8,
8+
internal: std.json.ArrayHashMap([]const u8),
9+
10+
/// Get a value (if it exists) from the Headers map at the provided name.
11+
/// NOTE: this may be a multi-value header, and will be a comma-separated list.
12+
pub fn get(self: Headers, name: []const u8) ?std.http.Header {
13+
const val = self.internal.map.get(name);
14+
if (val) |v| {
15+
return std.http.Header{
16+
.name = name,
17+
.value = v,
18+
};
19+
} else {
20+
return null;
21+
}
22+
}
23+
24+
/// Access the internal data to iterate over or mutate as needed.
25+
pub fn internal(self: Headers) std.json.ArrayHashMap([]const u8) {
26+
return self.internal;
27+
}
28+
29+
/// Check if the Headers is empty.
30+
pub fn isEmpty(self: Headers) bool {
31+
return self.internal.map.entries.len == 0;
32+
}
33+
34+
/// Check if a header exists in the Headers.
35+
pub fn contains(self: Headers, key: []const u8) bool {
36+
return self.internal.map.contains(key);
37+
}
38+
39+
pub fn deinit(self: *Headers) void {
40+
self.allocator.free(self.raw);
41+
self.internal.deinit(self.allocator);
42+
}
43+
};
344

445
pub const HttpResponse = struct {
546
memory: Memory,
@@ -23,11 +64,19 @@ pub const HttpResponse = struct {
2364
return self.status;
2465
}
2566

26-
pub fn headers(self: HttpResponse, allocator: std.mem.Allocator) !std.StringArrayHashMap([]u8) {
27-
const buf = try allocator.alloc(u8, @intCast(self.responseHeaders.length));
28-
self.responseHeaders.load(buf);
29-
const out = try std.json.parseFromSlice(std.StringArrayHashMap([]u8), allocator, buf, .{ .allocate = .alloc_always, .ignore_unknown_fields = true });
30-
return out.value;
67+
/// IMPORTANT: it's the caller's responsibility to `deinit` the Headers if returned.
68+
pub fn headers(self: HttpResponse, allocator: std.mem.Allocator) !Headers {
69+
const data = try self.responseHeaders.loadAlloc(allocator);
70+
errdefer allocator.free(data);
71+
72+
const j = try std.json.parseFromSlice(std.json.ArrayHashMap([]const u8), allocator, data, .{ .allocate = .alloc_always, .ignore_unknown_fields = true });
73+
defer j.deinit();
74+
75+
return Headers{
76+
.allocator = allocator,
77+
.raw = data,
78+
.internal = j.value,
79+
};
3180
}
3281
};
3382

src/main.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ pub const Plugin = struct {
227227
const status: u16 = @intCast(extism.http_status_code());
228228

229229
const headersOffset = extism.http_headers();
230-
const headersLength = extism.length(offset);
230+
const headersLength = extism.length_unsafe(headersOffset);
231231
const headersMem = Memory.init(headersOffset, headersLength);
232232

233233
const mem = Memory.init(offset, length);

0 commit comments

Comments
 (0)