Skip to content

Commit 54380e3

Browse files
inhereclaude
andcommitted
feat(core): FastSetCookie accepts variadic func(*http.Cookie) opts
Callers can now adjust the cookie before it's written without falling back to the longer SetCookie signature: c.FastSetCookie("session", "v", 3600, func(ck *http.Cookie) { ck.Secure = true ck.SameSite = http.SameSiteStrictMode }) Defaults (path=/, httpOnly=true, secure=false) are preserved, so all existing FastSetCookie calls keep working unchanged. Implementation now constructs the http.Cookie directly and applies opts in order, letting any field be overridden including Path / MaxAge / Domain. Tests cover both the no-opts default and the multi-opt override path. Both READMEs show the HTTPS Secure + SameSite snippet. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e62b8a2 commit 54380e3

4 files changed

Lines changed: 62 additions & 7 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,15 @@ r.GET("/setcookie", func(c *rux.Context) {
357357
c.WriteString("hello, in " + c.URL().Path)
358358
})
359359

360+
// FastSetCookie accepts optional func(*http.Cookie) callbacks to override
361+
// the developer-friendly defaults — handy for HTTPS / SameSite.
362+
r.GET("/setsecure", func(c *rux.Context) {
363+
c.FastSetCookie("session", "v", 3600, func(ck *http.Cookie) {
364+
ck.Secure = true
365+
ck.SameSite = http.SameSiteStrictMode
366+
})
367+
})
368+
360369
r.GET("/delcookie", func(c *rux.Context) {
361370
val := ctx.Cookie("rux_cookie") // "test-value1"
362371
c.DelCookie("rux_cookie", "rux_cookie2")

README.zh-CN.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,15 @@ r.GET("/setcookie", func(c *rux.Context) {
310310
c.WriteString("hello, in " + c.URL().Path)
311311
})
312312

313+
// FastSetCookie 接收可选的 func(*http.Cookie) 回调,用来覆盖默认设置
314+
// (比如 HTTPS 场景需要的 Secure / SameSite)
315+
r.GET("/setsecure", func(c *rux.Context) {
316+
c.FastSetCookie("session", "v", 3600, func(ck *http.Cookie) {
317+
ck.Secure = true
318+
ck.SameSite = http.SameSiteStrictMode
319+
})
320+
})
321+
313322
r.GET("/delcookie", func(c *rux.Context) {
314323
val := ctx.Cookie("rux_cookie") // "test-value1"
315324
c.DelCookie("rux_cookie", "rux_cookie2")

internal/core/context.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -264,14 +264,29 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string,
264264
}
265265

266266
// FastSetCookie sets a response cookie with developer-friendly defaults:
267-
// path=/, httpOnly=true, secure=false. The Secure attribute is deliberately
268-
// off so cookies work over plain HTTP during local development.
267+
// path=/, httpOnly=true, secure=false. Optional opts run after the cookie
268+
// is built and let you flip Secure, set SameSite, override Path, etc.
269269
//
270-
// SECURITY: for production HTTPS deployments use SetCookie directly with
271-
// secure=true (and consider SameSite via http.SetCookie if you need it).
272-
// This default is preserved for backward compatibility.
273-
func (c *Context) FastSetCookie(name, value string, maxAge int) {
274-
c.SetCookie(name, value, maxAge, "/", "", false, true)
270+
// c.FastSetCookie("session", "v", 3600)
271+
// c.FastSetCookie("session", "v", 3600, func(ck *http.Cookie) {
272+
// ck.Secure = true
273+
// ck.SameSite = http.SameSiteStrictMode
274+
// })
275+
//
276+
// The defaults are kept for backward compatibility; HTTPS callers should
277+
// flip Secure via opts (or use SetCookie directly).
278+
func (c *Context) FastSetCookie(name, value string, maxAge int, opts ...func(*http.Cookie)) {
279+
ck := &http.Cookie{
280+
Name: name,
281+
Value: value,
282+
MaxAge: maxAge,
283+
Path: "/",
284+
HttpOnly: true,
285+
}
286+
for _, opt := range opts {
287+
opt(ck)
288+
}
289+
http.SetCookie(c.Resp, ck)
275290
}
276291

277292
// DelCookie deletes one or more cookies by setting MaxAge=-1.

internal/core/context_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,28 @@ func TestContext_FastSetCookie(t *testing.T) {
5858
assert.True(t, strings.Contains(setCookie, "token=xyz"))
5959
assert.True(t, strings.Contains(setCookie, "Path=/"))
6060
assert.True(t, strings.Contains(setCookie, "HttpOnly"))
61+
// Secure off by default — see FastSetCookie godoc.
62+
assert.False(t, strings.Contains(setCookie, "Secure"))
63+
}
64+
65+
func TestContext_FastSetCookie_WithOpts(t *testing.T) {
66+
c := &Context{}
67+
w := httptest.NewRecorder()
68+
c.Init(w, httptest.NewRequest("GET", "/x", nil))
69+
c.FastSetCookie("session", "v", 3600,
70+
func(ck *http.Cookie) { ck.Secure = true },
71+
func(ck *http.Cookie) { ck.SameSite = http.SameSiteStrictMode },
72+
func(ck *http.Cookie) { ck.Domain = "example.com" },
73+
)
74+
75+
setCookie := w.Header().Get("Set-Cookie")
76+
assert.True(t, strings.Contains(setCookie, "session=v"))
77+
assert.True(t, strings.Contains(setCookie, "Secure"))
78+
assert.True(t, strings.Contains(setCookie, "SameSite=Strict"))
79+
assert.True(t, strings.Contains(setCookie, "Domain=example.com"))
80+
// Defaults still preserved unless an opt overrode them.
81+
assert.True(t, strings.Contains(setCookie, "Path=/"))
82+
assert.True(t, strings.Contains(setCookie, "HttpOnly"))
6183
}
6284

6385
func TestContext_DelCookie(t *testing.T) {

0 commit comments

Comments
 (0)