mirror of
https://github.com/soypat/lneto.git
synced 2026-09-10 08:39:30 +00:00
add HTTP normalize tests and fix bugs triggered by tests
This commit is contained in:
+12
-27
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user