1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.cookie;
17
18 import static io.netty.util.internal.ObjectUtil.checkNotNull;
19
20 import io.netty.handler.codec.http.HttpHeaderDateFormat;
21
22 import java.text.ParsePosition;
23 import java.util.Date;
24
25
26
27
28
29
30
31
32
33 public final class ClientCookieDecoder extends CookieDecoder {
34
35
36
37
38
39 public static final ClientCookieDecoder STRICT = new ClientCookieDecoder(true);
40
41
42
43
44 public static final ClientCookieDecoder LAX = new ClientCookieDecoder(false);
45
46 private ClientCookieDecoder(boolean strict) {
47 super(strict);
48 }
49
50
51
52
53
54
55 public Cookie decode(String header) {
56 final int headerLen = checkNotNull(header, "header").length();
57
58 if (headerLen == 0) {
59 return null;
60 }
61
62 CookieBuilder cookieBuilder = null;
63
64 loop: for (int i = 0;;) {
65
66
67 for (;;) {
68 if (i == headerLen) {
69 break loop;
70 }
71 char c = header.charAt(i);
72 if (c == ',') {
73
74
75 break loop;
76
77 } else if (c == '\t' || c == '\n' || c == 0x0b || c == '\f'
78 || c == '\r' || c == ' ' || c == ';') {
79 i++;
80 continue;
81 }
82 break;
83 }
84
85 int nameBegin = i;
86 int nameEnd = i;
87 int valueBegin = -1;
88 int valueEnd = -1;
89
90 if (i != headerLen) {
91 keyValLoop: for (;;) {
92
93 char curChar = header.charAt(i);
94 if (curChar == ';') {
95
96 nameEnd = i;
97 valueBegin = valueEnd = -1;
98 break keyValLoop;
99
100 } else if (curChar == '=') {
101
102 nameEnd = i;
103 i++;
104 if (i == headerLen) {
105
106 valueBegin = valueEnd = 0;
107 break keyValLoop;
108 }
109
110 valueBegin = i;
111
112 int semiPos = header.indexOf(';', i);
113 valueEnd = i = semiPos > 0 ? semiPos : headerLen;
114 break keyValLoop;
115 } else {
116 i++;
117 }
118
119 if (i == headerLen) {
120
121 nameEnd = headerLen;
122 valueBegin = valueEnd = -1;
123 break;
124 }
125 }
126 }
127
128 if (valueEnd > 0 && header.charAt(valueEnd - 1) == ',') {
129
130 valueEnd--;
131 }
132
133 if (cookieBuilder == null) {
134
135 DefaultCookie cookie = initCookie(header, nameBegin, nameEnd, valueBegin, valueEnd);
136
137 if (cookie == null) {
138 return null;
139 }
140
141 cookieBuilder = new CookieBuilder(cookie, header);
142 } else {
143
144 cookieBuilder.appendAttribute(nameBegin, nameEnd, valueBegin, valueEnd);
145 }
146 }
147 return cookieBuilder.cookie();
148 }
149
150 private static class CookieBuilder {
151
152 private final String header;
153 private final DefaultCookie cookie;
154 private String domain;
155 private String path;
156 private long maxAge = Long.MIN_VALUE;
157 private int expiresStart;
158 private int expiresEnd;
159 private boolean secure;
160 private boolean httpOnly;
161
162 public CookieBuilder(DefaultCookie cookie, String header) {
163 this.cookie = cookie;
164 this.header = header;
165 }
166
167 private long mergeMaxAgeAndExpires() {
168
169 if (maxAge != Long.MIN_VALUE) {
170 return maxAge;
171 } else {
172 String expires = computeValue(expiresStart, expiresEnd);
173 if (expires != null) {
174 Date expiresDate = HttpHeaderDateFormat.get().parse(expires, new ParsePosition(0));
175 if (expiresDate != null) {
176 long maxAgeMillis = expiresDate.getTime() - System.currentTimeMillis();
177 return maxAgeMillis / 1000 + (maxAgeMillis % 1000 != 0 ? 1 : 0);
178 }
179 }
180 }
181 return Long.MIN_VALUE;
182 }
183
184 public Cookie cookie() {
185 cookie.setDomain(domain);
186 cookie.setPath(path);
187 cookie.setMaxAge(mergeMaxAgeAndExpires());
188 cookie.setSecure(secure);
189 cookie.setHttpOnly(httpOnly);
190 return cookie;
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206 public void appendAttribute(int keyStart, int keyEnd, int valueStart, int valueEnd) {
207 int length = keyEnd - keyStart;
208
209 if (length == 4) {
210 parse4(keyStart, valueStart, valueEnd);
211 } else if (length == 6) {
212 parse6(keyStart, valueStart, valueEnd);
213 } else if (length == 7) {
214 parse7(keyStart, valueStart, valueEnd);
215 } else if (length == 8) {
216 parse8(keyStart, valueStart, valueEnd);
217 }
218 }
219
220 private void parse4(int nameStart, int valueStart, int valueEnd) {
221 if (header.regionMatches(true, nameStart, CookieHeaderNames.PATH, 0, 4)) {
222 path = computeValue(valueStart, valueEnd);
223 }
224 }
225
226 private void parse6(int nameStart, int valueStart, int valueEnd) {
227 if (header.regionMatches(true, nameStart, CookieHeaderNames.DOMAIN, 0, 5)) {
228 domain = computeValue(valueStart, valueEnd);
229 } else if (header.regionMatches(true, nameStart, CookieHeaderNames.SECURE, 0, 5)) {
230 secure = true;
231 }
232 }
233
234 private void setMaxAge(String value) {
235 try {
236 maxAge = Math.max(Long.valueOf(value), 0L);
237 } catch (NumberFormatException e1) {
238
239 }
240 }
241
242 private void parse7(int nameStart, int valueStart, int valueEnd) {
243 if (header.regionMatches(true, nameStart, CookieHeaderNames.EXPIRES, 0, 7)) {
244 expiresStart = valueStart;
245 expiresEnd = valueEnd;
246 } else if (header.regionMatches(true, nameStart, CookieHeaderNames.MAX_AGE, 0, 7)) {
247 setMaxAge(computeValue(valueStart, valueEnd));
248 }
249 }
250
251 private void parse8(int nameStart, int valueStart, int valueEnd) {
252 if (header.regionMatches(true, nameStart, CookieHeaderNames.HTTPONLY, 0, 8)) {
253 httpOnly = true;
254 }
255 }
256
257 private String computeValue(int valueStart, int valueEnd) {
258 return valueStart == -1 || valueStart == valueEnd ? null : header.substring(valueStart, valueEnd);
259 }
260 }
261 }