Repository: valyala/fasthttp
Pull request #1746 (2024-04-02 02:54:50)

Add macos-14 platform, enable shuffle of unit-tests
- Add `macos-14` platform which uses Apple M1 cpu's (ARM64).
- Add `-shuffle=on` to enable shuffling the unit-tests order. Each run will run tests in different order to catch tests depending on others or using data from other tests.

We recently enabled both of these options in gofiber with great success. https://github.com/gofiber/fiber/blob/main/.github/workflows/test.yml#L37

gaby: @erikdubbelboer The test that failed is known to fail? 

Patch:
diff --git a/cookie.go b/cookie.go
index d246b2d..e99ea67 100644
--- a/cookie.go
+++ b/cookie.go
@@ -33,7 +33,7 @@ const (
 	CookieSameSiteStrictMode
 	// CookieSameSiteNoneMode sets the SameSite flag with the "None" parameter.
 	// See https://tools.ietf.org/html/draft-west-cookie-incrementalism-00
-	CookieSameSiteNoneMode
+	CookieSameSiteNoneMode // third-party cookies are phasing out, use Partitioned cookies instead
 )
 
 // AcquireCookie returns an empty Cookie object from the pool.
@@ -74,9 +74,10 @@ type Cookie struct {
 	domain []byte
 	path   []byte
 
-	httpOnly bool
-	secure   bool
-	sameSite CookieSameSite
+	httpOnly    bool
+	secure      bool
+	sameSite    CookieSameSite
+	partitioned bool
 
 	bufKV argsKV
 	buf   []byte
@@ -94,6 +95,7 @@ func (c *Cookie) CopyTo(src *Cookie) {
 	c.httpOnly = src.httpOnly
 	c.secure = src.secure
 	c.sameSite = src.sameSite
+	c.partitioned = src.partitioned
 }
 
 // HTTPOnly returns true if the cookie is http only.
@@ -130,6 +132,21 @@ func (c *Cookie) SetSameSite(mode CookieSameSite) {
 	}
 }
 
+// Partitioned returns true if the cookie is partitioned.
+func (c *Cookie) Partitioned() bool {
+	return c.partitioned
+}
+
+// SetPartitioned sets the cookie's Partitioned flag to the given value.
+// Set value Partitioned to true will set Secure to true and Path to / also to avoid browser rejection.
+func (c *Cookie) SetPartitioned(partitioned bool) {
+	c.partitioned = partitioned
+	if partitioned {
+		c.SetSecure(true)
+		c.SetPath("/")
+	}
+}
+
 // Path returns cookie path.
 func (c *Cookie) Path() []byte {
 	return c.path
@@ -247,6 +264,7 @@ func (c *Cookie) Reset() {
 	c.httpOnly = false
 	c.secure = false
 	c.sameSite = CookieSameSiteDisabled
+	c.partitioned = false
 }
 
 // AppendBytes appends cookie representation to dst and returns
@@ -304,6 +322,10 @@ func (c *Cookie) AppendBytes(dst []byte) []byte {
 		dst = append(dst, '=')
 		dst = append(dst, strCookieSameSiteNone...)
 	}
+	if c.partitioned {
+		dst = append(dst, ';', ' ')
+		dst = append(dst, strCookiePartitioned...)
+	}
 	return dst
 }
 
@@ -425,6 +447,10 @@ func (c *Cookie) ParseBytes(src []byte) error {
 				} else if caseInsensitiveCompare(strCookieSameSite, kv.value) {
 					c.sameSite = CookieSameSiteDefaultMode
 				}
+			case 'p': // "partitioned"
+				if caseInsensitiveCompare(strCookiePartitioned, kv.value) {
+					c.partitioned = true
+				}
 			}
 		} // else empty or no match
 	}
diff --git a/go.mod b/go.mod
index f39397e..a4c287f 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/valyala/bytebufferpool v1.0.0
 	github.com/valyala/tcplisten v1.0.0
 	golang.org/x/crypto v0.21.0
-	golang.org/x/net v0.22.0
+	golang.org/x/net v0.23.0
 	golang.org/x/sys v0.18.0
 )
 
diff --git a/go.sum b/go.sum
index a48ab9a..1293d45 100644
--- a/go.sum
+++ b/go.sum
@@ -8,8 +8,8 @@ github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVS
 github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
 golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
 golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
-golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
diff --git a/strings.go b/strings.go
index a9e4072..36ed029 100644
--- a/strings.go
+++ b/strings.go
@@ -63,6 +63,7 @@ var (
 	strCookiePath           = []byte("path")
 	strCookieHTTPOnly       = []byte("HttpOnly")
 	strCookieSecure         = []byte("secure")
+	strCookiePartitioned    = []byte("Partitioned")
 	strCookieMaxAge         = []byte("max-age")
 	strCookieSameSite       = []byte("SameSite")
 	strCookieSameSiteLax    = []byte("Lax")


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 5cfc4e5..731b456 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,7 +10,7 @@ jobs:
       fail-fast: false
       matrix:
         go-version: [1.19.x, 1.20.x, 1.21.x, 1.22.x]
-        os: [ubuntu-latest, macos-latest, windows-latest]
+        os: [ubuntu-latest, macos-latest, windows-latest, macos-14]
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v4
@@ -19,5 +19,5 @@ jobs:
           go-version: ${{ matrix.go-version }}
 
       - run: go version
-      - run: go test ./...
-      - run: go test -race ./...
+      - run: go test -shuffle=on ./...
+      - run: go test -race -shuffle=on ./...
diff --git a/client_test.go b/client_test.go
index 4842965..468d447 100644
--- a/client_test.go
+++ b/client_test.go
@@ -2003,9 +2003,8 @@ func TestClientNonIdempotentRetry(t *testing.T) {
 					s: "HTTP/1.1 345 OK\r\nContent-Type: foobar\r\nContent-Length: 7\r\n\r\n0123456",
 				}, nil
 			default:
-				t.Fatalf("unexpected number of dials: %d", dialsCount)
+				return nil, fmt.Errorf("unexpected number of dials: %d", dialsCount)
 			}
-			panic("unreachable")
 		},
 	}
 
@@ -2053,9 +2052,8 @@ func TestClientNonIdempotentRetry_BodyStream(t *testing.T) {
 					b: []byte("HTTP/1.1 345 OK\r\nContent-Type: foobar\r\n\r\n"),
 				}, nil
 			default:
-				t.Fatalf("unexpected number of dials: %d", dialsCount)
+				return nil, fmt.Errorf("unexpected number of dials: %d", dialsCount)
 			}
-			panic("unreachable")
 		},
 	}
 
@@ -2096,9 +2094,8 @@ func TestClientIdempotentRequest(t *testing.T) {
 					s: "HTTP/1.1 345 OK\r\nContent-Type: foobar\r\nContent-Length: 7\r\n\r\n0123456",
 				}, nil
 			default:
-				t.Fatalf("unexpected number of dials: %d", dialsCount)
+				return nil, fmt.Errorf("unexpected number of dials: %d", dialsCount)
 			}
-			panic("unreachable")
 		},
 	}
 
@@ -2152,9 +2149,8 @@ func TestClientRetryRequestWithCustomDecider(t *testing.T) {
 					s: "HTTP/1.1 345 OK\r\nContent-Type: foobar\r\nContent-Length: 7\r\n\r\n0123456",
 				}, nil
 			default:
-				t.Fatalf("unexpected number of dials: %d", dialsCount)
+				return nil, fmt.Errorf("unexpected number of dials: %d", dialsCount)
 			}
-			panic("unreachable")
 		},
 		RetryIf: func(req *Request) bool {
 			return req.URI().String() == "http://foobar/a/b"
diff --git a/cookie_test.go b/cookie_test.go
index b4b81ac..4a87f15 100644
--- a/cookie_test.go
+++ b/cookie_test.go
@@ -243,6 +243,35 @@ func TestCookieHttpOnly(t *testing.T) {
 	}
 }
 
+func TestCookiePartitioned(t *testing.T) {
+	t.Parallel()
+
+	var c Cookie
+
+	if err := c.Parse("foo=bar; PATH=/; secure; Partitioned"); err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if !c.Partitioned() {
+		t.Fatalf("Partitioned must be set")
+	}
+	s := c.String()
+	if !strings.Contains(s, "; Partitioned") {
+		t.Fatalf("missing Partitioned flag in cookie %q", s)
+	}
+
+	if !c.Secure() {
+		t.Fatalf("secure must be set")
+	}
+	s = c.String()
+	if !strings.Contains(s, "; secure") {
+		t.Fatalf("missing secure flag in cookie %q", s)
+	}
+
+	if string(c.Path()) != "/" {
+		t.Fatalf("path must be set /")
+	}
+}
+
 func TestCookieAcquireReleaseSequential(t *testing.T) {
 	t.Parallel()