Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimize: filter invalid char in header #1039

Merged
merged 9 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,13 @@ 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=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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=
Expand Down
2 changes: 2 additions & 0 deletions internal/bytesconv/bytesconv_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
61 changes: 61 additions & 0 deletions internal/bytesconv/bytesconv_table_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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 {
Expand Down
64 changes: 54 additions & 10 deletions internal/bytesconv/bytesconv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ package bytesconv

import (
"net/url"
"strings"
"testing"
"time"

"github.com/cloudwego/hertz/pkg/common/bytebufferpool"
"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) {
Expand Down Expand Up @@ -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)
// }
//}
68 changes: 57 additions & 11 deletions internal/bytesconv/bytesconv_timing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]])
welkeyever marked this conversation as resolved.
Show resolved Hide resolved
}
_ = 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]]
}
}
}
18 changes: 17 additions & 1 deletion pkg/protocol/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -1657,12 +1657,28 @@
}

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

Check warning on line 1663 in pkg/protocol/header.go

View check run for this annotation

Codecov / codecov/patch

pkg/protocol/header.go#L1663

Added line #L1663 was not covered by tests
}
}
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() {
Expand Down
2 changes: 1 addition & 1 deletion pkg/protocol/http1/resp/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
6 changes: 0 additions & 6 deletions pkg/protocol/http1/resp/response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
welkeyever marked this conversation as resolved.
Show resolved Hide resolved
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"))
}