github.com/useflyent/fhttp@v0.0.0-20211004035111-333f430cfbbf/header.go (about) 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/textproto" 10 "sort" 11 "strings" 12 "sync" 13 "time" 14 15 "github.com/useflyent/fhttp/httptrace" 16 ) 17 18 // A Header represents the Key-value pairs in an HTTP header. 19 // 20 // The keys should be in canonical form, as returned by 21 // CanonicalHeaderKey. 22 type Header map[string][]string 23 24 // HeaderOrderKey is a magic Key for ResponseWriter.Header map keys 25 // that, if present, defines a header order that will be used to 26 // write the headers onto wire. The order of the slice defined how the headers 27 // will be sorted. A defined Key goes before an undefined Key. 28 // 29 // This is the only way to specify some order, because maps don't 30 // have a a stable iteration order. If no order is given, headers will 31 // be sorted lexicographically. 32 // 33 // According to RFC2616 it is good practice to send general-header fields 34 // first, followed by request-header or response-header fields and ending 35 // with entity-header fields. 36 const HeaderOrderKey = "Header-Order:" 37 38 // PHeaderOrderKey is a magic Key for setting http2 pseudo header order. 39 // If the header is nil it will use regular GoLang header order. 40 // Valid fields are :authority, :method, :path, :scheme 41 const PHeaderOrderKey = "PHeader-Order:" 42 43 // Add adds the Key, value pair to the header. 44 // It appends to any existing Values associated with Key. 45 // The Key is case insensitive; it is canonicalized by 46 // CanonicalHeaderKey. 47 func (h Header) Add(key, value string) { 48 textproto.MIMEHeader(h).Add(key, value) 49 } 50 51 // Set sets the header entries associated with Key to the 52 // single element value. It replaces any existing Values 53 // associated with Key. The Key is case insensitive; it is 54 // canonicalized by textproto.CanonicalMIMEHeaderKey. 55 // To use non-canonical keys, assign to the map directly. 56 func (h Header) Set(key, value string) { 57 textproto.MIMEHeader(h).Set(key, value) 58 } 59 60 // Get gets the first value associated with the given Key. If 61 // there are no Values associated with the Key, Get returns "". 62 // It is case insensitive; textproto.CanonicalMIMEHeaderKey is 63 // used to canonicalize the provided Key. To use non-canonical keys, 64 // access the map directly. 65 func (h Header) Get(key string) string { 66 return textproto.MIMEHeader(h).Get(key) 67 } 68 69 // Values returns all Values associated with the given Key. 70 // It is case insensitive; textproto.CanonicalMIMEHeaderKey is 71 // used to canonicalize the provided Key. To use non-canonical 72 // keys, access the map directly. 73 // The returned slice is not a copy. 74 func (h Header) Values(key string) []string { 75 return textproto.MIMEHeader(h).Values(key) 76 } 77 78 // get is like Get, but Key must already be in CanonicalHeaderKey form. 79 func (h Header) get(key string) string { 80 if v := h[key]; len(v) > 0 { 81 return v[0] 82 } 83 return "" 84 } 85 86 // has reports whether h has the provided Key defined, even if it's 87 // set to 0-length slice. 88 func (h Header) has(key string) bool { 89 _, ok := h[key] 90 return ok 91 } 92 93 // Del deletes the Values associated with Key. 94 // The Key is case insensitive; it is canonicalized by 95 // CanonicalHeaderKey. 96 func (h Header) Del(key string) { 97 textproto.MIMEHeader(h).Del(key) 98 } 99 100 // Write writes a header in wire format. 101 func (h Header) Write(w io.Writer) error { 102 return h.write(w, nil) 103 } 104 105 func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error { 106 return h.writeSubset(w, nil, trace) 107 } 108 109 // Clone returns a copy of h or nil if h is nil. 110 func (h Header) Clone() Header { 111 if h == nil { 112 return nil 113 } 114 115 // Find total number of Values. 116 nv := 0 117 for _, vv := range h { 118 nv += len(vv) 119 } 120 sv := make([]string, nv) // shared backing array for headers' Values 121 h2 := make(Header, len(h)) 122 for k, vv := range h { 123 n := copy(sv, vv) 124 h2[k] = sv[:n:n] 125 sv = sv[n:] 126 } 127 return h2 128 } 129 130 var timeFormats = []string{ 131 TimeFormat, 132 time.RFC850, 133 time.ANSIC, 134 } 135 136 // ParseTime parses a time header (such as the Date: header), 137 // trying each of the three formats allowed by HTTP/1.1: 138 // TimeFormat, time.RFC850, and time.ANSIC. 139 func ParseTime(text string) (t time.Time, err error) { 140 for _, layout := range timeFormats { 141 t, err = time.Parse(layout, text) 142 if err == nil { 143 return 144 } 145 } 146 return 147 } 148 149 var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ") 150 151 // stringWriter implements WriteString on a Writer. 152 type stringWriter struct { 153 w io.Writer 154 } 155 156 func (w stringWriter) WriteString(s string) (n int, err error) { 157 return w.w.Write([]byte(s)) 158 } 159 160 type HeaderKeyValues struct { 161 Key string 162 Values []string 163 } 164 165 // A headerSorter implements sort.Interface by sorting a []keyValues 166 // by the given order, if not nil, or by Key otherwise. 167 // It's used as a pointer, so it can fit in a sort.Interface 168 // interface value without allocation. 169 type headerSorter struct { 170 kvs []HeaderKeyValues 171 order map[string]int 172 } 173 174 func (s *headerSorter) Len() int { return len(s.kvs) } 175 func (s *headerSorter) Swap(i, j int) { s.kvs[i], s.kvs[j] = s.kvs[j], s.kvs[i] } 176 func (s *headerSorter) Less(i, j int) bool { 177 // If the order isn't defined, sort lexicographically. 178 if s.order == nil { 179 return s.kvs[i].Key < s.kvs[j].Key 180 } 181 //idxi, iok := s.order[s.kvs[i].Key] 182 //idxj, jok := s.order[s.kvs[j].Key] 183 idxi, iok := s.order[strings.ToLower(s.kvs[i].Key)] 184 idxj, jok := s.order[strings.ToLower(s.kvs[j].Key)] 185 if !iok && !jok { 186 return s.kvs[i].Key < s.kvs[j].Key 187 } else if !iok && jok { 188 return false 189 } else if iok && !jok { 190 return true 191 } 192 return idxi < idxj 193 } 194 195 var headerSorterPool = sync.Pool{ 196 New: func() interface{} { return new(headerSorter) }, 197 } 198 199 var mutex = &sync.RWMutex{} 200 201 // SortedKeyValues returns h's keys sorted in the returned kvs 202 // slice. The headerSorter used to sort is also returned, for possible 203 // return to headerSorterCache. 204 func (h Header) SortedKeyValues(exclude map[string]bool) (kvs []HeaderKeyValues, hs *headerSorter) { 205 hs = headerSorterPool.Get().(*headerSorter) 206 if cap(hs.kvs) < len(h) { 207 hs.kvs = make([]HeaderKeyValues, 0, len(h)) 208 } 209 kvs = hs.kvs[:0] 210 for k, vv := range h { 211 mutex.RLock() 212 if !exclude[k] { 213 kvs = append(kvs, HeaderKeyValues{k, vv}) 214 } 215 mutex.RUnlock() 216 } 217 hs.kvs = kvs 218 sort.Sort(hs) 219 return kvs, hs 220 } 221 222 func (h Header) SortedKeyValuesBy(order map[string]int, exclude map[string]bool) (kvs []HeaderKeyValues, hs *headerSorter) { 223 hs = headerSorterPool.Get().(*headerSorter) 224 if cap(hs.kvs) < len(h) { 225 hs.kvs = make([]HeaderKeyValues, 0, len(h)) 226 } 227 kvs = hs.kvs[:0] 228 for k, vv := range h { 229 mutex.RLock() 230 if !exclude[k] { 231 kvs = append(kvs, HeaderKeyValues{k, vv}) 232 } 233 mutex.RUnlock() 234 } 235 hs.kvs = kvs 236 hs.order = order 237 sort.Sort(hs) 238 239 return kvs, hs 240 } 241 242 // WriteSubset writes a header in wire format. 243 // If exclude is not nil, keys where exclude[Key] == true are not written. 244 // Keys are not canonicalized before checking the exclude map. 245 func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { 246 return h.writeSubset(w, exclude, nil) 247 } 248 249 func (h Header) writeSubset(w io.Writer, exclude map[string]bool, trace *httptrace.ClientTrace) error { 250 ws, ok := w.(io.StringWriter) 251 if !ok { 252 ws = stringWriter{w} 253 } 254 255 var kvs []HeaderKeyValues 256 var sorter *headerSorter 257 258 // Check if the HeaderOrder is defined. 259 if headerOrder, ok := h[HeaderOrderKey]; ok { 260 order := make(map[string]int) 261 for i, v := range headerOrder { 262 order[v] = i 263 } 264 if exclude == nil { 265 exclude = make(map[string]bool) 266 } 267 mutex.Lock() 268 exclude[HeaderOrderKey] = true 269 exclude[PHeaderOrderKey] = true 270 mutex.Unlock() 271 kvs, sorter = h.SortedKeyValuesBy(order, exclude) 272 } else { 273 kvs, sorter = h.SortedKeyValues(exclude) 274 } 275 276 var formattedVals []string 277 for _, kv := range kvs { 278 for _, v := range kv.Values { 279 v = headerNewlineToSpace.Replace(v) 280 v = textproto.TrimString(v) 281 for _, s := range []string{kv.Key, ": ", v, "\r\n"} { 282 if _, err := ws.WriteString(s); err != nil { 283 headerSorterPool.Put(sorter) 284 return err 285 } 286 } 287 if trace != nil && trace.WroteHeaderField != nil { 288 formattedVals = append(formattedVals, v) 289 } 290 } 291 if trace != nil && trace.WroteHeaderField != nil { 292 trace.WroteHeaderField(kv.Key, formattedVals) 293 formattedVals = nil 294 } 295 } 296 headerSorterPool.Put(sorter) 297 return nil 298 } 299 300 // CanonicalHeaderKey returns the canonical format of the 301 // header Key s. The canonicalization converts the first 302 // letter and any letter following a hyphen to upper case; 303 // the rest are converted to lowercase. For example, the 304 // canonical Key for "accept-encoding" is "Accept-Encoding". 305 // If s contains a space or invalid header field bytes, it is 306 // returned without modifications. 307 func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) } 308 309 // hasToken reports whether token appears with v, ASCII 310 // case-insensitive, with space or comma boundaries. 311 // token must be all lowercase. 312 // v may contain mixed cased. 313 func hasToken(v, token string) bool { 314 if len(token) > len(v) || token == "" { 315 return false 316 } 317 if v == token { 318 return true 319 } 320 for sp := 0; sp <= len(v)-len(token); sp++ { 321 // Check that first character is good. 322 // The token is ASCII, so checking only a single byte 323 // is sufficient. We skip this potential starting 324 // position if both the first byte and its potential 325 // ASCII uppercase equivalent (b|0x20) don't match. 326 // False positives ('^' => '~') are caught by EqualFold. 327 if b := v[sp]; b != token[0] && b|0x20 != token[0] { 328 continue 329 } 330 // Check that start pos is on a valid token boundary. 331 if sp > 0 && !isTokenBoundary(v[sp-1]) { 332 continue 333 } 334 // Check that end pos is on a valid token boundary. 335 if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) { 336 continue 337 } 338 if strings.EqualFold(v[sp:sp+len(token)], token) { 339 return true 340 } 341 } 342 return false 343 } 344 345 func isTokenBoundary(b byte) bool { 346 return b == ' ' || b == ',' || b == '\t' 347 }