1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.http;
17
18 import org.jboss.netty.util.internal.StringUtil;
19
20 import java.text.ParseException;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Set;
25 import java.util.TreeSet;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 public class CookieDecoder {
43
44 private static final char COMMA = ',';
45
46
47
48
49 public CookieDecoder() {
50 }
51
52
53
54
55 @Deprecated
56 public CookieDecoder(@SuppressWarnings("unused") boolean lenient) {
57 }
58
59
60
61
62
63
64 public Set<Cookie> decode(String header) {
65 List<String> names = new ArrayList<String>(8);
66 List<String> values = new ArrayList<String>(8);
67 extractKeyValuePairs(header, names, values);
68
69 if (names.isEmpty()) {
70 return Collections.emptySet();
71 }
72
73 int i;
74 int version = 0;
75
76
77
78 if (names.get(0).equalsIgnoreCase(CookieHeaderNames.VERSION)) {
79 try {
80 version = Integer.parseInt(values.get(0));
81 } catch (NumberFormatException e) {
82
83 }
84 i = 1;
85 } else {
86 i = 0;
87 }
88
89 if (names.size() <= i) {
90
91 return Collections.emptySet();
92 }
93
94 Set<Cookie> cookies = new TreeSet<Cookie>();
95 for (; i < names.size(); i ++) {
96 String name = names.get(i);
97 String value = values.get(i);
98 if (value == null) {
99 value = "";
100 }
101
102 Cookie c = new DefaultCookie(name, value);
103
104 boolean discard = false;
105 boolean secure = false;
106 boolean httpOnly = false;
107 String comment = null;
108 String commentURL = null;
109 String domain = null;
110 String path = null;
111 int maxAge = Integer.MIN_VALUE;
112 List<Integer> ports = new ArrayList<Integer>(2);
113
114 for (int j = i + 1; j < names.size(); j++, i++) {
115 name = names.get(j);
116 value = values.get(j);
117
118 if (CookieHeaderNames.DISCARD.equalsIgnoreCase(name)) {
119 discard = true;
120 } else if (CookieHeaderNames.SECURE.equalsIgnoreCase(name)) {
121 secure = true;
122 } else if (CookieHeaderNames.HTTPONLY.equalsIgnoreCase(name)) {
123 httpOnly = true;
124 } else if (CookieHeaderNames.COMMENT.equalsIgnoreCase(name)) {
125 comment = value;
126 } else if (CookieHeaderNames.COMMENTURL.equalsIgnoreCase(name)) {
127 commentURL = value;
128 } else if (CookieHeaderNames.DOMAIN.equalsIgnoreCase(name)) {
129 domain = value;
130 } else if (CookieHeaderNames.PATH.equalsIgnoreCase(name)) {
131 path = value;
132 } else if (CookieHeaderNames.EXPIRES.equalsIgnoreCase(name)) {
133 try {
134 long maxAgeMillis =
135 HttpHeaderDateFormat.get().parse(value).getTime() -
136 System.currentTimeMillis();
137
138 maxAge = (int) (maxAgeMillis / 1000) +
139 (maxAgeMillis % 1000 != 0? 1 : 0);
140
141 } catch (ParseException e) {
142
143 }
144 } else if (CookieHeaderNames.MAX_AGE.equalsIgnoreCase(name)) {
145 maxAge = Integer.parseInt(value);
146 } else if (CookieHeaderNames.VERSION.equalsIgnoreCase(name)) {
147 version = Integer.parseInt(value);
148 } else if (CookieHeaderNames.PORT.equalsIgnoreCase(name)) {
149 String[] portList = StringUtil.split(value, COMMA);
150 for (String s1: portList) {
151 try {
152 ports.add(Integer.valueOf(s1));
153 } catch (NumberFormatException e) {
154
155 }
156 }
157 } else {
158 break;
159 }
160 }
161
162 c.setVersion(version);
163 c.setMaxAge(maxAge);
164 c.setPath(path);
165 c.setDomain(domain);
166 c.setSecure(secure);
167 c.setHttpOnly(httpOnly);
168 if (version > 0) {
169 c.setComment(comment);
170 }
171 if (version > 1) {
172 c.setCommentUrl(commentURL);
173 c.setPorts(ports);
174 c.setDiscard(discard);
175 }
176
177 cookies.add(c);
178 }
179
180 return cookies;
181 }
182
183 private static void extractKeyValuePairs(
184 final String header, final List<String> names, final List<String> values) {
185
186 final int headerLen = header.length();
187 loop: for (int i = 0;;) {
188
189
190 for (;;) {
191 if (i == headerLen) {
192 break loop;
193 }
194 switch (header.charAt(i)) {
195 case '\t': case '\n': case 0x0b: case '\f': case '\r':
196 case ' ': case ',': case ';':
197 i ++;
198 continue;
199 }
200 break;
201 }
202
203
204 for (;;) {
205 if (i == headerLen) {
206 break loop;
207 }
208 if (header.charAt(i) == '$') {
209 i ++;
210 continue;
211 }
212 break;
213 }
214
215 String name;
216 String value;
217
218 if (i == headerLen) {
219 name = null;
220 value = null;
221 } else {
222 int newNameStart = i;
223 keyValLoop: for (;;) {
224 switch (header.charAt(i)) {
225 case ';':
226
227 name = header.substring(newNameStart, i);
228 value = null;
229 break keyValLoop;
230 case '=':
231
232 name = header.substring(newNameStart, i);
233 i ++;
234 if (i == headerLen) {
235
236 value = "";
237 break keyValLoop;
238 }
239
240 int newValueStart = i;
241 char c = header.charAt(i);
242 if (c == '"' || c == '\'') {
243
244 StringBuilder newValueBuf = new StringBuilder(header.length() - i);
245 final char q = c;
246 boolean hadBackslash = false;
247 i ++;
248 for (;;) {
249 if (i == headerLen) {
250 value = newValueBuf.toString();
251 break keyValLoop;
252 }
253 if (hadBackslash) {
254 hadBackslash = false;
255 c = header.charAt(i ++);
256 switch (c) {
257 case '\\': case '"': case '\'':
258
259 newValueBuf.setCharAt(newValueBuf.length() - 1, c);
260 break;
261 default:
262
263 newValueBuf.append(c);
264 }
265 } else {
266 c = header.charAt(i ++);
267 if (c == q) {
268 value = newValueBuf.toString();
269 break keyValLoop;
270 }
271 newValueBuf.append(c);
272 if (c == '\\') {
273 hadBackslash = true;
274 }
275 }
276 }
277 } else {
278
279 int semiPos = header.indexOf(';', i);
280 if (semiPos > 0) {
281 value = header.substring(newValueStart, semiPos);
282 i = semiPos;
283 } else {
284 value = header.substring(newValueStart);
285 i = headerLen;
286 }
287 }
288 break keyValLoop;
289 default:
290 i ++;
291 }
292
293 if (i == headerLen) {
294
295 name = header.substring(newNameStart);
296 value = null;
297 break;
298 }
299 }
300 }
301
302 names.add(name);
303 values.add(value);
304 }
305 }
306 }