Skip to content

Commit

Permalink
feat: support partitioned cookies (cloudwego#1041)
Browse files Browse the repository at this point in the history
  • Loading branch information
Haswf authored Jan 12, 2024
1 parent 119a744 commit a949b0b
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 3 deletions.
1 change: 1 addition & 0 deletions internal/bytestr/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ var (
StrCookieSameSiteLax = []byte("Lax")
StrCookieSameSiteStrict = []byte("Strict")
StrCookieSameSiteNone = []byte("None")
StrCookiePartitioned = []byte("Partitioned")

StrClose = []byte("close")
StrGzip = []byte("gzip")
Expand Down
2 changes: 1 addition & 1 deletion pkg/common/adaptor/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (c *compatResponse) WriteHeader(statusCode int) {
}
if k == consts.HeaderSetCookie {
cookie := protocol.AcquireCookie()
cookie.Parse(vv)
_ = cookie.Parse(vv)
c.h.Header.SetCookie(cookie)
continue
}
Expand Down
32 changes: 31 additions & 1 deletion pkg/protocol/cookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const (
CookieSameSiteStrictMode
// CookieSameSiteNoneMode sets the SameSite flag with the "None" parameter
// see https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
// third-party cookies are phasing out, use Partitioned cookies instead
// see https://developers.google.com/privacy-sandbox/3pcd
CookieSameSiteNoneMode
)

Expand Down Expand Up @@ -101,7 +103,10 @@ type Cookie struct {

httpOnly bool
secure bool
sameSite CookieSameSite
// A partitioned third-party cookie is tied to the top-level site
// where it's initially set and cannot be accessed from elsewhere.
partitioned bool
sameSite CookieSameSite

bufKV argsKV
buf []byte
Expand Down Expand Up @@ -222,6 +227,12 @@ func (c *Cookie) AppendBytes(dst []byte) []byte {
case CookieSameSiteNoneMode:
dst = appendCookiePart(dst, bytestr.StrCookieSameSite, bytestr.StrCookieSameSiteNone)
}

if c.partitioned {
dst = append(dst, ';', ' ')
dst = append(dst, bytestr.StrCookiePartitioned...)
}

return dst
}

Expand Down Expand Up @@ -337,6 +348,7 @@ func (c *Cookie) Reset() {
c.httpOnly = false
c.secure = false
c.sameSite = CookieSameSiteDisabled
c.partitioned = false
}

// Value returns cookie value.
Expand Down Expand Up @@ -438,9 +450,14 @@ func (c *Cookie) ParseBytes(src []byte) error {
} else if utils.CaseInsensitiveCompare(bytestr.StrCookieSameSite, kv.value) {
c.sameSite = CookieSameSiteDefaultMode
}
case 'p': // "partitioned"
if utils.CaseInsensitiveCompare(bytestr.StrCookiePartitioned, kv.value) {
c.partitioned = true
}
}
} // else empty or no match
}

return nil
}

Expand Down Expand Up @@ -496,6 +513,11 @@ func (c *Cookie) SameSite() CookieSameSite {
return c.sameSite
}

// Partitioned returns if cookie is partitioned.
func (c *Cookie) Partitioned() bool {
return c.partitioned
}

// SetSameSite sets the cookie's SameSite flag to the given value.
// set value CookieSameSiteNoneMode will set Secure to true also to avoid browser rejection
func (c *Cookie) SetSameSite(mode CookieSameSite) {
Expand All @@ -515,6 +537,14 @@ func (c *Cookie) SetHTTPOnly(httpOnly bool) {
c.httpOnly = httpOnly
}

// SetPartitioned sets cookie as partitioned. Setting Partitioned to true will also set Secure.
func (c *Cookie) SetPartitioned(partitioned bool) {
c.partitioned = partitioned
if partitioned {
c.SetSecure(true)
}
}

// String returns cookie representation.
func (c *Cookie) String() string {
return string(c.Cookie())
Expand Down
28 changes: 28 additions & 0 deletions pkg/protocol/cookie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ func TestCookieAppendBytes(t *testing.T) {

c.SetExpire(CookieExpireDelete)
testCookieAppendBytes(t, c, "xxx", "yyy", "xxx=yyy; expires=Tue, 10 Nov 2009 23:00:00 GMT; domain=foobar.com; path=/a/b")

c.SetPartitioned(true)
testCookieAppendBytes(t, c, "xxx", "yyy", "xxx=yyy; expires=Tue, 10 Nov 2009 23:00:00 GMT; domain=foobar.com; path=/a/b; secure; Partitioned")
}

func testCookieAppendBytes(t *testing.T, c *Cookie, key, value, expectedS string) {
Expand Down Expand Up @@ -257,6 +260,31 @@ func TestCookieSameSite(t *testing.T) {
}
}

func TestCookiePartitioned(t *testing.T) {
t.Parallel()

var c Cookie

if err := c.Parse("__Host-name=value; Secure; Path=/; SameSite=None; Partitioned;"); err != nil {
t.Fatalf("unexpected error for valid paritionedd cookie: %s", err)
}
if !c.Partitioned() {
t.Fatalf("partitioned must be set")
}

if err := c.Parse("foo=bar"); err != nil {
t.Fatalf("unexpected error: %s", err)
}
c.SetPartitioned(true)
s := c.String()
if !strings.Contains(s, "; Partitioned") {
t.Fatalf("missing Partitioned flag in cookie %q", s)
}
if !strings.Contains(s, "; secure") {
t.Fatalf("missing Secure flag in cookie %q", s)
}
}

func TestCookieMaxAge(t *testing.T) {
t.Parallel()

Expand Down
1 change: 0 additions & 1 deletion pkg/protocol/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -1298,7 +1298,6 @@ func (h *RequestHeader) SetCookie(key, value string) {
}

// SetCookie sets the given response cookie.
//
// It is save re-using the cookie after the function returns.
func (h *ResponseHeader) SetCookie(cookie *Cookie) {
h.cookies = setArgBytes(h.cookies, cookie.Key(), cookie.Cookie(), ArgsHasValue)
Expand Down

0 comments on commit a949b0b

Please sign in to comment.