Repository: cloudwego/hertz
Pull request #1043 (2024-01-10 03:11:26)

chore(hz): release v080

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cloudwego/hertz/pull/1043?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`1f93aaa`)](https://app.codecov.io/gh/cloudwego/hertz/commit/1f93aaafd088bd6deddebba2602d68c6944b7291?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) 82.47% compared to head [(`48538bb`)](https://app.codecov.io/gh/cloudwego/hertz/pull/1043?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) 82.57%.


<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop    #1043      +/-   ##
===========================================
+ Coverage    82.47%   82.57%   +0.09%     
===========================================
  Files           98       98              
  Lines        10002    10002              
===========================================
+ Hits          8249     8259      +10     
+ Misses        1253     1246       -7     
+ Partials       500      497       -3     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cloudwego/hertz/pull/1043?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).


Patch:
diff --git a/cmd/hz/meta/const.go b/cmd/hz/meta/const.go
index 3f4d916..dfeafd2 100644
--- a/cmd/hz/meta/const.go
+++ b/cmd/hz/meta/const.go
@@ -19,7 +19,7 @@ package meta
 import "runtime"
 
 // Version hz version
-const Version = "v0.7.1"
+const Version = "v0.8.0"
 
 const DefaultServiceName = "hertz_service"
 
diff --git a/go.mod b/go.mod
index 509c881..3619b83 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,6 @@ require (
 	github.com/cloudwego/netpoll v0.5.0
 	github.com/fsnotify/fsnotify v1.5.4
 	github.com/tidwall/gjson v1.14.4
-	golang.org/x/net v0.0.0-20190311183353-d8887717615a
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 	golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
 	google.golang.org/protobuf v1.27.1
diff --git a/go.sum b/go.sum
index 26da89b..3cf3c2f 100644
--- a/go.sum
+++ b/go.sum
@@ -64,7 +64,6 @@ golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5P
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -72,7 +71,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
diff --git a/internal/bytesconv/bytesconv_table.go b/internal/bytesconv/bytesconv_table.go
index c501d92..5954957 100644
--- a/internal/bytesconv/bytesconv_table.go
+++ b/internal/bytesconv/bytesconv_table.go
@@ -52,4 +52,6 @@ const (
 	QuotedPathShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
 	ValidCookieValueTable       = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
 	ValidHeaderFieldValueTable  = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+	NewlineToSpaceTable         = "\x00\x01\x02\x03\x04\x05\x06\a\b\t \v\f \x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
+	ValidHeaderFieldNameTable   = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x01\x01\x01\x00\x00\x01\x01\x00\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
 )
diff --git a/internal/bytesconv/bytesconv_table_gen.go b/internal/bytesconv/bytesconv_table_gen.go
index 2e2722d..eff361b 100644
--- a/internal/bytesconv/bytesconv_table_gen.go
+++ b/internal/bytesconv/bytesconv_table_gen.go
@@ -178,6 +178,65 @@ func main() {
 		return a
 	}()
 
+	newlineToSpaceTable := func() [256]byte {
+		var a [256]byte
+		for i := 0; i < 256; i++ {
+			c := byte(i)
+			if c == '\r' || c == '\n' {
+				c = ' '
+			}
+			a[i] = c
+		}
+		return a
+	}()
+
+	validHeaderFieldNameTable := func() [256]byte {
+		// The implementation here is equal to httpguts ValidHeaderFieldName(string)
+		// see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2
+		//
+		//  RFC 7230 says:
+		//   header-field   = field-name ":" OWS field-value OWS
+		//   field-name     = token
+		//   token          = 1*tchar
+		//   tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
+		//           "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
+		var a [256]byte
+		for i := 0; i < 256; i++ {
+			a[i] = 0
+		}
+
+		a['!'] = 1
+		a['#'] = 1
+		a['$'] = 1
+		a['%'] = 1
+		a['&'] = 1
+		a['\''] = 1
+		a['*'] = 1
+		a['+'] = 1
+		a['-'] = 1
+		a['.'] = 1
+		a['^'] = 1
+		a['_'] = 1
+		a['`'] = 1
+		a['|'] = 1
+		a['~'] = 1
+
+		// ALPHA
+		for i := int('a'); i <= int('z'); i++ {
+			a[i] = 1
+		}
+		for i := int('A'); i <= int('Z'); i++ {
+			a[i] = 1
+		}
+
+		// DIGIT
+		for i := int('0'); i <= int('9'); i++ {
+			a[i] = 1
+		}
+
+		return a
+	}()
+
 	w := new(bytes.Buffer)
 	w.WriteString(pre)
 	fmt.Fprintf(w, "const (\n")
@@ -188,6 +247,8 @@ func main() {
 	fmt.Fprintf(w, "\tQuotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable)
 	fmt.Fprintf(w, "\tValidCookieValueTable = %q\n", validCookieValueTable)
 	fmt.Fprintf(w, "\tValidHeaderFieldValueTable = %q\n", validHeaderFieldValueTable)
+	fmt.Fprintf(w, "\tNewlineToSpaceTable = %q\n", newlineToSpaceTable)
+	fmt.Fprintf(w, "\tValidHeaderFieldNameTable = %q\n", validHeaderFieldNameTable)
 	fmt.Fprintf(w, ")\n")
 
 	if err := ioutil.WriteFile("bytesconv_table.go", w.Bytes(), 0o660); err != nil {
diff --git a/pkg/common/adaptor/response.go b/pkg/common/adaptor/response.go
index e981007..4dbc0fa 100644
--- a/pkg/common/adaptor/response.go
+++ b/pkg/common/adaptor/response.go
@@ -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
 				}
diff --git a/pkg/protocol/cookie.go b/pkg/protocol/cookie.go
index 7ca22c6..4477a89 100644
--- a/pkg/protocol/cookie.go
+++ b/pkg/protocol/cookie.go
@@ -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
 )
 
@@ -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
@@ -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
 }
 
@@ -337,6 +348,7 @@ func (c *Cookie) Reset() {
 	c.httpOnly = false
 	c.secure = false
 	c.sameSite = CookieSameSiteDisabled
+	c.partitioned = false
 }
 
 // Value returns cookie value.
@@ -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
 }
 
