From 512c63078156a075362b24432c2944480228cf34 Mon Sep 17 00:00:00 2001 From: Michael Pollind Date: Sun, 10 Aug 2025 18:59:27 -0700 Subject: [PATCH] feat: tweak support for additional cookie fields Signed-off-by: Michael Pollind --- facil.io/lib/facil/http/http.c | 14 +++++++++++--- facil.io/lib/facil/http/http.h | 13 +++++++++++++ src/fio.zig | 6 ++---- src/request.zig | 22 +++++++++++++++++++--- 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/facil.io/lib/facil/http/http.c b/facil.io/lib/facil/http/http.c index 74aa8d5..6366f92 100644 --- a/facil.io/lib/facil/http/http.c +++ b/facil.io/lib/facil/http/http.c @@ -317,12 +317,20 @@ int http_set_cookie(http_s *h, http_cookie_args_s cookie) { if (cookie.http_only) { fiobj_str_write(c, "HttpOnly;", 9); } - if (cookie.secure) { - fiobj_str_write(c, "secure;", 7); - } if(cookie.partitioned) { fiobj_str_write(c, "Partitioned;", 12); } + if(cookie.same_site == HTTP_COOKIE_SAME_SITE_LAX) { + fiobj_str_write(c, "SameSite=Lax;", 13); + } else if (cookie.same_site == HTTP_COOKIE_SAME_SITE_STRICT) { + fiobj_str_write(c, "SameSite=Strict;", 16); + } else if (cookie.same_site == HTTP_COOKIE_SAME_SITE_NONE) { + fiobj_str_write(c, "SameSite=None;", 14); + } + + if (cookie.secure) { + fiobj_str_write(c, "secure;", 7); + } set_header_add(h->private_data.out_headers, HTTP_HEADER_SET_COOKIE, c); return 0; } diff --git a/facil.io/lib/facil/http/http.h b/facil.io/lib/facil/http/http.h index 591d4cb..11a5e35 100644 --- a/facil.io/lib/facil/http/http.h +++ b/facil.io/lib/facil/http/http.h @@ -58,6 +58,7 @@ Compile Time Settings #define FIO_HTTP_EXACT_LOGGING 0 #endif + /** the `http_listen settings, see details in the struct definition. */ typedef struct http_settings_s http_settings_s; @@ -120,6 +121,16 @@ typedef struct { void *udata; } http_s; +/** + * This determins the SameSite attribute of a cookie. + */ +typedef enum { + HTTP_COOKIE_SAME_SITE_DEFAULT, + HTTP_COOKIE_SAME_SITE_LAX, + HTTP_COOKIE_SAME_SITE_STRICT, + HTTP_COOKIE_SAME_SITE_NONE +} http_cookie_same_site_e; + /** * This is a helper for setting cookie data. @@ -155,6 +166,8 @@ typedef struct { unsigned http_only : 1; /** Partitioned storage, with a separate cookie jar per top-level site */ unsigned partitioned: 1; + /** The SameSite attribute, see `http_cookie_same_site_e` for details. */ + unsigned same_site : 3; } http_cookie_args_s; /** diff --git a/src/fio.zig b/src/fio.zig index a5f7484..c200ca5 100644 --- a/src/fio.zig +++ b/src/fio.zig @@ -103,7 +103,7 @@ pub const http_s = extern struct { // unsigned http_only : 1; // } http_cookie_args_s; -pub const http_cookie_args_s = extern struct { +pub const http_cookie_args_s = extern struct { name: [*c]u8, value: [*c]u8, domain: [*c]u8, @@ -114,9 +114,7 @@ pub const http_cookie_args_s = extern struct { path_len: isize, /// in seconds max_age: c_int, - secure: c_uint, - http_only: c_uint, - partitioned: c_uint, + flags: c_uint, }; pub const struct_fio_str_info_s = extern struct { diff --git a/src/request.zig b/src/request.zig index 6cbac68..4129441 100644 --- a/src/request.zig +++ b/src/request.zig @@ -263,6 +263,13 @@ pub fn fiobj2HttpParam(a: Allocator, o: fio.FIOBJ) !?HttpParam { }; } +pub const CookieSameSite = enum(u8) { + Default, + Lax, + Strict, + None +}; + /// Args for setting a cookie pub const CookieArgs = struct { name: []const u8, @@ -274,6 +281,7 @@ pub const CookieArgs = struct { secure: bool = true, http_only: bool = true, partitioned: bool = false, + same_site: CookieSameSite = .Default, }; path: ?[]const u8, @@ -682,9 +690,17 @@ pub fn setCookie(self: *const Request, args: CookieArgs) HttpError!void { .path = if (args.path) |p| util.toCharPtr(p) else null, .path_len = if (args.path) |p| @as(isize, @intCast(p.len)) else 0, .max_age = args.max_age_s, - .secure = if (args.secure) 1 else 0, - .http_only = if (args.http_only) 1 else 0, - .partitioned = if (args.partitioned) 1 else 0, + .flags = ( + ((if (args.secure) @as(c_uint,1) else 0) << 0) | + ((if (args.http_only) @as(c_uint,1) else 0) << 1) | + ((if (args.partitioned) @as(c_uint,1) else 0) << 2) | + ((switch (args.same_site) { + .Default => @as(c_uint, 0), + .Lax => @as(c_uint,1), + .Strict => @as(c_uint,2), + .None => @as(c_uint,3) + }) << 3) + ) }; // TODO WAT?