Browse Source

Add cache of common HTTP headers mapped between lower and canonical case.

For garbage reduction.
Brad Fitzpatrick 11 years ago
parent
commit
6520e26846
2 changed files with 95 additions and 11 deletions
  1. 80 0
      headermap.go
  2. 15 11
      server.go

+ 80 - 0
headermap.go

@@ -0,0 +1,80 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+	"net/http"
+	"strings"
+)
+
+var (
+	commonLowerHeader = map[string]string{} // Go-Canonical-Case -> lower-case
+	commonCanonHeader = map[string]string{} // lower-case -> Go-Canonical-Case
+)
+
+func init() {
+	for _, v := range []string{
+		"accept",
+		"accept-charset",
+		"accept-encoding",
+		"accept-language",
+		"accept-ranges",
+		"age",
+		"access-control-allow-origin",
+		"allow",
+		"authorization",
+		"cache-control",
+		"content-disposition",
+		"content-encoding",
+		"content-language",
+		"content-length",
+		"content-location",
+		"content-range",
+		"content-type",
+		"cookie",
+		"date",
+		"etag",
+		"expect",
+		"expires",
+		"from",
+		"host",
+		"if-match",
+		"if-modified-since",
+		"if-none-match",
+		"if-unmodified-since",
+		"last-modified",
+		"link",
+		"location",
+		"max-forwards",
+		"proxy-authenticate",
+		"proxy-authorization",
+		"range",
+		"referer",
+		"refresh",
+		"retry-after",
+		"server",
+		"set-cookie",
+		"strict-transport-security",
+		"transfer-encoding",
+		"user-agent",
+		"vary",
+		"via",
+		"www-authenticate",
+	} {
+		chk := http.CanonicalHeaderKey(v)
+		commonLowerHeader[chk] = v
+		commonCanonHeader[v] = chk
+	}
+}
+
+func lowerHeader(v string) string {
+	if s, ok := commonLowerHeader[v]; ok {
+		return s
+	}
+	return strings.ToLower(v)
+}

+ 15 - 11
server.go

@@ -77,7 +77,6 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
 		handler:           h,
 		framer:            NewFramer(c, c), // TODO: write to a (custom?) buffered writer that can alternate when it's in buffered mode.
 		streams:           make(map[uint32]*stream),
-		canonHeader:       make(map[string]string),
 		readFrameCh:       make(chan frameAndProcessed),
 		readFrameErrCh:    make(chan error, 1), // must be buffered for 1
 		wantWriteFrameCh:  make(chan frameWriteMsg, 8),
@@ -258,12 +257,19 @@ func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
 
 func (sc *serverConn) canonicalHeader(v string) string {
 	sc.serveG.check()
-	// TODO: use a sync.Pool instead of putting the cache on *serverConn?
-	cv, ok := sc.canonHeader[v]
-	if !ok {
-		cv = http.CanonicalHeaderKey(v)
-		sc.canonHeader[v] = cv
+	cv, ok := commonCanonHeader[v]
+	if ok {
+		return cv
+	}
+	cv, ok = sc.canonHeader[v]
+	if ok {
+		return cv
+	}
+	if sc.canonHeader == nil {
+		sc.canonHeader = make(map[string]string)
 	}
+	cv = http.CanonicalHeaderKey(v)
+	sc.canonHeader[v] = cv
 	return cv
 }
 
@@ -845,15 +851,13 @@ func (sc *serverConn) writeHeadersFrame(v interface{}) error {
 	sc.headerWriteBuf.Reset()
 	sc.hpackEncoder.WriteField(hpack.HeaderField{Name: ":status", Value: httpCodeString(req.httpResCode)})
 	for k, vv := range req.h {
+		k = lowerHeader(k)
 		for _, v := range vv {
 			// TODO: more of "8.1.2.2 Connection-Specific Header Fields"
-			if k == "Transfer-Encoding" && v != "trailers" {
+			if k == "transfer-encoding" && v != "trailers" {
 				continue
 			}
-			// TODO: for gargage, cache lowercase copies of headers at
-			// least for common ones and/or popular recent ones for
-			// this serverConn. LRU?
-			sc.hpackEncoder.WriteField(hpack.HeaderField{Name: strings.ToLower(k), Value: v})
+			sc.hpackEncoder.WriteField(hpack.HeaderField{Name: k, Value: v})
 		}
 	}
 	if req.contentType != "" {