...
1 package http
2
3 import (
4 "fmt"
5 "strconv"
6 "strings"
7 "unicode"
8 )
9
10 func splitHeaderListValues(vs []string, splitFn func(string) ([]string, error)) ([]string, error) {
11 values := make([]string, 0, len(vs))
12
13 for i := 0; i < len(vs); i++ {
14 parts, err := splitFn(vs[i])
15 if err != nil {
16 return nil, err
17 }
18 values = append(values, parts...)
19 }
20
21 return values, nil
22 }
23
24
25
26
27 func SplitHeaderListValues(vs []string) ([]string, error) {
28 return splitHeaderListValues(vs, quotedCommaSplit)
29 }
30
31 func quotedCommaSplit(v string) (parts []string, err error) {
32 v = strings.TrimSpace(v)
33
34 expectMore := true
35 for i := 0; i < len(v); i++ {
36 if unicode.IsSpace(rune(v[i])) {
37 continue
38 }
39 expectMore = false
40
41
42
43
44
45
46
47
48 remaining := v[i:]
49
50 var value string
51 var valueLen int
52 if remaining[0] == '"' {
53
54
55
56 var j int
57 var skipQuote bool
58 for j += 1; j < len(remaining); j++ {
59 if remaining[j] == '\\' || (remaining[j] != '\\' && skipQuote) {
60 skipQuote = !skipQuote
61 continue
62 }
63 if remaining[j] == '"' {
64 break
65 }
66 }
67 if j == len(remaining) || j == 1 {
68 return nil, fmt.Errorf("value %v missing closing double quote",
69 remaining)
70 }
71 valueLen = j + 1
72
73 tail := remaining[valueLen:]
74 var k int
75 for ; k < len(tail); k++ {
76 if !unicode.IsSpace(rune(tail[k])) && tail[k] != ',' {
77 return nil, fmt.Errorf("value %v has non-space trailing characters",
78 remaining)
79 }
80 if tail[k] == ',' {
81 expectMore = true
82 break
83 }
84 }
85 value = remaining[:valueLen]
86 value, err = strconv.Unquote(value)
87 if err != nil {
88 return nil, fmt.Errorf("failed to unquote value %v, %w", value, err)
89 }
90
91
92 valueLen += k
93
94 } else {
95
96
97
98
99
100 valueLen = strings.Index(remaining, ",")
101 if valueLen != -1 {
102 expectMore = true
103 } else {
104 valueLen = len(remaining)
105 }
106 value = strings.TrimSpace(remaining[:valueLen])
107 }
108
109 i += valueLen
110 parts = append(parts, value)
111
112 }
113
114 if expectMore {
115 parts = append(parts, "")
116 }
117
118 return parts, nil
119 }
120
121
122
123
124
125
126 func SplitHTTPDateTimestampHeaderListValues(vs []string) ([]string, error) {
127 return splitHeaderListValues(vs, splitHTTPDateHeaderValue)
128 }
129
130 func splitHTTPDateHeaderValue(v string) ([]string, error) {
131 if n := strings.Count(v, ","); n <= 1 {
132
133 return []string{v}, nil
134 } else if n%2 == 0 {
135 return nil, fmt.Errorf("invalid timestamp HTTPDate header comma separations, %q", v)
136 }
137
138 var parts []string
139 var i, j int
140
141 var doSplit bool
142 for ; i < len(v); i++ {
143 if v[i] == ',' {
144 if doSplit {
145 doSplit = false
146 parts = append(parts, strings.TrimSpace(v[j:i]))
147 j = i + 1
148 } else {
149
150
151
152
153 doSplit = true
154 }
155 }
156 }
157
158 if j < len(v) {
159 parts = append(parts, strings.TrimSpace(v[j:]))
160 }
161
162 return parts, nil
163 }
164
View as plain text