1
2
3
4
5 package httpguts
6
7 import (
8 "net"
9 "strings"
10 "unicode/utf8"
11
12 "golang.org/x/net/idna"
13 )
14
15 var isTokenTable = [256]bool{
16 '!': true,
17 '#': true,
18 '$': true,
19 '%': true,
20 '&': true,
21 '\'': true,
22 '*': true,
23 '+': true,
24 '-': true,
25 '.': true,
26 '0': true,
27 '1': true,
28 '2': true,
29 '3': true,
30 '4': true,
31 '5': true,
32 '6': true,
33 '7': true,
34 '8': true,
35 '9': true,
36 'A': true,
37 'B': true,
38 'C': true,
39 'D': true,
40 'E': true,
41 'F': true,
42 'G': true,
43 'H': true,
44 'I': true,
45 'J': true,
46 'K': true,
47 'L': true,
48 'M': true,
49 'N': true,
50 'O': true,
51 'P': true,
52 'Q': true,
53 'R': true,
54 'S': true,
55 'T': true,
56 'U': true,
57 'W': true,
58 'V': true,
59 'X': true,
60 'Y': true,
61 'Z': true,
62 '^': true,
63 '_': true,
64 '`': true,
65 'a': true,
66 'b': true,
67 'c': true,
68 'd': true,
69 'e': true,
70 'f': true,
71 'g': true,
72 'h': true,
73 'i': true,
74 'j': true,
75 'k': true,
76 'l': true,
77 'm': true,
78 'n': true,
79 'o': true,
80 'p': true,
81 'q': true,
82 'r': true,
83 's': true,
84 't': true,
85 'u': true,
86 'v': true,
87 'w': true,
88 'x': true,
89 'y': true,
90 'z': true,
91 '|': true,
92 '~': true,
93 }
94
95 func IsTokenRune(r rune) bool {
96 return r < utf8.RuneSelf && isTokenTable[byte(r)]
97 }
98
99
100
101 func HeaderValuesContainsToken(values []string, token string) bool {
102 for _, v := range values {
103 if headerValueContainsToken(v, token) {
104 return true
105 }
106 }
107 return false
108 }
109
110
111
112 func isOWS(b byte) bool { return b == ' ' || b == '\t' }
113
114
115
116 func trimOWS(x string) string {
117
118
119
120
121 for len(x) > 0 && isOWS(x[0]) {
122 x = x[1:]
123 }
124 for len(x) > 0 && isOWS(x[len(x)-1]) {
125 x = x[:len(x)-1]
126 }
127 return x
128 }
129
130
131
132
133
134 func headerValueContainsToken(v string, token string) bool {
135 for comma := strings.IndexByte(v, ','); comma != -1; comma = strings.IndexByte(v, ',') {
136 if tokenEqual(trimOWS(v[:comma]), token) {
137 return true
138 }
139 v = v[comma+1:]
140 }
141 return tokenEqual(trimOWS(v), token)
142 }
143
144
145 func lowerASCII(b byte) byte {
146 if 'A' <= b && b <= 'Z' {
147 return b + ('a' - 'A')
148 }
149 return b
150 }
151
152
153 func tokenEqual(t1, t2 string) bool {
154 if len(t1) != len(t2) {
155 return false
156 }
157 for i, b := range t1 {
158 if b >= utf8.RuneSelf {
159
160 return false
161 }
162 if lowerASCII(byte(b)) != lowerASCII(t2[i]) {
163 return false
164 }
165 }
166 return true
167 }
168
169
170
171
172
173 func isLWS(b byte) bool { return b == ' ' || b == '\t' }
174
175
176
177
178
179
180 func isCTL(b byte) bool {
181 const del = 0x7f
182 return b < ' ' || b == del
183 }
184
185
186
187
188
189
190
191
192
193
194
195
196 func ValidHeaderFieldName(v string) bool {
197 if len(v) == 0 {
198 return false
199 }
200 for i := 0; i < len(v); i++ {
201 if !isTokenTable[v[i]] {
202 return false
203 }
204 }
205 return true
206 }
207
208
209 func ValidHostHeader(h string) bool {
210
211
212
213
214
215
216
217
218
219
220
221 for i := 0; i < len(h); i++ {
222 if !validHostByte[h[i]] {
223 return false
224 }
225 }
226 return true
227 }
228
229
230 var validHostByte = [256]bool{
231 '0': true, '1': true, '2': true, '3': true, '4': true, '5': true, '6': true, '7': true,
232 '8': true, '9': true,
233
234 'a': true, 'b': true, 'c': true, 'd': true, 'e': true, 'f': true, 'g': true, 'h': true,
235 'i': true, 'j': true, 'k': true, 'l': true, 'm': true, 'n': true, 'o': true, 'p': true,
236 'q': true, 'r': true, 's': true, 't': true, 'u': true, 'v': true, 'w': true, 'x': true,
237 'y': true, 'z': true,
238
239 'A': true, 'B': true, 'C': true, 'D': true, 'E': true, 'F': true, 'G': true, 'H': true,
240 'I': true, 'J': true, 'K': true, 'L': true, 'M': true, 'N': true, 'O': true, 'P': true,
241 'Q': true, 'R': true, 'S': true, 'T': true, 'U': true, 'V': true, 'W': true, 'X': true,
242 'Y': true, 'Z': true,
243
244 '!': true,
245 '$': true,
246 '%': true,
247 '&': true,
248 '(': true,
249 ')': true,
250 '*': true,
251 '+': true,
252 ',': true,
253 '-': true,
254 '.': true,
255 ':': true,
256 ';': true,
257 '=': true,
258 '[': true,
259 '\'': true,
260 ']': true,
261 '_': true,
262 '~': true,
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303 func ValidHeaderFieldValue(v string) bool {
304 for i := 0; i < len(v); i++ {
305 b := v[i]
306 if isCTL(b) && !isLWS(b) {
307 return false
308 }
309 }
310 return true
311 }
312
313 func isASCII(s string) bool {
314 for i := 0; i < len(s); i++ {
315 if s[i] >= utf8.RuneSelf {
316 return false
317 }
318 }
319 return true
320 }
321
322
323
324 func PunycodeHostPort(v string) (string, error) {
325 if isASCII(v) {
326 return v, nil
327 }
328
329 host, port, err := net.SplitHostPort(v)
330 if err != nil {
331
332
333
334 host = v
335 port = ""
336 }
337 host, err = idna.ToASCII(host)
338 if err != nil {
339
340
341 return "", err
342 }
343 if port == "" {
344 return host, nil
345 }
346 return net.JoinHostPort(host, port), nil
347 }
348
View as plain text