Source file
src/net/http/header.go
1
2
3
4
5 package http
6
7 import (
8 "io"
9 "net/http/httptrace"
10 "net/http/internal/ascii"
11 "net/textproto"
12 "sort"
13 "strings"
14 "sync"
15 "time"
16 )
17
18
19
20
21
22 type Header map[string][]string
23
24
25
26
27
28 func (h Header) Add(key, value string) {
29 textproto.MIMEHeader(h).Add(key, value)
30 }
31
32
33
34
35
36
37 func (h Header) Set(key, value string) {
38 textproto.MIMEHeader(h).Set(key, value)
39 }
40
41
42
43
44
45
46 func (h Header) Get(key string) string {
47 return textproto.MIMEHeader(h).Get(key)
48 }
49
50
51
52
53
54
55 func (h Header) Values(key string) []string {
56 return textproto.MIMEHeader(h).Values(key)
57 }
58
59
60 func (h Header) get(key string) string {
61 if v := h[key]; len(v) > 0 {
62 return v[0]
63 }
64 return ""
65 }
66
67
68
69 func (h Header) has(key string) bool {
70 _, ok := h[key]
71 return ok
72 }
73
74
75
76
77 func (h Header) Del(key string) {
78 textproto.MIMEHeader(h).Del(key)
79 }
80
81
82 func (h Header) Write(w io.Writer) error {
83 return h.write(w, nil)
84 }
85
86 func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error {
87 return h.writeSubset(w, nil, trace)
88 }
89
90
91 func (h Header) Clone() Header {
92 if h == nil {
93 return nil
94 }
95
96
97 nv := 0
98 for _, vv := range h {
99 nv += len(vv)
100 }
101 sv := make([]string, nv)
102 h2 := make(Header, len(h))
103 for k, vv := range h {
104 if vv == nil {
105
106
107 h2[k] = nil
108 continue
109 }
110 n := copy(sv, vv)
111 h2[k] = sv[:n:n]
112 sv = sv[n:]
113 }
114 return h2
115 }
116
117 var timeFormats = []string{
118 TimeFormat,
119 time.RFC850,
120 time.ANSIC,
121 }
122
123
124
125
126 func ParseTime(text string) (t time.Time, err error) {
127 for _, layout := range timeFormats {
128 t, err = time.Parse(layout, text)
129 if err == nil {
130 return
131 }
132 }
133 return
134 }
135
136 var headerNewlineToSpace = strings.NewReplacer("\n", " ", "\r", " ")
137
138
139 type stringWriter struct {
140 w io.Writer
141 }
142
143 func (w stringWriter) WriteString(s string) (n int, err error) {
144 return w.w.Write([]byte(s))
145 }
146
147 type keyValues struct {
148 key string
149 values []string
150 }
151
152
153
154
155 type headerSorter struct {
156 kvs []keyValues
157 }
158
159 func (s *headerSorter) Len() int { return len(s.kvs) }
160 func (s *headerSorter) Swap(i, j int) { s.kvs[i], s.kvs[j] = s.kvs[j], s.kvs[i] }
161 func (s *headerSorter) Less(i, j int) bool { return s.kvs[i].key < s.kvs[j].key }
162
163 var headerSorterPool = sync.Pool{
164 New: func() interface{} { return new(headerSorter) },
165 }
166
167
168
169
170 func (h Header) sortedKeyValues(exclude map[string]bool) (kvs []keyValues, hs *headerSorter) {
171 hs = headerSorterPool.Get().(*headerSorter)
172 if cap(hs.kvs) < len(h) {
173 hs.kvs = make([]keyValues, 0, len(h))
174 }
175 kvs = hs.kvs[:0]
176 for k, vv := range h {
177 if !exclude[k] {
178 kvs = append(kvs, keyValues{k, vv})
179 }
180 }
181 hs.kvs = kvs
182 sort.Sort(hs)
183 return kvs, hs
184 }
185
186
187
188
189 func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error {
190 return h.writeSubset(w, exclude, nil)
191 }
192
193 func (h Header) writeSubset(w io.Writer, exclude map[string]bool, trace *httptrace.ClientTrace) error {
194 ws, ok := w.(io.StringWriter)
195 if !ok {
196 ws = stringWriter{w}
197 }
198 kvs, sorter := h.sortedKeyValues(exclude)
199 var formattedVals []string
200 for _, kv := range kvs {
201 for _, v := range kv.values {
202 v = headerNewlineToSpace.Replace(v)
203 v = textproto.TrimString(v)
204 for _, s := range []string{kv.key, ": ", v, "\r\n"} {
205 if _, err := ws.WriteString(s); err != nil {
206 headerSorterPool.Put(sorter)
207 return err
208 }
209 }
210 if trace != nil && trace.WroteHeaderField != nil {
211 formattedVals = append(formattedVals, v)
212 }
213 }
214 if trace != nil && trace.WroteHeaderField != nil {
215 trace.WroteHeaderField(kv.key, formattedVals)
216 formattedVals = nil
217 }
218 }
219 headerSorterPool.Put(sorter)
220 return nil
221 }
222
223
224
225
226
227
228
229
230 func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) }
231
232
233
234
235
236 func hasToken(v, token string) bool {
237 if len(token) > len(v) || token == "" {
238 return false
239 }
240 if v == token {
241 return true
242 }
243 for sp := 0; sp <= len(v)-len(token); sp++ {
244
245
246
247
248
249
250 if b := v[sp]; b != token[0] && b|0x20 != token[0] {
251 continue
252 }
253
254 if sp > 0 && !isTokenBoundary(v[sp-1]) {
255 continue
256 }
257
258 if endPos := sp + len(token); endPos != len(v) && !isTokenBoundary(v[endPos]) {
259 continue
260 }
261 if ascii.EqualFold(v[sp:sp+len(token)], token) {
262 return true
263 }
264 }
265 return false
266 }
267
268 func isTokenBoundary(b byte) bool {
269 return b == ' ' || b == ',' || b == '\t'
270 }
271
View as plain text