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 new CookieDateFormat().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
184 private static void extractKeyValuePairs(
185 final String header, final List<String> names, final List<String> values) {
186
187 final int headerLen = header.length();
188 loop: for (int i = 0;;) {
189
190
191 for (;;) {
192 if (i == headerLen) {
193 break loop;
194 }
195 switch (header.charAt(i)) {
196 case '\t': case '\n': case 0x0b: case '\f': case '\r':
197 case ' ': case ',': case ';':
198 i ++;
199 continue;
200 }
201 break;
202 }
203
204
205 for (;;) {
206 if (i == headerLen) {
207 break loop;
208 }
209 if (header.charAt(i) == '$') {
210 i ++;
211 continue;
212 }
213 break;
214 }
215
216 String name;
217 String value;
218
219 if (i == headerLen) {
220 name = null;
221 value = null;
222 } else {
223 int newNameStart = i;
224 keyValLoop: for (;;) {
225 switch (header.charAt(i)) {
226 case ';':
227
228 name = header.substring(newNameStart, i);
229 value = null;
230 break keyValLoop;
231 case '=':
232
233 name = header.substring(newNameStart, i);
234 i ++;
235 if (i == headerLen) {
236
237 value = "";
238 break keyValLoop;
239 }
240
241 int newValueStart = i;
242 char c = header.charAt(i);
243 if (c == '"' || c == '\'') {
244
245 StringBuilder newValueBuf = new StringBuilder(header.length() - i);
246 final char q = c;
247 boolean hadBackslash = false;
248 i ++;
249 for (;;) {
250 if (i == headerLen) {
251 value = newValueBuf.toString();
252 break keyValLoop;
253 }
254 if (hadBackslash) {
255 hadBackslash = false;
256 c = header.charAt(i ++);
257 switch (c) {
258 case '\\': case '"': case '\'':
259
260 newValueBuf.setCharAt(newValueBuf.length() - 1, c);
261 break;
262 default:
263
264 newValueBuf.append(c);
265 }
266 } else {
267 c = header.charAt(i ++);
268 if (c == q) {
269 value = newValueBuf.toString();
270 break keyValLoop;
271 }
272 newValueBuf.append(c);
273 if (c == '\\') {
274 hadBackslash = true;
275 }
276 }
277 }
278 } else {
279
280 int semiPos = header.indexOf(';', i);
281 if (semiPos > 0) {
282 value = header.substring(newValueStart, semiPos);
283 i = semiPos;
284 } else {
285 value = header.substring(newValueStart);
286 i = headerLen;
287 }
288 }
289 break keyValLoop;
290 default:
291 i ++;
292 }
293
294 if (i == headerLen) {
295
296 name = header.substring(newNameStart);
297 value = null;
298 break;
299 }
300 }
301 }
302
303 names.add(name);
304 values.add(value);
305 }
306 }
307 }