]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: correct and faster hasToken
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 28 May 2012 17:55:39 +0000 (10:55 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 28 May 2012 17:55:39 +0000 (10:55 -0700)
Fixes #3535

R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/6245060

src/pkg/net/http/header.go
src/pkg/net/http/header_test.go
src/pkg/net/http/request.go

index b107c312da782667fdfe8d627e88a3f44965fc34..7987036222c9623339a9593976c8cc42b1a14d2d 100644 (file)
@@ -76,3 +76,38 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error {
 // the rest are converted to lowercase.  For example, the
 // canonical key for "accept-encoding" is "Accept-Encoding".
 func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) }
+
+// hasToken returns whether token appears with v, ASCII
+// case-insensitive, with space or comma boundaries.
+// token must be all lowercase.
+// v may contain mixed cased.
+func hasToken(v, token string) bool {
+       if len(token) > len(v) || token == "" {
+               return false
+       }
+       if v == token {
+               return true
+       }
+       for sp := 0; sp <= len(v)-len(token); sp++ {
+               // Check that first character is good.
+               if b := v[sp]; b != token[0] && b|0x20 != token[0] {
+                       continue
+               }
+               // Check that start pos is on a valid token boundary.
+               if sp > 0 && !isTokenBoundary(v[sp-1]) {
+                       continue
+               }
+               // Check that end pos is on a valid token boundary.
+               if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) {
+                       continue
+               }
+               if strings.EqualFold(v[sp:sp+len(token)], token) {
+                       return true
+               }
+       }
+       return false
+}
+
+func isTokenBoundary(b byte) bool {
+       return b == ' ' || b == ',' || b == '\t'
+}
index ccdee8a97bdef352fc1f8b18b0be3bc7256c60ed..fee5756b77d5f0933ebfc9d61372085f557d3777 100644 (file)
@@ -79,3 +79,46 @@ func TestHeaderWrite(t *testing.T) {
                buf.Reset()
        }
 }
+
+type hasTokenTest struct {
+       header string
+       token  string
+       want   bool
+}
+
+var hasTokenTests = []hasTokenTest{
+       {"", "", false},
+       {"", "foo", false},
+       {"foo", "foo", true},
+       {"foo ", "foo", true},
+       {" foo", "foo", true},
+       {" foo ", "foo", true},
+       {"foo,bar", "foo", true},
+       {"bar,foo", "foo", true},
+       {"bar, foo", "foo", true},
+       {"bar,foo, baz", "foo", true},
+       {"bar, foo,baz", "foo", true},
+       {"bar,foo, baz", "foo", true},
+       {"bar, foo, baz", "foo", true},
+       {"FOO", "foo", true},
+       {"FOO ", "foo", true},
+       {" FOO", "foo", true},
+       {" FOO ", "foo", true},
+       {"FOO,BAR", "foo", true},
+       {"BAR,FOO", "foo", true},
+       {"BAR, FOO", "foo", true},
+       {"BAR,FOO, baz", "foo", true},
+       {"BAR, FOO,BAZ", "foo", true},
+       {"BAR,FOO, BAZ", "foo", true},
+       {"BAR, FOO, BAZ", "foo", true},
+       {"foobar", "foo", false},
+       {"barfoo ", "foo", false},
+}
+
+func TestHasToken(t *testing.T) {
+       for _, tt := range hasTokenTests {
+               if hasToken(tt.header, tt.token) != tt.want {
+                       t.Errorf("hasToken(%q, %q) = %v; want %v", tt.header, tt.token, !tt.want, tt.want)
+               }
+       }
+}
index 219db483b48656be259e65a2341cf1e94020a39f..784dd6b3224e33a242859f3fbd49d0ba3aecf080 100644 (file)
@@ -745,11 +745,3 @@ func (r *Request) wantsHttp10KeepAlive() bool {
 func (r *Request) wantsClose() bool {
        return hasToken(r.Header.Get("Connection"), "close")
 }
-
-func hasToken(s, token string) bool {
-       if s == "" {
-               return false
-       }
-       // TODO This is a poor implementation of the RFC. See http://golang.org/issue/3535
-       return strings.Contains(strings.ToLower(s), token)
-}