Source file src/pkg/net/http/lex.go
1 // Copyright 2009 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
5 package http
6
7 // This file deals with lexical matters of HTTP
8
9 func isSeparator(c byte) bool {
10 switch c {
11 case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t':
12 return true
13 }
14 return false
15 }
16
17 func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 }
18
19 func isChar(c byte) bool { return 0 <= c && c <= 127 }
20
21 func isAnyText(c byte) bool { return !isCtl(c) }
22
23 func isQdText(c byte) bool { return isAnyText(c) && c != '"' }
24
25 func isToken(c byte) bool { return isChar(c) && !isCtl(c) && !isSeparator(c) }
26
27 // Valid escaped sequences are not specified in RFC 2616, so for now, we assume
28 // that they coincide with the common sense ones used by GO. Malformed
29 // characters should probably not be treated as errors by a robust (forgiving)
30 // parser, so we replace them with the '?' character.
31 func httpUnquotePair(b byte) byte {
32 // skip the first byte, which should always be '\'
33 switch b {
34 case 'a':
35 return '\a'
36 case 'b':
37 return '\b'
38 case 'f':
39 return '\f'
40 case 'n':
41 return '\n'
42 case 'r':
43 return '\r'
44 case 't':
45 return '\t'
46 case 'v':
47 return '\v'
48 case '\\':
49 return '\\'
50 case '\'':
51 return '\''
52 case '"':
53 return '"'
54 }
55 return '?'
56 }
57
58 // raw must begin with a valid quoted string. Only the first quoted string is
59 // parsed and is unquoted in result. eaten is the number of bytes parsed, or -1
60 // upon failure.
61 func httpUnquote(raw []byte) (eaten int, result string) {
62 buf := make([]byte, len(raw))
63 if raw[0] != '"' {
64 return -1, ""
65 }
66 eaten = 1
67 j := 0 // # of bytes written in buf
68 for i := 1; i < len(raw); i++ {
69 switch b := raw[i]; b {
70 case '"':
71 eaten++
72 buf = buf[0:j]
73 return i + 1, string(buf)
74 case '\\':
75 if len(raw) < i+2 {
76 return -1, ""
77 }
78 buf[j] = httpUnquotePair(raw[i+1])
79 eaten += 2
80 j++
81 i++
82 default:
83 if isQdText(b) {
84 buf[j] = b
85 } else {
86 buf[j] = '?'
87 }
88 eaten++
89 j++
90 }
91 }
92 return -1, ""
93 }
94
95 // This is a best effort parse, so errors are not returned, instead not all of
96 // the input string might be parsed. result is always non-nil.
97 func httpSplitFieldValue(fv string) (eaten int, result []string) {
98 result = make([]string, 0, len(fv))
99 raw := []byte(fv)
100 i := 0
101 chunk := ""
102 for i < len(raw) {
103 b := raw[i]
104 switch {
105 case b == '"':
106 eaten, unq := httpUnquote(raw[i:len(raw)])
107 if eaten < 0 {
108 return i, result
109 } else {
110 i += eaten
111 chunk += unq
112 }
113 case isSeparator(b):
114 if chunk != "" {
115 result = result[0 : len(result)+1]
116 result[len(result)-1] = chunk
117 chunk = ""
118 }
119 i++
120 case isToken(b):
121 chunk += string(b)
122 i++
123 case b == '\n' || b == '\r':
124 i++
125 default:
126 chunk += "?"
127 i++
128 }
129 }
130 if chunk != "" {
131 result = result[0 : len(result)+1]
132 result[len(result)-1] = chunk
133 chunk = ""
134 }
135 return i, result
136 }