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: stop parse the header if encounter a invalid char #1011

Merged
merged 3 commits into from
Jan 3, 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: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ 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: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ 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
1 change: 1 addition & 0 deletions internal/bytesconv/bytesconv_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ const (
QuotedArgShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\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\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\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\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"
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"
)
34 changes: 28 additions & 6 deletions internal/bytesconv/bytesconv_table_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,36 @@ func main() {
return a
}()

validHeaderFieldValueTable := func() [256]byte {
// The implementation here is equal to httpguts.ValidHeaderFieldValue
var a [256]byte
for i := 0; i < 256; i++ {
a[i] = 1
}

for i := 0; i < ' '; i++ {
a[i] = 0
}

// del CTL
a[0x7f] = 0
// tab
a['\t'] = 1

return a
}()

w := new(bytes.Buffer)
w.WriteString(pre)
fmt.Fprintf(w, "const Hex2intTable = %q\n", hex2intTable)
fmt.Fprintf(w, "const ToLowerTable = %q\n", toLowerTable)
fmt.Fprintf(w, "const ToUpperTable = %q\n", toUpperTable)
fmt.Fprintf(w, "const QuotedArgShouldEscapeTable = %q\n", quotedArgShouldEscapeTable)
fmt.Fprintf(w, "const QuotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable)
fmt.Fprintf(w, "const ValidCookieValueTable = %q\n", validCookieValueTable)
fmt.Fprintf(w, "const (\n")
fmt.Fprintf(w, "\tHex2intTable = %q\n", hex2intTable)
fmt.Fprintf(w, "\tToLowerTable = %q\n", toLowerTable)
fmt.Fprintf(w, "\tToUpperTable = %q\n", toUpperTable)
fmt.Fprintf(w, "\tQuotedArgShouldEscapeTable = %q\n", quotedArgShouldEscapeTable)
fmt.Fprintf(w, "\tQuotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable)
fmt.Fprintf(w, "\tValidCookieValueTable = %q\n", validCookieValueTable)
fmt.Fprintf(w, "\tValidHeaderFieldValueTable = %q\n", validHeaderFieldValueTable)
fmt.Fprintf(w, ")\n")

if err := ioutil.WriteFile("bytesconv_table.go", w.Bytes(), 0o660); err != nil {
log.Fatal(err)
Expand Down
20 changes: 20 additions & 0 deletions internal/bytesconv/bytesconv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ 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) {
Expand Down Expand Up @@ -183,3 +184,22 @@ func TestParseHTTPDate(t *testing.T) {
assert.DeepEqual(t, t1, t2)
}
}

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)
}
}
53 changes: 53 additions & 0 deletions internal/bytesconv/bytesconv_timing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2024 CloudWeGo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package bytesconv
welkeyever marked this conversation as resolved.
Show resolved Hide resolved

import (
"testing"

"golang.org/x/net/http/httpguts"
)

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([]byte, 0)
for i := 0; i < 256; i++ {
allBytes = append(allBytes, byte(i))
}

for i := 0; i < b.N; i++ {
for _, s := range allBytes {
_ = func() bool {
return ValidHeaderFieldValueTable[s] != 0
}()
}
}
}
19 changes: 18 additions & 1 deletion pkg/protocol/http1/req/header.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"fmt"
"io"

"github.com/cloudwego/hertz/internal/bytesconv"
"github.com/cloudwego/hertz/internal/bytestr"
errs "github.com/cloudwego/hertz/pkg/common/errors"
"github.com/cloudwego/hertz/pkg/common/utils"
Expand Down Expand Up @@ -165,6 +166,16 @@
return len(buf) - len(bNext), nil
}

// validHeaderFieldValue is equal to httpguts.ValidHeaderFieldValue(shares the same context)
func validHeaderFieldValue(val []byte) bool {
for _, v := range val {
if bytesconv.ValidHeaderFieldValueTable[v] == 0 {
return false

Check warning on line 173 in pkg/protocol/http1/req/header.go

View check run for this annotation

Codecov / codecov/patch

pkg/protocol/http1/req/header.go#L173

Added line #L173 was not covered by tests
}
}
return true
}

func parseHeaders(h *protocol.RequestHeader, buf []byte) (int, error) {
h.InitContentLengthWithValue(-2)

Expand All @@ -178,7 +189,13 @@
// See RFC 7230, Section 3.2.4.
if bytes.IndexByte(s.Key, ' ') != -1 || bytes.IndexByte(s.Key, '\t') != -1 {
err = fmt.Errorf("invalid header key %q", s.Key)
continue
return 0, err

Check warning on line 192 in pkg/protocol/http1/req/header.go

View check run for this annotation

Codecov / codecov/patch

pkg/protocol/http1/req/header.go#L192

Added line #L192 was not covered by tests
}

// Check the invalid chars in header value
if !validHeaderFieldValue(s.Value) {
err = fmt.Errorf("invalid header value %q", s.Value)
return 0, err

Check warning on line 198 in pkg/protocol/http1/req/header.go

View check run for this annotation

Codecov / codecov/patch

pkg/protocol/http1/req/header.go#L197-L198

Added lines #L197 - L198 were not covered by tests
}

switch s.Key[0] | 0x20 {
Expand Down
Loading