add HTTP normalize tests and fix bugs triggered by tests

This commit is contained in:
soypat
2025-06-28 13:10:36 -03:00
parent 26b3adc777
commit 6884d5e859
2 changed files with 70 additions and 27 deletions
+12 -27
View File
@@ -412,10 +412,12 @@ func (*noCopy) Unlock() {}
// - content-length -> Content-Length // - content-length -> Content-Length
// - cOnTeNt-LenGtH -> Content-Length // - cOnTeNt-LenGtH -> Content-Length
func NormalizeHeaderKey(b []byte) (modified bool) { func NormalizeHeaderKey(b []byte) (modified bool) {
if len(b) == 0 {
return false
}
const asciiCapDiff = 'a' - 'A' const asciiCapDiff = 'a' - 'A'
for i := -1; i < len(b); i++ { for i := -1; i < len(b); i++ {
ch := b[i] nextToUpper := i == -1 || (b[i] == '-' && i < len(b)-1)
nextToUpper := i == -1 || (ch == '-' && i < len(b)-1)
if nextToUpper { if nextToUpper {
i++ i++
isLower := b[i] >= 'a' && b[i] <= 'z' isLower := b[i] >= 'a' && b[i] <= 'z'
@@ -442,7 +444,6 @@ func CopyNormalizedHeaderValue(dst []byte, value []byte) (n int, modified bool)
if len(dst) < len(value) { if len(dst) < len(value) {
panic("httpraw.CopyNormalizedHeaderValue: dst buffer shorter than length") panic("httpraw.CopyNormalizedHeaderValue: dst buffer shorter than length")
} }
lineStart := false
write := 0 write := 0
read := 0 read := 0
for { for {
@@ -453,34 +454,18 @@ func CopyNormalizedHeaderValue(dst []byte, value []byte) (n int, modified bool)
} }
omit := 1 omit := 1
rmStart += read rmStart += read
if rmStart > 0 && value[rmStart] == '\r' {
rmStart--
omit++
}
if rmStart+1 < len(value) && value[rmStart+1] == '\t' { if rmStart+1 < len(value) && value[rmStart+1] == '\t' {
omit++ omit++
} }
n := copy(dst[write:], value[:rmStart]) if rmStart > 0 && value[rmStart-1] == '\r' {
read += omit + n rmStart--
write += n omit++
}
return write, modified
for read := 0; read < len(value); read++ {
c := value[read]
switch {
case c == '\r' || c == '\n':
lineStart = c == '\n'
continue
case lineStart && c == '\t':
c = ' '
modified = true
default:
lineStart = false
} }
dst[write] = c modified = true
write++ n := copy(dst[write:], value[read:rmStart])
dst[write+n] = ' '
read += omit + n
write += n + 1
} }
modified = modified || n != len(value)
return write, modified return write, modified
} }
+58
View File
@@ -144,3 +144,61 @@ func strSameSite(mode http.SameSite) string {
panic("invalid same site") panic("invalid same site")
} }
} }
func TestHeaderNormalizeKey(t *testing.T) {
var tests = []struct {
key string
wantnorm string
}{
{key: "", wantnorm: ""},
{key: "a-a-a", wantnorm: "A-A-A"},
{key: "a-a-a-", wantnorm: "A-A-A-"},
{key: "-", wantnorm: "-"},
{key: "CONTENT", wantnorm: "Content"},
{key: "cONTENT", wantnorm: "Content"},
{key: "Content-Length", wantnorm: "Content-Length"},
{key: "Content-length", wantnorm: "Content-Length"},
{key: "content-length", wantnorm: "Content-Length"},
{key: "conTent-lENgth", wantnorm: "Content-Length"},
{key: "conTent-lENgth-", wantnorm: "Content-Length-"},
}
for _, test := range tests {
gotKey := []byte(test.key)
modified := NormalizeHeaderKey(gotKey)
if string(gotKey) != test.wantnorm {
t.Errorf("mismatch want %q got %q", test.wantnorm, gotKey)
}
wantMod := string(gotKey) != test.key
if wantMod != modified {
t.Errorf("mismatch want mod=%v, got mod=%v", wantMod, modified)
}
}
}
func TestCopyNormalizedHeaderValue(t *testing.T) {
var tests = []struct {
value string
wantnorm string
}{
{value: "abc\r\n\tdef\r\n\tghi", wantnorm: "abc def ghi"},
{value: "abc\r\n\tabc", wantnorm: "abc abc"},
{value: "abc\n\tdef\n\tghi", wantnorm: "abc def ghi"},
{value: "abc\n\tdef\n\tghi\n\t", wantnorm: "abc def ghi "},
{value: "abc\n\tdef\n\tghi\r\n\t", wantnorm: "abc def ghi "},
{value: "", wantnorm: ""},
{value: "abc", wantnorm: "abc"},
}
dst := make([]byte, 256)
for _, test := range tests {
value := []byte(test.value)
n, modified := CopyNormalizedHeaderValue(dst[:len(value)], value)
got := dst[:n]
if string(got) != test.wantnorm {
t.Errorf("mismatch want %q got %q", test.wantnorm, got)
}
wantMod := string(got) != test.value
if wantMod != modified {
t.Errorf("mismatch want mod=%v, got mod=%v", wantMod, modified)
}
}
}