From b314a0602f82da5cf2028bbbe6672c7f59571860 Mon Sep 17 00:00:00 2001 From: Jacob Quinn Date: Sat, 15 Aug 2026 07:36:59 -0600 Subject: [PATCH 1/2] Write cookie attributes one value per call in stringify Base's varargs write(io, x1, xs...) iterates a heterogeneous tuple and dispatches each element dynamically, which juliac --trim=safe cannot statically resolve. Cookie stringify used it in four places; writing one value per call is byte-identical in output and compiles to direct calls. The server-side Request rebuild half of this work already landed via #1349. Found trimming a Servo app on Julia nightly (JuliaCon 2026 workshop); together with #1349 this clears the last HTTP-owned verify errors there. Co-Authored-By: Claude Fable 5 --- src/http_cookies.jl | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/http_cookies.jl b/src/http_cookies.jl index 47286e1ae..8919b6732 100644 --- a/src/http_cookies.jl +++ b/src/http_cookies.jl @@ -124,12 +124,30 @@ function stringify(c::Cookie, isrequest::Bool=true)::String nm = strip(c.name) !iscookienamevalid(nm) && return "" io = IOBuffer() - write(io, sanitizeCookieName(nm), '=', sanitizeCookieValue(c.value)) + # One value per `write`: Base's varargs `write(io, x1, xs...)` iterates a + # heterogeneous tuple and dispatches each element dynamically, which + # juliac --trim cannot resolve; single-argument writes are direct. + write(io, sanitizeCookieName(nm)) + write(io, '=') + write(io, sanitizeCookieValue(c.value)) if !isrequest - length(c.path) > 0 && write(io, "; Path=", sanitizeCookiePath(c.path)) - length(c.domain) > 0 && validCookieDomain(c.domain) && write(io, "; Domain=", c.domain[1] == '.' ? SubString(c.domain, 2) : c.domain) - validCookieExpires(c.expires) && write(io, "; Expires=", Dates.format(c.expires, Dates.RFC1123Format), " GMT") - c.maxage > 0 && write(io, "; Max-Age=", string(c.maxage)) + if length(c.path) > 0 + write(io, "; Path=") + write(io, sanitizeCookiePath(c.path)) + end + if length(c.domain) > 0 && validCookieDomain(c.domain) + write(io, "; Domain=") + write(io, c.domain[1] == '.' ? SubString(c.domain, 2) : c.domain) + end + if validCookieExpires(c.expires) + write(io, "; Expires=") + write(io, Dates.format(c.expires, Dates.RFC1123Format)) + write(io, " GMT") + end + if c.maxage > 0 + write(io, "; Max-Age=") + write(io, string(c.maxage)) + end c.maxage < 0 && write(io, "; Max-Age=0") c.httponly && write(io, "; HttpOnly") c.secure && write(io, "; Secure") From 70c46a23e16e9e6ad9897c6ec32b00eba5fac050 Mon Sep 17 00:00:00 2001 From: Jacob Quinn Date: Sat, 15 Aug 2026 16:04:25 -0600 Subject: [PATCH 2/2] test: cover cookie stringify in trim builds --- test/http_cookie_tests.jl | 19 +++++++++++++++++++ test/http_trim_cookies.jl | 29 +++++++++++++++++++++++++++++ test/trim_compile_tests.jl | 1 + 3 files changed, 49 insertions(+) create mode 100644 test/http_trim_cookies.jl diff --git a/test/http_cookie_tests.jl b/test/http_cookie_tests.jl index 61d208393..b0dc3fc11 100644 --- a/test/http_cookie_tests.jl +++ b/test/http_cookie_tests.jl @@ -1,6 +1,7 @@ using Test using HTTP using Reseau +using Dates const HT = HTTP @@ -35,6 +36,24 @@ end @test occursin("; Secure", rendered) @test occursin("; SameSite=Lax", rendered) + expires = DateTime(2030, 1, 2, 3, 4, 5) + complete = HT.Cookie( + " complete ", + "a b"; + path="/docs;private", + domain="example.com", + expires, + maxage=-1, + httponly=true, + secure=true, + samesite=HT.SameSiteStrictMode, + ) + expires_text = Dates.format(expires, Dates.RFC1123Format) + @test HT.stringify(complete) == "complete=\"a b\"" + @test HT.stringify(complete, false) == + "complete=\"a b\"; Path=/docsprivate; Domain=example.com; " * + "Expires=$expires_text GMT; Max-Age=0; HttpOnly; Secure; SameSite=Strict" + req_headers = HT.Headers() HT.appendheader(req_headers, "Cookie", "a=1; b=two") request = HT.Request("GET", "/"; headers = req_headers) diff --git a/test/http_trim_cookies.jl b/test/http_trim_cookies.jl new file mode 100644 index 000000000..e8d76365b --- /dev/null +++ b/test/http_trim_cookies.jl @@ -0,0 +1,29 @@ +include("trim_workload_common.jl") + +using Dates + +function run_http_trim_cookies()::Nothing + expires = DateTime(2030, 1, 2, 3, 4, 5) + cookie = HT.Cookie("session", "a b") + cookie.path = "/docs;private" + cookie.domain = ".example.com" + cookie.expires = expires + cookie.maxage = 60 + cookie.secure = true + cookie.httponly = true + cookie.samesite = HT.SameSiteStrictMode + expected = "session=\"a b\"; Path=/docsprivate; Domain=example.com; " * + "Expires=Wed, 02 Jan 2030 03:04:05 GMT; Max-Age=60; " * + "HttpOnly; Secure; SameSite=Strict" + HT.stringify(cookie, false) == expected || error("unexpected response cookie") + HT.stringify(cookie) == "session=\"a b\"" || error("unexpected request cookie") + return nothing +end + +function @main(args::Vector{String})::Cint + _ = args + run_http_trim_cookies() + return 0 +end + +Base.Experimental.entrypoint(main, (Vector{String},)) diff --git a/test/trim_compile_tests.jl b/test/trim_compile_tests.jl index 5fe450fad..faf0c1869 100644 --- a/test/trim_compile_tests.jl +++ b/test/trim_compile_tests.jl @@ -157,6 +157,7 @@ end ("http_trim_client_h2_tcp_roundtrip.jl", "http_trim_client_h2_tcp_roundtrip"), ("http_trim_client_h2_roundtrip.jl", "http_trim_client_h2_roundtrip"), ("http_trim_client_server.jl", "http_trim_client_server"), + ("http_trim_cookies.jl", "http_trim_cookies"), ("http_trim_open_fileserver.jl", "http_trim_open_fileserver"), ("http_trim_http2.jl", "http_trim_http2"), ("http_trim_websocket.jl", "http_trim_websocket"),