...

Source file src/pkg/net/http/header.go

     1	// Copyright 2010 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package http
     6	
     7	import (
     8		"io"
     9		"net/http/httptrace"
    10		"net/textproto"
    11		"sort"
    12		"strings"
    13		"sync"
    14		"time"
    15	)
    16	
    17	// A Header represents the key-value pairs in an HTTP header.
    18	//
    19	// The keys should be in canonical form, as returned by
    20	// CanonicalHeaderKey.
    21	type Header map[string][]string
    22	
    23	// Add adds the key, value pair to the header.
    24	// It appends to any existing values associated with key.
    25	// The key is case insensitive; it is canonicalized by
    26	// CanonicalHeaderKey.
    27	func (h Header) Add(key, value string) {
    28		textproto.MIMEHeader(h).Add(key, value)
    29	}
    30	
    31	// Set sets the header entries associated with key to the
    32	// single element value. It replaces any existing values
    33	// associated with key. The key is case insensitive; it is
    34	// canonicalized by textproto.CanonicalMIMEHeaderKey.
    35	// To use non-canonical keys, assign to the map directly.
    36	func (h Header) Set(key, value string) {
    37		textproto.MIMEHeader(h).Set(key, value)
    38	}
    39	
    40	// Get gets the first value associated with the given key. If
    41	// there are no values associated with the key, Get returns "".
    42	// It is case insensitive; textproto.CanonicalMIMEHeaderKey is
    43	// used to canonicalize the provided key. To access multiple
    44	// values of a key, or to use non-canonical keys, access the
    45	// map directly.
    46	func (h Header) Get(key string) string {
    47		return textproto.MIMEHeader(h).Get(key)
    48	}
    49	
    50	// get is like Get, but key must already be in CanonicalHeaderKey form.
    51	func (h Header) get(key string) string {
    52		if v := h[key]; len(v) > 0 {
    53			return v[0]
    54		}
    55		return ""
    56	}
    57	
    58	// has reports whether h has the provided key defined, even if it's
    59	// set to 0-length slice.
    60	func (h Header) has(key string) bool {
    61		_, ok := h[key]
    62		return ok
    63	}
    64	
    65	// Del deletes the values associated with key.
    66	// The key is case insensitive; it is canonicalized by
    67	// CanonicalHeaderKey.
    68	func (h Header) Del(key string) {
    69		textproto.MIMEHeader(h).Del(key)
    70	}
    71	
    72	// Write writes a header in wire format.
    73	func (h Header) Write(w io.Writer) error {
    74		return h.write(w, nil)
    75	}
    76	
    77	func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error {
    78		return h.writeSubset(w, nil, trace)
    79	}
    80	
    81	// Clone returns a copy of h or nil if h is nil.
    82	func (h Header) Clone() Header {
    83		if h == nil {
    84			return nil
    85		}
    86	
    87		// Find total number of values.
    88		nv := 0
    89		for _, vv := range h {
    90			nv += len(vv)
    91		}
    92		sv := make([]string, nv) // shared backing array for headers' values
    93		h2 := make(Header, len(h))
    94		for k, vv := range h {
    95			n := copy(sv, vv)
    96			h2[k] = sv[:n:n]
    97			sv = sv[n:]
    98		}
    99		return h2
   100	}
   101	
   102	var timeFormats = []string{
   103		TimeFormat,
   104		time.RFC850,
   105		time.ANSIC,
   106	}
   107	
   108	// ParseTime parses a time header (such as the Date: header),
   109	// trying each of the three formats allowed by HTTP/1.1:
   110	// TimeFormat, time.RFC850, and time.ANSIC.
   111	func ParseTime(text string) (t time.Time, err error) {
   112		for _, layout := range timeFormats {
   113			t, err = time.Parse(layout, text)
   114			if err == nil {
   115				return
   116			}
   117		}
   118		return
   119	}
   120	
   121	var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ")
   122	
   123	// stringWriter implements WriteString on a Writer.
   124	type stringWriter struct {
   125		w io.Writer
   126	}
   127	
   128	func (w stringWriter) WriteString(s string) (n int, err error) {
   129		return w.w.Write([]byte(s))
   130	}
   131	
   132	type keyValues struct {
   133		key    string
   134		values []string
   135	}
   136	
   137	// A headerSorter implements sort.Interface by sorting a []keyValues
   138	// by key. It's used as a pointer, so it can fit in a sort.Interface
   139	// interface value without allocation.
   140	type headerSorter struct {
   141		kvs []keyValues
   142	}
   143	
   144	func (s *headerSorter) Len() int           { return len(s.kvs) }
   145	func (s *headerSorter) Swap(i, j int)      { s.kvs[i], s.kvs[j] = s.kvs[j], s.kvs[i] }
   146	func (s *headerSorter) Less(i, j int) bool { return s.kvs[i].key < s.kvs[j].key }
   147	
   148	var headerSorterPool = sync.Pool{
   149		New: func() interface{} { return new(headerSorter) },
   150	}
   151	
   152	// sortedKeyValues returns h's keys sorted in the returned kvs
   153	// slice. The headerSorter used to sort is also returned, for possible
   154	// return to headerSorterCache.
   155	func (h Header) sortedKeyValues(exclude map[string]bool) (kvs []keyValues, hs *headerSorter) {
   156		hs = headerSorterPool.Get().(*headerSorter)
   157		if cap(hs.kvs) < len(h) {
   158			hs.kvs = make([]keyValues, 0, len(h))
   159		}
   160		kvs = hs.kvs[:0]
   161		for k, vv := range h {
   162			if !exclude[k] {
   163				kvs = append(kvs, keyValues{k, vv})
   164			}
   165		}
   166		hs.kvs = kvs
   167		sort.Sort(hs)
   168		return kvs, hs
   169	}
   170	
   171	// WriteSubset writes a header in wire format.
   172	// If exclude is not nil, keys where exclude[key] == true are not written.
   173	func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error {
   174		return h.writeSubset(w, exclude, nil)
   175	}
   176	
   177	func (h Header) writeSubset(w io.Writer, exclude map[string]bool, trace *httptrace.ClientTrace) error {
   178		ws, ok := w.(io.StringWriter)
   179		if !ok {
   180			ws = stringWriter{w}
   181		}
   182		kvs, sorter := h.sortedKeyValues(exclude)
   183		var formattedVals []string
   184		for _, kv := range kvs {
   185			for _, v := range kv.values {
   186				v = headerNewlineToSpace.Replace(v)
   187				v = textproto.TrimString(v)
   188				for _, s := range []string{kv.key, ": ", v, "\r\n"} {
   189					if _, err := ws.WriteString(s); err != nil {
   190						headerSorterPool.Put(sorter)
   191						return err
   192					}
   193				}
   194				if trace != nil && trace.WroteHeaderField != nil {
   195					formattedVals = append(formattedVals, v)
   196				}
   197			}
   198			if trace != nil && trace.WroteHeaderField != nil {
   199				trace.WroteHeaderField(kv.key, formattedVals)
   200				formattedVals = nil
   201			}
   202		}
   203		headerSorterPool.Put(sorter)
   204		return nil
   205	}
   206	
   207	// CanonicalHeaderKey returns the canonical format of the
   208	// header key s. The canonicalization converts the first
   209	// letter and any letter following a hyphen to upper case;
   210	// the rest are converted to lowercase. For example, the
   211	// canonical key for "accept-encoding" is "Accept-Encoding".
   212	// If s contains a space or invalid header field bytes, it is
   213	// returned without modifications.
   214	func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) }
   215	
   216	// hasToken reports whether token appears with v, ASCII
   217	// case-insensitive, with space or comma boundaries.
   218	// token must be all lowercase.
   219	// v may contain mixed cased.
   220	func hasToken(v, token string) bool {
   221		if len(token) > len(v) || token == "" {
   222			return false
   223		}
   224		if v == token {
   225			return true
   226		}
   227		for sp := 0; sp <= len(v)-len(token); sp++ {
   228			// Check that first character is good.
   229			// The token is ASCII, so checking only a single byte
   230			// is sufficient. We skip this potential starting
   231			// position if both the first byte and its potential
   232			// ASCII uppercase equivalent (b|0x20) don't match.
   233			// False positives ('^' => '~') are caught by EqualFold.
   234			if b := v[sp]; b != token[0] && b|0x20 != token[0] {
   235				continue
   236			}
   237			// Check that start pos is on a valid token boundary.
   238			if sp > 0 && !isTokenBoundary(v[sp-1]) {
   239				continue
   240			}
   241			// Check that end pos is on a valid token boundary.
   242			if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) {
   243				continue
   244			}
   245			if strings.EqualFold(v[sp:sp+len(token)], token) {
   246				return true
   247			}
   248		}
   249		return false
   250	}
   251	
   252	func isTokenBoundary(b byte) bool {
   253		return b == ' ' || b == ',' || b == '\t'
   254	}
   255	

View as plain text