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