Skip to content

Commit

Permalink
add request cookie helper
Browse files Browse the repository at this point in the history
  • Loading branch information
karlseguin committed Dec 15, 2024
1 parent ab02ff0 commit 78044fd
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
10 changes: 10 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,16 @@ Also, if the body isn't fully read, but the connection is marked for keepalive (

While the `io.Reader` can be used for non-lazy loaded bodies, there's overhead to this. It is better to use it only when you know that the body is large (i.e., a file upload).

## Cookies
Use the `req.cookies` method to get a `Request.Cookie` object. Use `get` to get an optional cookie value for a given cookie name. The cookie name is case sensitive.

```zig
var cookies = req.cookies();
if (cookies.get("auth")) |auth| {
/// ...
}
```

# httpz.Response
The following fields are the most useful:

Expand Down
71 changes: 71 additions & 0 deletions src/request.zig
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ pub const Request = struct {
return self.parseQuery();
}

pub fn cookies(self: *const Request) Cookie {
return .{
.header = self.header("cookie") orelse "",
};
}

pub fn json(self: *Request, comptime T: type) !?T {
const b = self.body() orelse return null;
return try std.json.parseFromSliceLeaky(T, self.arena, b, .{});
Expand Down Expand Up @@ -529,6 +535,28 @@ pub const Request = struct {
return n;
}
};

pub const Cookie = struct {
header: []const u8,

pub fn get(self: Cookie, name: []const u8) ?[]const u8 {
var it = std.mem.splitScalar(u8, self.header, ';');
while (it.next()) |kv| {
if (name.len >= kv.len) {
// need at leat an '=' beyond the name
continue;
}
if (std.mem.startsWith(u8, kv, name) == false) {
continue;
}
if (kv[name.len] != '=') {
continue;
}
return kv[name.len + 1..];
}
return null;
}
};
};

// All the upfront memory allocation that we can do. Each worker keeps a pool
Expand Down Expand Up @@ -1698,6 +1726,49 @@ test "request: fuzz" {
}
}

test "request: cookie" {
defer t.reset();
{
const r = try testParse("PUT / HTTP/1.0\r\n\r\n", .{});
const cookies = r.cookies();
try t.expectEqual(null, cookies.get(""));
try t.expectEqual(null, cookies.get("auth"));
}

{
const r = try testParse("PUT / HTTP/1.0\r\nCookie: \r\n\r\n", .{});
const cookies = r.cookies();
try t.expectEqual(null, cookies.get(""));
try t.expectEqual(null, cookies.get("auth"));
}

{
const r = try testParse("PUT / HTTP/1.0\r\nCookie: auth=hello\r\n\r\n", .{});
const cookies = r.cookies();
try t.expectEqual(null, cookies.get(""));
try t.expectString("hello", cookies.get("auth").?);
try t.expectEqual(null, cookies.get("world"));
}

{
const r = try testParse("PUT / HTTP/1.0\r\nCookie: Name=leto;power=9000\r\n\r\n", .{});
const cookies = r.cookies();
try t.expectEqual(null, cookies.get(""));
try t.expectEqual(null, cookies.get("name"));
try t.expectString("leto", cookies.get("Name").?);
try t.expectString("9000", cookies.get("power").?);
}

{
const r = try testParse("PUT / HTTP/1.0\r\nCookie: Name=Ghanima;id=Name\r\n\r\n", .{});
const cookies = r.cookies();
try t.expectEqual(null, cookies.get(""));
try t.expectEqual(null, cookies.get("name"));
try t.expectString("Ghanima", cookies.get("Name").?);
try t.expectString("Name", cookies.get("id").?);
}
}

fn testParse(input: []const u8, config: Config) !Request {
var ctx = t.Context.allocInit(t.arena.allocator(), .{ .request = config });
ctx.write(input);
Expand Down

0 comments on commit 78044fd

Please sign in to comment.