@@ -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) {
@@ -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())
diff --git a/pkg/protocol/header.go b/pkg/protocol/header.go
index 7edf19b..278b137 100644
--- a/pkg/protocol/header.go
+++ b/pkg/protocol/header.go
@@ -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)
@@ -1657,12 +1656,28 @@ func (h *ResponseHeader) GetAll(key string) []string {
 }
 
 func appendHeaderLine(dst, key, value []byte) []byte {
+	for _, k := range key {
+		// if header field contains invalid key, just skip it.
+		if bytesconv.ValidHeaderFieldNameTable[k] == 0 {
+			return dst
+		}
+	}
 	dst = append(dst, key...)
 	dst = append(dst, bytestr.StrColonSpace...)
-	dst = append(dst, value...)
+	dst = append(dst, newlineToSpace(value)...)
 	return append(dst, bytestr.StrCRLF...)
 }
 
+// newlineToSpace will return a copy of the original byte slice.
+func newlineToSpace(val []byte) []byte {
+	filteredVal := make([]byte, len(val))
+	copy(filteredVal, val)
+	for i := 0; i < len(filteredVal); i++ {
+		filteredVal[i] = bytesconv.NewlineToSpaceTable[filteredVal[i]]
+	}
+	return filteredVal
+}
+
 func UpdateServerDate() {
 	refreshServerDate()
 	go func() {
diff --git a/pkg/protocol/http1/resp/header.go b/pkg/protocol/http1/resp/header.go
index 18a0f8a..ff12cb2 100644
--- a/pkg/protocol/http1/resp/header.go
+++ b/pkg/protocol/http1/resp/header.go
@@ -85,7 +85,7 @@ func ReadHeader(h *protocol.ResponseHeader, r network.Reader) error {
 	}
 }
 
-// Write writes response header to w.
+// WriteHeader writes response header to w.
 func WriteHeader(h *protocol.ResponseHeader, w network.Writer) error {
 	header := h.Header()
 	h.SetHeaderLength(len(header))


Test patch:
diff --git a/internal/bytesconv/bytesconv_test.go b/internal/bytesconv/bytesconv_test.go
index 4218a82..316c917 100644
--- a/internal/bytesconv/bytesconv_test.go
+++ b/internal/bytesconv/bytesconv_test.go
@@ -18,6 +18,7 @@ package bytesconv
 
 import (
 	"net/url"
+	"strings"
 	"testing"
 	"time"
 
@@ -25,7 +26,6 @@ import (
 	"github.com/cloudwego/hertz/pkg/common/test/assert"
 	"github.com/cloudwego/hertz/pkg/common/test/mock"
 	"github.com/cloudwego/hertz/pkg/network"
-	"golang.org/x/net/http/httpguts"
 )
 
 func TestAppendDate(t *testing.T) {
@@ -185,21 +185,65 @@ func TestParseHTTPDate(t *testing.T) {
 	}
 }
 
-func TestValidHeaderFieldValueTable(t *testing.T) {
+// For test only, but it will import golang.org/x/net/http.
+// So comment out all this code. Keep this for the full context.
+//func TestValidHeaderFieldValueTable(t *testing.T) {
+//	t.Parallel()
+//
+//	// Test all characters
+//	allBytes := make([]byte, 0)
+//	for i := 0; i < 256; i++ {
+//		allBytes = append(allBytes, byte(i))
+//	}
+//	for _, s := range allBytes {
+//		ss := []byte{s}
+//		expectedS := httpguts.ValidHeaderFieldValue(string(ss))
+//		res := func() bool {
+//			return ValidHeaderFieldValueTable[s] != 0
+//		}()
+//
+//		assert.DeepEqual(t, expectedS, res)
+//	}
+//}
+
+func TestNewlineToSpaceTable(t *testing.T) {
 	t.Parallel()
-
 	// Test all characters
 	allBytes := make([]byte, 0)
 	for i := 0; i < 256; i++ {
 		allBytes = append(allBytes, byte(i))
 	}
-	for _, s := range allBytes {
-		ss := []byte{s}
-		expectedS := httpguts.ValidHeaderFieldValue(string(ss))
-		res := func() bool {
-			return ValidHeaderFieldValueTable[s] != 0
-		}()
 
-		assert.DeepEqual(t, expectedS, res)
+	headerNewlineToSpace := strings.NewReplacer("\n", " ", "\r", " ")
+
+	expectedS := headerNewlineToSpace.Replace(string(allBytes))
+
+	res := make([]byte, len(allBytes))
+	copy(res, allBytes)
+	for i := 0; i < len(res); i++ {
+		res[i] = NewlineToSpaceTable[res[i]]
 	}
+
+	assert.DeepEqual(t, expectedS, string(res))
 }
+
+// For test only, but it will import golang.org/x/net/http.
+// So comment out all this code. Keep this for the full context.
+//func TestValidHeaderFieldNameTable(t *testing.T) {
+//	t.Parallel()
+//
+//	// Test all characters
+//	allBytes := make([]byte, 0)
+//	for i := 0; i < 256; i++ {
+//		allBytes = append(allBytes, byte(i))
+//	}
+//	for _, s := range allBytes {
+//		ss := []byte{s}
+//		expectedS := httpguts.ValidHeaderFieldName(string(ss))
+//		res := func() bool {
+//			return ValidHeaderFieldNameTable[s] != 0
+//		}()
+//
+//		assert.DeepEqual(t, expectedS, res)
+//	}
+//}
diff --git a/internal/bytesconv/bytesconv_timing_test.go b/internal/bytesconv/bytesconv_timing_test.go
index 6fdd28b..3f913a0 100644
--- a/internal/bytesconv/bytesconv_timing_test.go
+++ b/internal/bytesconv/bytesconv_timing_test.go
@@ -17,37 +17,83 @@
 package bytesconv
 
 import (
+	"strings"
 	"testing"
-
-	"golang.org/x/net/http/httpguts"
 )
 
-func BenchmarkValidHeaderFiledValueTable(b *testing.B) {
+// For test only, but it will import golang.org/x/net/http.
+// So comment out all this code. Keep this for the full context.
+//func BenchmarkValidHeaderFiledValueTable(b *testing.B) {
+//	// Test all characters
+//	allBytes := make([]string, 0)
+//	for i := 0; i < 256; i++ {
+//		allBytes = append(allBytes, string([]byte{byte(i)}))
+//	}
+//
+//	for i := 0; i < b.N; i++ {
+//		for _, s := range allBytes {
+//			_ = httpguts.ValidHeaderFieldValue(s)
+//		}
+//	}
+//}
+
+func BenchmarkValidHeaderFiledValueTableHertz(b *testing.B) {
 	// Test all characters
-	allBytes := make([]string, 0)
+	allBytes := make([]byte, 0)
 	for i := 0; i < 256; i++ {
-		allBytes = append(allBytes, string([]byte{byte(i)}))
+		allBytes = append(allBytes, byte(i))
 	}
 
 	for i := 0; i < b.N; i++ {
 		for _, s := range allBytes {
-			_ = httpguts.ValidHeaderFieldValue(s)
+			_ = func() bool {
+				return ValidHeaderFieldValueTable[s] != 0
+			}()
 		}
 	}
 }
 
-func BenchmarkValidHeaderFiledValueTableHertz(b *testing.B) {
+func BenchmarkNewlineToSpace(b *testing.B) {
 	// Test all characters
 	allBytes := make([]byte, 0)
 	for i := 0; i < 256; i++ {
 		allBytes = append(allBytes, byte(i))
 	}
+	headerNewlineToSpace := strings.NewReplacer("\n", " ", "\r", " ")
 
 	for i := 0; i < b.N; i++ {
-		for _, s := range allBytes {
-			_ = func() bool {
-				return ValidHeaderFieldValueTable[s] != 0
-			}()
+		_ = headerNewlineToSpace.Replace(string(allBytes))
+	}
+}
+
+func BenchmarkNewlineToSpaceHertz01(b *testing.B) {
+	// Test all characters
+	allBytes := make([]byte, 0)
+	for i := 0; i < 256; i++ {
+		allBytes = append(allBytes, byte(i))
+	}
+
+	for i := 0; i < b.N; i++ {
+		filteredVal := make([]byte, 0, len(allBytes))
+		for i := 0; i < len(allBytes); i++ {
+			filteredVal = append(filteredVal, NewlineToSpaceTable[allBytes[i]])
+		}
+		_ = filteredVal
+	}
+}
+
+func BenchmarkNewlineToSpaceHertz02(b *testing.B) {
+	// Test all characters
+	allBytes := make([]byte, 0)
+	for i := 0; i < 256; i++ {
+		allBytes = append(allBytes, byte(i))
+	}
+
+	for i := 0; i < b.N; i++ {
+		filteredVal := make([]byte, len(allBytes))
+		copy(filteredVal, allBytes)
+		for ii := 0; ii < len(allBytes); ii++ {
+			filteredVal[ii] = NewlineToSpaceTable[filteredVal[ii]]
 		}
 	}
 }
diff --git a/internal/bytestr/bytes.go b/internal/bytestr/bytes.go
index a93bd2d..bbf3dcb 100644
--- a/internal/bytestr/bytes.go
+++ b/internal/bytestr/bytes.go
@@ -97,6 +97,7 @@ var (
 	StrCookieSameSiteLax    = []byte("Lax")
 	StrCookieSameSiteStrict = []byte("Strict")
 	StrCookieSameSiteNone   = []byte("None")
+	StrCookiePartitioned    = []byte("Partitioned")
 
 	StrClose               = []byte("close")
 	StrGzip                = []byte("gzip")
diff --git a/pkg/protocol/cookie_test.go b/pkg/protocol/cookie_test.go
index f7637e5..d799441 100644
--- a/pkg/protocol/cookie_test.go
+++ b/pkg/protocol/cookie_test.go
@@ -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) {
@@ -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()
 
diff --git a/pkg/protocol/http1/resp/response_test.go b/pkg/protocol/http1/resp/response_test.go
index 0ff010f..cff0a57 100644
--- a/pkg/protocol/http1/resp/response_test.go
+++ b/pkg/protocol/http1/resp/response_test.go
@@ -820,9 +820,3 @@ func TestResponseReadBodyStreamBadTrailer(t *testing.T) {
 	testResponseReadBodyStreamBadTrailer(t, resp, "HTTP/1.1 300 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: bar\r\n\r\n5\r\n56789\r\n0\r\ncontent-type: bar\r\n\r\n")
 	testResponseReadBodyStreamBadTrailer(t, resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nqwer\r\n2\r\nty\r\n0\r\nproxy-connection: bar2\r\n\r\n")
 }
-
-func TestResponseString(t *testing.T) {
-	resp := protocol.Response{}
-	resp.Header.Set("Location", "foo\r\nSet-Cookie: SESSIONID=MaliciousValue\r\n")
-	assert.True(t, strings.Contains(GetHTTP1Response(&resp).String(), "Location: foo\r\nSet-Cookie: SESSIONID=MaliciousValue\r\n"))
-}