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.
17 var raceEnabled
= false // set by race.go
19 // A Header represents the key-value pairs in an HTTP header.
20 type Header
map[string][]string
22 // Add adds the key, value pair to the header.
23 // It appends to any existing values associated with key.
24 func (h Header
) Add(key
, value
string) {
25 textproto
.MIMEHeader(h
).Add(key
, value
)
28 // Set sets the header entries associated with key to
29 // the single element value. It replaces any existing
30 // values associated with key.
31 func (h Header
) Set(key
, value
string) {
32 textproto
.MIMEHeader(h
).Set(key
, value
)
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.
41 func (h Header
) Get(key
string) string {
42 return textproto
.MIMEHeader(h
).Get(key
)
45 // get is like Get, but key must already be in CanonicalHeaderKey form.
46 func (h Header
) get(key
string) string {
47 if v
:= h
[key
]; len(v
) > 0 {
53 // Del deletes the values associated with key.
54 func (h Header
) Del(key
string) {
55 textproto
.MIMEHeader(h
).Del(key
)
58 // Write writes a header in wire format.
59 func (h Header
) Write(w io
.Writer
) error
{
60 return h
.write(w
, nil)
63 func (h Header
) write(w io
.Writer
, trace
*httptrace
.ClientTrace
) error
{
64 return h
.writeSubset(w
, nil, trace
)
67 func (h Header
) clone() Header
{
68 h2
:= make(Header
, len(h
))
69 for k
, vv
:= range h
{
70 vv2
:= make([]string, len(vv
))
77 var timeFormats
= []string{
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.
86 func ParseTime(text
string) (t time
.Time
, err error
) {
87 for _
, layout
:= range timeFormats
{
88 t
, err
= time
.Parse(layout
, text
)
96 var headerNewlineToSpace
= strings
.NewReplacer("\n", " ", "\r", " ")
98 type writeStringer
interface {
99 WriteString(string) (int, error
)
102 // stringWriter implements WriteString on a Writer.
103 type stringWriter
struct {
107 func (w stringWriter
) WriteString(s
string) (n
int, err error
) {
108 return w
.w
.Write([]byte(s
))
111 type keyValues
struct {
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.
119 type headerSorter
struct {
123 func (s
*headerSorter
) Len() int { return len(s
.kvs
) }
124 func (s
*headerSorter
) Swap(i
, j
int) { s
.kvs
[i
], s
.kvs
[j
] = s
.kvs
[j
], s
.kvs
[i
] }
125 func (s
*headerSorter
) Less(i
, j
int) bool { return s
.kvs
[i
].key
< s
.kvs
[j
].key
}
127 var headerSorterPool
= sync
.Pool
{
128 New
: func() interface{} { return new(headerSorter
) },
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.
134 func (h Header
) sortedKeyValues(exclude
map[string]bool) (kvs
[]keyValues
, hs
*headerSorter
) {
135 hs
= headerSorterPool
.Get().(*headerSorter
)
136 if cap(hs
.kvs
) < len(h
) {
137 hs
.kvs
= make([]keyValues
, 0, len(h
))
140 for k
, vv
:= range h
{
142 kvs
= append(kvs
, keyValues
{k
, vv
})
150 // WriteSubset writes a header in wire format.
151 // If exclude is not nil, keys where exclude[key] == true are not written.
152 func (h Header
) WriteSubset(w io
.Writer
, exclude
map[string]bool) error
{
153 return h
.writeSubset(w
, exclude
, nil)
156 func (h Header
) writeSubset(w io
.Writer
, exclude
map[string]bool, trace
*httptrace
.ClientTrace
) error
{
157 ws
, ok
:= w
.(writeStringer
)
161 kvs
, sorter
:= h
.sortedKeyValues(exclude
)
162 var formattedVals
[]string
163 for _
, kv
:= range kvs
{
164 for _
, v
:= range kv
.values
{
165 v
= headerNewlineToSpace
.Replace(v
)
166 v
= textproto
.TrimString(v
)
167 for _
, s
:= range []string{kv
.key
, ": ", v
, "\r\n"} {
168 if _
, err
:= ws
.WriteString(s
); err
!= nil {
169 headerSorterPool
.Put(sorter
)
173 if trace
!= nil && trace
.WroteHeaderField
!= nil {
174 formattedVals
= append(formattedVals
, v
)
177 if trace
!= nil && trace
.WroteHeaderField
!= nil {
178 trace
.WroteHeaderField(kv
.key
, formattedVals
)
182 headerSorterPool
.Put(sorter
)
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.
193 func CanonicalHeaderKey(s
string) string { return textproto
.CanonicalMIMEHeaderKey(s
) }
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.
199 func hasToken(v
, token
string) bool {
200 if len(token
) > len(v
) || token
== "" {
206 for 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 byte
209 // is sufficient. We skip this potential starting
210 // position if both the first byte and its potential
211 // ASCII uppercase equivalent (b|0x20) don't match.
212 // False positives ('^' => '~') are caught by EqualFold.
213 if b
:= v
[sp
]; b
!= token
[0] && b|
0x20 != token
[0] {
216 // Check that start pos is on a valid token boundary.
217 if sp
> 0 && !isTokenBoundary(v
[sp
-1]) {
220 // Check that end pos is on a valid token boundary.
221 if endPos
:= sp
+ len(token
); endPos
!= len(v
) && !isTokenBoundary(v
[endPos
]) {
224 if strings
.EqualFold(v
[sp
:sp
+len(token
)], token
) {
231 func isTokenBoundary(b
byte) bool {
232 return b
== ' ' || b
== ',' || b
== '\t'
235 func cloneHeader(h Header
) Header
{
236 h2
:= make(Header
, len(h
))
237 for k
, vv
:= range h
{
238 vv2
:= make([]string, len(vv
))