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 5package http 6 7import ( 8 "io" 9 "net/textproto" 10 "sort" 11 "strings" 12 "time" 13) 14 15// A Header represents the key-value pairs in an HTTP header. 16type Header map[string][]string 17 18// Add adds the key, value pair to the header. 19// It appends to any existing values associated with key. 20func (h Header) Add(key, value string) { 21 textproto.MIMEHeader(h).Add(key, value) 22} 23 24// Set sets the header entries associated with key to 25// the single element value. It replaces any existing 26// values associated with key. 27func (h Header) Set(key, value string) { 28 textproto.MIMEHeader(h).Set(key, value) 29} 30 31// Get gets the first value associated with the given key. 32// If there are no values associated with the key, Get returns "". 33// To access multiple values of a key, access the map directly 34// with CanonicalHeaderKey. 35func (h Header) Get(key string) string { 36 return textproto.MIMEHeader(h).Get(key) 37} 38 39// get is like Get, but key must already be in CanonicalHeaderKey form. 40func (h Header) get(key string) string { 41 if v := h[key]; len(v) > 0 { 42 return v[0] 43 } 44 return "" 45} 46 47// Del deletes the values associated with key. 48func (h Header) Del(key string) { 49 textproto.MIMEHeader(h).Del(key) 50} 51 52// Write writes a header in wire format. 53func (h Header) Write(w io.Writer) error { 54 return h.WriteSubset(w, nil) 55} 56 57func (h Header) clone() Header { 58 h2 := make(Header, len(h)) 59 for k, vv := range h { 60 vv2 := make([]string, len(vv)) 61 copy(vv2, vv) 62 h2[k] = vv2 63 } 64 return h2 65} 66 67var timeFormats = []string{ 68 TimeFormat, 69 time.RFC850, 70 time.ANSIC, 71} 72 73// ParseTime parses a time header (such as the Date: header), 74// trying each of the three formats allowed by HTTP/1.1: 75// TimeFormat, time.RFC850, and time.ANSIC. 76func ParseTime(text string) (t time.Time, err error) { 77 for _, layout := range timeFormats { 78 t, err = time.Parse(layout, text) 79 if err == nil { 80 return 81 } 82 } 83 return 84} 85 86var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ") 87 88type writeStringer interface { 89 WriteString(string) (int, error) 90} 91 92// stringWriter implements WriteString on a Writer. 93type stringWriter struct { 94 w io.Writer 95} 96 97func (w stringWriter) WriteString(s string) (n int, err error) { 98 return w.w.Write([]byte(s)) 99} 100 101type keyValues struct { 102 key string 103 values []string 104} 105 106type byKey []keyValues 107 108func (s byKey) Len() int { return len(s) } 109func (s byKey) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 110func (s byKey) Less(i, j int) bool { return s[i].key < s[j].key } 111 112func (h Header) sortedKeyValues(exclude map[string]bool) []keyValues { 113 kvs := make([]keyValues, 0, len(h)) 114 for k, vv := range h { 115 if !exclude[k] { 116 kvs = append(kvs, keyValues{k, vv}) 117 } 118 } 119 sort.Sort(byKey(kvs)) 120 return kvs 121} 122 123// WriteSubset writes a header in wire format. 124// If exclude is not nil, keys where exclude[key] == true are not written. 125func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { 126 ws, ok := w.(writeStringer) 127 if !ok { 128 ws = stringWriter{w} 129 } 130 for _, kv := range h.sortedKeyValues(exclude) { 131 for _, v := range kv.values { 132 v = headerNewlineToSpace.Replace(v) 133 v = textproto.TrimString(v) 134 for _, s := range []string{kv.key, ": ", v, "\r\n"} { 135 if _, err := ws.WriteString(s); err != nil { 136 return err 137 } 138 } 139 } 140 } 141 return nil 142} 143 144// CanonicalHeaderKey returns the canonical format of the 145// header key s. The canonicalization converts the first 146// letter and any letter following a hyphen to upper case; 147// the rest are converted to lowercase. For example, the 148// canonical key for "accept-encoding" is "Accept-Encoding". 149func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) } 150 151// hasToken returns whether token appears with v, ASCII 152// case-insensitive, with space or comma boundaries. 153// token must be all lowercase. 154// v may contain mixed cased. 155func hasToken(v, token string) bool { 156 if len(token) > len(v) || token == "" { 157 return false 158 } 159 if v == token { 160 return true 161 } 162 for sp := 0; sp <= len(v)-len(token); sp++ { 163 // Check that first character is good. 164 // The token is ASCII, so checking only a single byte 165 // is sufficient. We skip this potential starting 166 // position if both the first byte and its potential 167 // ASCII uppercase equivalent (b|0x20) don't match. 168 // False positives ('^' => '~') are caught by EqualFold. 169 if b := v[sp]; b != token[0] && b|0x20 != token[0] { 170 continue 171 } 172 // Check that start pos is on a valid token boundary. 173 if sp > 0 && !isTokenBoundary(v[sp-1]) { 174 continue 175 } 176 // Check that end pos is on a valid token boundary. 177 if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) { 178 continue 179 } 180 if strings.EqualFold(v[sp:sp+len(token)], token) { 181 return true 182 } 183 } 184 return false 185} 186 187func isTokenBoundary(b byte) bool { 188 return b == ' ' || b == ',' || b == '\t' 189} 190