1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.http.impl.client.cache;
28
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.List;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import org.apache.http.Header;
36 import org.apache.http.client.utils.DateUtils;
37
38
39
40
41
42
43 class WarningValue {
44
45 private int offs;
46 private int init_offs;
47 private final String src;
48 private int warnCode;
49 private String warnAgent;
50 private String warnText;
51 private Date warnDate;
52
53 WarningValue(final String s) {
54 this(s, 0);
55 }
56
57 WarningValue(final String s, final int offs) {
58 this.offs = this.init_offs = offs;
59 this.src = s;
60 consumeWarnValue();
61 }
62
63
64
65
66
67
68
69
70
71 public static WarningValue[] getWarningValues(final Header h) {
72 final List<WarningValue> out = new ArrayList<WarningValue>();
73 final String src = h.getValue();
74 int offs = 0;
75 while(offs < src.length()) {
76 try {
77 final WarningValueent/cache/WarningValue.html#WarningValue">WarningValue wv = new WarningValue(src, offs);
78 out.add(wv);
79 offs = wv.offs;
80 } catch (final IllegalArgumentException e) {
81 final int nextComma = src.indexOf(',', offs);
82 if (nextComma == -1) {
83 break;
84 }
85 offs = nextComma + 1;
86 }
87 }
88 final WarningValue[] wvs = {};
89 return out.toArray(wvs);
90 }
91
92
93
94
95
96 protected void consumeLinearWhitespace() {
97 while(offs < src.length()) {
98 switch(src.charAt(offs)) {
99 case '\r':
100 if (offs+2 >= src.length()
101 || src.charAt(offs+1) != '\n'
102 || (src.charAt(offs+2) != ' '
103 && src.charAt(offs+2) != '\t')) {
104 return;
105 }
106 offs += 2;
107 break;
108 case ' ':
109 case '\t':
110 break;
111 default:
112 return;
113 }
114 offs++;
115 }
116 }
117
118
119
120
121 private boolean isChar(final char c) {
122 final int i = c;
123 return (i >= 0 && i <= 127);
124 }
125
126
127
128
129
130 private boolean isControl(final char c) {
131 final int i = c;
132 return (i == 127 || (i >=0 && i <= 31));
133 }
134
135
136
137
138
139
140
141 private boolean isSeparator(final char c) {
142 return (c == '(' || c == ')' || c == '<' || c == '>'
143 || c == '@' || c == ',' || c == ';' || c == ':'
144 || c == '\\' || c == '\"' || c == '/'
145 || c == '[' || c == ']' || c == '?' || c == '='
146 || c == '{' || c == '}' || c == ' ' || c == '\t');
147 }
148
149
150
151
152 protected void consumeToken() {
153 if (!isTokenChar(src.charAt(offs))) {
154 parseError();
155 }
156 while(offs < src.length()) {
157 if (!isTokenChar(src.charAt(offs))) {
158 break;
159 }
160 offs++;
161 }
162 }
163
164 private boolean isTokenChar(final char c) {
165 return (isChar(c) && !isControl(c) && !isSeparator(c));
166 }
167
168 private static final String TOPLABEL = "\\p{Alpha}([\\p{Alnum}-]*\\p{Alnum})?";
169 private static final String DOMAINLABEL = "\\p{Alnum}([\\p{Alnum}-]*\\p{Alnum})?";
170 private static final String HOSTNAME = "(" + DOMAINLABEL + "\\.)*" + TOPLABEL + "\\.?";
171 private static final String IPV4ADDRESS = "\\d+\\.\\d+\\.\\d+\\.\\d+";
172 private static final String HOST = "(" + HOSTNAME + ")|(" + IPV4ADDRESS + ")";
173 private static final String PORT = "\\d*";
174 private static final String HOSTPORT = "(" + HOST + ")(\\:" + PORT + ")?";
175 private static final Pattern HOSTPORT_PATTERN = Pattern.compile(HOSTPORT);
176
177 protected void consumeHostPort() {
178 final Matcher m = HOSTPORT_PATTERN.matcher(src.substring(offs));
179 if (!m.find()) {
180 parseError();
181 }
182 if (m.start() != 0) {
183 parseError();
184 }
185 offs += m.end();
186 }
187
188
189
190
191
192
193 protected void consumeWarnAgent() {
194 final int curr_offs = offs;
195 try {
196 consumeHostPort();
197 warnAgent = src.substring(curr_offs, offs);
198 consumeCharacter(' ');
199 return;
200 } catch (final IllegalArgumentException e) {
201 offs = curr_offs;
202 }
203 consumeToken();
204 warnAgent = src.substring(curr_offs, offs);
205 consumeCharacter(' ');
206 }
207
208
209
210
211
212 protected void consumeQuotedString() {
213 if (src.charAt(offs) != '\"') {
214 parseError();
215 }
216 offs++;
217 boolean foundEnd = false;
218 while(offs < src.length() && !foundEnd) {
219 final char c = src.charAt(offs);
220 if (offs + 1 < src.length() && c == '\\'
221 && isChar(src.charAt(offs+1))) {
222 offs += 2;
223 } else if (c == '\"') {
224 foundEnd = true;
225 offs++;
226 } else if (c != '\"' && !isControl(c)) {
227 offs++;
228 } else {
229 parseError();
230 }
231 }
232 if (!foundEnd) {
233 parseError();
234 }
235 }
236
237
238
239
240 protected void consumeWarnText() {
241 final int curr = offs;
242 consumeQuotedString();
243 warnText = src.substring(curr, offs);
244 }
245
246 private static final String MONTH = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
247 private static final String WEEKDAY = "Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday";
248 private static final String WKDAY = "Mon|Tue|Wed|Thu|Fri|Sat|Sun";
249 private static final String TIME = "\\d{2}:\\d{2}:\\d{2}";
250 private static final String DATE3 = "(" + MONTH + ") ( |\\d)\\d";
251 private static final String DATE2 = "\\d{2}-(" + MONTH + ")-\\d{2}";
252 private static final String DATE1 = "\\d{2} (" + MONTH + ") \\d{4}";
253 private static final String ASCTIME_DATE = "(" + WKDAY + ") (" + DATE3 + ") (" + TIME + ") \\d{4}";
254 private static final String RFC850_DATE = "(" + WEEKDAY + "), (" + DATE2 + ") (" + TIME + ") GMT";
255 private static final String RFC1123_DATE = "(" + WKDAY + "), (" + DATE1 + ") (" + TIME + ") GMT";
256 private static final String HTTP_DATE = "(" + RFC1123_DATE + ")|(" + RFC850_DATE + ")|(" + ASCTIME_DATE + ")";
257 private static final String WARN_DATE = "\"(" + HTTP_DATE + ")\"";
258 private static final Pattern WARN_DATE_PATTERN = Pattern.compile(WARN_DATE);
259
260
261
262
263 protected void consumeWarnDate() {
264 final int curr = offs;
265 final Matcher m = WARN_DATE_PATTERN.matcher(src.substring(offs));
266 if (!m.lookingAt()) {
267 parseError();
268 }
269 offs += m.end();
270 warnDate = DateUtils.parseDate(src.substring(curr+1,offs-1));
271 }
272
273
274
275
276 protected void consumeWarnValue() {
277 consumeLinearWhitespace();
278 consumeWarnCode();
279 consumeWarnAgent();
280 consumeWarnText();
281 if (offs + 1 < src.length() && src.charAt(offs) == ' ' && src.charAt(offs+1) == '\"') {
282 consumeCharacter(' ');
283 consumeWarnDate();
284 }
285 consumeLinearWhitespace();
286 if (offs != src.length()) {
287 consumeCharacter(',');
288 }
289 }
290
291 protected void consumeCharacter(final char c) {
292 if (offs + 1 > src.length()
293 || c != src.charAt(offs)) {
294 parseError();
295 }
296 offs++;
297 }
298
299
300
301
302 protected void consumeWarnCode() {
303 if (offs + 4 > src.length()
304 || !Character.isDigit(src.charAt(offs))
305 || !Character.isDigit(src.charAt(offs + 1))
306 || !Character.isDigit(src.charAt(offs + 2))
307 || src.charAt(offs + 3) != ' ') {
308 parseError();
309 }
310 warnCode = Integer.parseInt(src.substring(offs,offs+3));
311 offs += 4;
312 }
313
314 private void parseError() {
315 final String s = src.substring(init_offs);
316 throw new IllegalArgumentException("Bad warn code \"" + s + "\"");
317 }
318
319
320
321
322 public int getWarnCode() { return warnCode; }
323
324
325
326
327
328
329 public String getWarnAgent() { return warnAgent; }
330
331
332
333
334
335
336
337
338
339
340
341
342 public String getWarnText() { return warnText; }
343
344
345
346
347
348
349 public Date getWarnDate() { return warnDate; }
350
351
352
353
354
355
356
357
358
359
360 @Override
361 public String toString() {
362 if (warnDate != null) {
363 return String.format("%d %s %s \"%s\"", Integer.valueOf(warnCode),
364 warnAgent, warnText, DateUtils.formatDate(warnDate));
365 } else {
366 return String.format("%d %s %s", Integer.valueOf(warnCode), warnAgent, warnText);
367 }
368 }
369
370 }