Commit 6e3d87f3 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

net/textproto: add benchmark, cleanup, update comment

The cleanup also makes it ~5% faster, but that's
not the point of this CL.

Optimizations can come in future CLs.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6286043
parent 6b31508e
...@@ -452,16 +452,18 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { ...@@ -452,16 +452,18 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
return m, err return m, err
} }
// Key ends at first colon; must not have spaces. // Key ends at first colon; should not have spaces but
// they appear in the wild, violating specs, so we
// remove them if present.
i := bytes.IndexByte(kv, ':') i := bytes.IndexByte(kv, ':')
if i < 0 { if i < 0 {
return m, ProtocolError("malformed MIME header line: " + string(kv)) return m, ProtocolError("malformed MIME header line: " + string(kv))
} }
key := string(kv[0:i]) endKey := i
if strings.Index(key, " ") >= 0 { for endKey > 0 && kv[endKey-1] == ' ' {
key = strings.TrimRight(key, " ") endKey--
} }
key = CanonicalMIMEHeaderKey(key) key := canonicalMIMEHeaderKey(kv[:endKey])
// Skip initial spaces in value. // Skip initial spaces in value.
i++ // skip colon i++ // skip colon
...@@ -486,25 +488,28 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { ...@@ -486,25 +488,28 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
// canonical key for "accept-encoding" is "Accept-Encoding". // canonical key for "accept-encoding" is "Accept-Encoding".
func CanonicalMIMEHeaderKey(s string) string { func CanonicalMIMEHeaderKey(s string) string {
// Quick check for canonical encoding. // Quick check for canonical encoding.
needUpper := true upper := true
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
c := s[i] c := s[i]
if needUpper && 'a' <= c && c <= 'z' { if upper && 'a' <= c && c <= 'z' {
goto MustRewrite return canonicalMIMEHeaderKey([]byte(s))
} }
if !needUpper && 'A' <= c && c <= 'Z' { if !upper && 'A' <= c && c <= 'Z' {
goto MustRewrite return canonicalMIMEHeaderKey([]byte(s))
} }
needUpper = c == '-' upper = c == '-'
} }
return s return s
}
MustRewrite: // canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is
// allowed to mutate the provided byte slice before returning the
// string.
func canonicalMIMEHeaderKey(a []byte) string {
// Canonicalize: first letter upper case // Canonicalize: first letter upper case
// and upper case after each dash. // and upper case after each dash.
// (Host, User-Agent, If-Modified-Since). // (Host, User-Agent, If-Modified-Since).
// MIME headers are ASCII only, so no Unicode issues. // MIME headers are ASCII only, so no Unicode issues.
a := []byte(s)
upper := true upper := true
for i, v := range a { for i, v := range a {
if v == ' ' { if v == ' ' {
......
...@@ -6,6 +6,7 @@ package textproto ...@@ -6,6 +6,7 @@ package textproto
import ( import (
"bufio" "bufio"
"bytes"
"io" "io"
"reflect" "reflect"
"strings" "strings"
...@@ -239,3 +240,19 @@ func TestRFC959Lines(t *testing.T) { ...@@ -239,3 +240,19 @@ func TestRFC959Lines(t *testing.T) {
} }
} }
} }
func BenchmarkReadMIMEHeader(b *testing.B) {
var buf bytes.Buffer
br := bufio.NewReader(&buf)
r := NewReader(br)
for i := 0; i < b.N; i++ {
buf.WriteString("User-Agent: not mozilla\r\nContent-Length: 23452\r\nContent-Type: text/html; charset-utf8\r\nFoo-Bar: foobar\r\nfoo-bar: some more string\r\n\r\n")
h, err := r.ReadMIMEHeader()
if err != nil {
b.Fatal(err)
}
if len(h) != 4 {
b.Fatalf("want 4")
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment