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