mobile/android/thirdparty/ch/boye/httpclientandroidlib/message/BasicLineParser.java

branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
equal deleted inserted replaced
-1:000000000000 0:ad46ff56a71b
1 /*
2 * ====================================================================
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package ch.boye.httpclientandroidlib.message;
29
30 import ch.boye.httpclientandroidlib.HttpVersion;
31 import ch.boye.httpclientandroidlib.ProtocolVersion;
32 import ch.boye.httpclientandroidlib.ParseException;
33 import ch.boye.httpclientandroidlib.RequestLine;
34 import ch.boye.httpclientandroidlib.StatusLine;
35 import ch.boye.httpclientandroidlib.Header;
36 import ch.boye.httpclientandroidlib.protocol.HTTP;
37 import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
38
39 /**
40 * Basic parser for lines in the head section of an HTTP message.
41 * There are individual methods for parsing a request line, a
42 * status line, or a header line.
43 * The lines to parse are passed in memory, the parser does not depend
44 * on any specific IO mechanism.
45 * Instances of this class are stateless and thread-safe.
46 * Derived classes MUST maintain these properties.
47 *
48 * <p>
49 * Note: This class was created by refactoring parsing code located in
50 * various other classes. The author tags from those other classes have
51 * been replicated here, although the association with the parsing code
52 * taken from there has not been traced.
53 * </p>
54 *
55 * @since 4.0
56 */
57 public class BasicLineParser implements LineParser {
58
59 /**
60 * A default instance of this class, for use as default or fallback.
61 * Note that {@link BasicLineParser} is not a singleton, there can
62 * be many instances of the class itself and of derived classes.
63 * The instance here provides non-customized, default behavior.
64 */
65 public final static BasicLineParser DEFAULT = new BasicLineParser();
66
67
68 /**
69 * A version of the protocol to parse.
70 * The version is typically not relevant, but the protocol name.
71 */
72 protected final ProtocolVersion protocol;
73
74
75 /**
76 * Creates a new line parser for the given HTTP-like protocol.
77 *
78 * @param proto a version of the protocol to parse, or
79 * <code>null</code> for HTTP. The actual version
80 * is not relevant, only the protocol name.
81 */
82 public BasicLineParser(ProtocolVersion proto) {
83 if (proto == null) {
84 proto = HttpVersion.HTTP_1_1;
85 }
86 this.protocol = proto;
87 }
88
89
90 /**
91 * Creates a new line parser for HTTP.
92 */
93 public BasicLineParser() {
94 this(null);
95 }
96
97
98 public final static
99 ProtocolVersion parseProtocolVersion(String value,
100 LineParser parser)
101 throws ParseException {
102
103 if (value == null) {
104 throw new IllegalArgumentException
105 ("Value to parse may not be null.");
106 }
107
108 if (parser == null)
109 parser = BasicLineParser.DEFAULT;
110
111 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
112 buffer.append(value);
113 ParserCursor cursor = new ParserCursor(0, value.length());
114 return parser.parseProtocolVersion(buffer, cursor);
115 }
116
117
118 // non-javadoc, see interface LineParser
119 public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer,
120 final ParserCursor cursor)
121 throws ParseException {
122
123 if (buffer == null) {
124 throw new IllegalArgumentException("Char array buffer may not be null");
125 }
126 if (cursor == null) {
127 throw new IllegalArgumentException("Parser cursor may not be null");
128 }
129
130 final String protoname = this.protocol.getProtocol();
131 final int protolength = protoname.length();
132
133 int indexFrom = cursor.getPos();
134 int indexTo = cursor.getUpperBound();
135
136 skipWhitespace(buffer, cursor);
137
138 int i = cursor.getPos();
139
140 // long enough for "HTTP/1.1"?
141 if (i + protolength + 4 > indexTo) {
142 throw new ParseException
143 ("Not a valid protocol version: " +
144 buffer.substring(indexFrom, indexTo));
145 }
146
147 // check the protocol name and slash
148 boolean ok = true;
149 for (int j=0; ok && (j<protolength); j++) {
150 ok = (buffer.charAt(i+j) == protoname.charAt(j));
151 }
152 if (ok) {
153 ok = (buffer.charAt(i+protolength) == '/');
154 }
155 if (!ok) {
156 throw new ParseException
157 ("Not a valid protocol version: " +
158 buffer.substring(indexFrom, indexTo));
159 }
160
161 i += protolength+1;
162
163 int period = buffer.indexOf('.', i, indexTo);
164 if (period == -1) {
165 throw new ParseException
166 ("Invalid protocol version number: " +
167 buffer.substring(indexFrom, indexTo));
168 }
169 int major;
170 try {
171 major = Integer.parseInt(buffer.substringTrimmed(i, period));
172 } catch (NumberFormatException e) {
173 throw new ParseException
174 ("Invalid protocol major version number: " +
175 buffer.substring(indexFrom, indexTo));
176 }
177 i = period + 1;
178
179 int blank = buffer.indexOf(' ', i, indexTo);
180 if (blank == -1) {
181 blank = indexTo;
182 }
183 int minor;
184 try {
185 minor = Integer.parseInt(buffer.substringTrimmed(i, blank));
186 } catch (NumberFormatException e) {
187 throw new ParseException(
188 "Invalid protocol minor version number: " +
189 buffer.substring(indexFrom, indexTo));
190 }
191
192 cursor.updatePos(blank);
193
194 return createProtocolVersion(major, minor);
195
196 } // parseProtocolVersion
197
198
199 /**
200 * Creates a protocol version.
201 * Called from {@link #parseProtocolVersion}.
202 *
203 * @param major the major version number, for example 1 in HTTP/1.0
204 * @param minor the minor version number, for example 0 in HTTP/1.0
205 *
206 * @return the protocol version
207 */
208 protected ProtocolVersion createProtocolVersion(int major, int minor) {
209 return protocol.forVersion(major, minor);
210 }
211
212
213
214 // non-javadoc, see interface LineParser
215 public boolean hasProtocolVersion(final CharArrayBuffer buffer,
216 final ParserCursor cursor) {
217
218 if (buffer == null) {
219 throw new IllegalArgumentException("Char array buffer may not be null");
220 }
221 if (cursor == null) {
222 throw new IllegalArgumentException("Parser cursor may not be null");
223 }
224 int index = cursor.getPos();
225
226 final String protoname = this.protocol.getProtocol();
227 final int protolength = protoname.length();
228
229 if (buffer.length() < protolength+4)
230 return false; // not long enough for "HTTP/1.1"
231
232 if (index < 0) {
233 // end of line, no tolerance for trailing whitespace
234 // this works only for single-digit major and minor version
235 index = buffer.length() -4 -protolength;
236 } else if (index == 0) {
237 // beginning of line, tolerate leading whitespace
238 while ((index < buffer.length()) &&
239 HTTP.isWhitespace(buffer.charAt(index))) {
240 index++;
241 }
242 } // else within line, don't tolerate whitespace
243
244
245 if (index + protolength + 4 > buffer.length())
246 return false;
247
248
249 // just check protocol name and slash, no need to analyse the version
250 boolean ok = true;
251 for (int j=0; ok && (j<protolength); j++) {
252 ok = (buffer.charAt(index+j) == protoname.charAt(j));
253 }
254 if (ok) {
255 ok = (buffer.charAt(index+protolength) == '/');
256 }
257
258 return ok;
259 }
260
261
262
263 public final static
264 RequestLine parseRequestLine(final String value,
265 LineParser parser)
266 throws ParseException {
267
268 if (value == null) {
269 throw new IllegalArgumentException
270 ("Value to parse may not be null.");
271 }
272
273 if (parser == null)
274 parser = BasicLineParser.DEFAULT;
275
276 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
277 buffer.append(value);
278 ParserCursor cursor = new ParserCursor(0, value.length());
279 return parser.parseRequestLine(buffer, cursor);
280 }
281
282
283 /**
284 * Parses a request line.
285 *
286 * @param buffer a buffer holding the line to parse
287 *
288 * @return the parsed request line
289 *
290 * @throws ParseException in case of a parse error
291 */
292 public RequestLine parseRequestLine(final CharArrayBuffer buffer,
293 final ParserCursor cursor)
294 throws ParseException {
295
296 if (buffer == null) {
297 throw new IllegalArgumentException("Char array buffer may not be null");
298 }
299 if (cursor == null) {
300 throw new IllegalArgumentException("Parser cursor may not be null");
301 }
302
303 int indexFrom = cursor.getPos();
304 int indexTo = cursor.getUpperBound();
305
306 try {
307 skipWhitespace(buffer, cursor);
308 int i = cursor.getPos();
309
310 int blank = buffer.indexOf(' ', i, indexTo);
311 if (blank < 0) {
312 throw new ParseException("Invalid request line: " +
313 buffer.substring(indexFrom, indexTo));
314 }
315 String method = buffer.substringTrimmed(i, blank);
316 cursor.updatePos(blank);
317
318 skipWhitespace(buffer, cursor);
319 i = cursor.getPos();
320
321 blank = buffer.indexOf(' ', i, indexTo);
322 if (blank < 0) {
323 throw new ParseException("Invalid request line: " +
324 buffer.substring(indexFrom, indexTo));
325 }
326 String uri = buffer.substringTrimmed(i, blank);
327 cursor.updatePos(blank);
328
329 ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
330
331 skipWhitespace(buffer, cursor);
332 if (!cursor.atEnd()) {
333 throw new ParseException("Invalid request line: " +
334 buffer.substring(indexFrom, indexTo));
335 }
336
337 return createRequestLine(method, uri, ver);
338 } catch (IndexOutOfBoundsException e) {
339 throw new ParseException("Invalid request line: " +
340 buffer.substring(indexFrom, indexTo));
341 }
342 } // parseRequestLine
343
344
345 /**
346 * Instantiates a new request line.
347 * Called from {@link #parseRequestLine}.
348 *
349 * @param method the request method
350 * @param uri the requested URI
351 * @param ver the protocol version
352 *
353 * @return a new status line with the given data
354 */
355 protected RequestLine createRequestLine(final String method,
356 final String uri,
357 final ProtocolVersion ver) {
358 return new BasicRequestLine(method, uri, ver);
359 }
360
361
362
363 public final static
364 StatusLine parseStatusLine(final String value,
365 LineParser parser)
366 throws ParseException {
367
368 if (value == null) {
369 throw new IllegalArgumentException
370 ("Value to parse may not be null.");
371 }
372
373 if (parser == null)
374 parser = BasicLineParser.DEFAULT;
375
376 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
377 buffer.append(value);
378 ParserCursor cursor = new ParserCursor(0, value.length());
379 return parser.parseStatusLine(buffer, cursor);
380 }
381
382
383 // non-javadoc, see interface LineParser
384 public StatusLine parseStatusLine(final CharArrayBuffer buffer,
385 final ParserCursor cursor)
386 throws ParseException {
387
388 if (buffer == null) {
389 throw new IllegalArgumentException("Char array buffer may not be null");
390 }
391 if (cursor == null) {
392 throw new IllegalArgumentException("Parser cursor may not be null");
393 }
394
395 int indexFrom = cursor.getPos();
396 int indexTo = cursor.getUpperBound();
397
398 try {
399 // handle the HTTP-Version
400 ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
401
402 // handle the Status-Code
403 skipWhitespace(buffer, cursor);
404 int i = cursor.getPos();
405
406 int blank = buffer.indexOf(' ', i, indexTo);
407 if (blank < 0) {
408 blank = indexTo;
409 }
410 int statusCode = 0;
411 String s = buffer.substringTrimmed(i, blank);
412 for (int j = 0; j < s.length(); j++) {
413 if (!Character.isDigit(s.charAt(j))) {
414 throw new ParseException(
415 "Status line contains invalid status code: "
416 + buffer.substring(indexFrom, indexTo));
417 }
418 }
419 try {
420 statusCode = Integer.parseInt(s);
421 } catch (NumberFormatException e) {
422 throw new ParseException(
423 "Status line contains invalid status code: "
424 + buffer.substring(indexFrom, indexTo));
425 }
426 //handle the Reason-Phrase
427 i = blank;
428 String reasonPhrase = null;
429 if (i < indexTo) {
430 reasonPhrase = buffer.substringTrimmed(i, indexTo);
431 } else {
432 reasonPhrase = "";
433 }
434 return createStatusLine(ver, statusCode, reasonPhrase);
435
436 } catch (IndexOutOfBoundsException e) {
437 throw new ParseException("Invalid status line: " +
438 buffer.substring(indexFrom, indexTo));
439 }
440 } // parseStatusLine
441
442
443 /**
444 * Instantiates a new status line.
445 * Called from {@link #parseStatusLine}.
446 *
447 * @param ver the protocol version
448 * @param status the status code
449 * @param reason the reason phrase
450 *
451 * @return a new status line with the given data
452 */
453 protected StatusLine createStatusLine(final ProtocolVersion ver,
454 final int status,
455 final String reason) {
456 return new BasicStatusLine(ver, status, reason);
457 }
458
459
460
461 public final static
462 Header parseHeader(final String value,
463 LineParser parser)
464 throws ParseException {
465
466 if (value == null) {
467 throw new IllegalArgumentException
468 ("Value to parse may not be null");
469 }
470
471 if (parser == null)
472 parser = BasicLineParser.DEFAULT;
473
474 CharArrayBuffer buffer = new CharArrayBuffer(value.length());
475 buffer.append(value);
476 return parser.parseHeader(buffer);
477 }
478
479
480 // non-javadoc, see interface LineParser
481 public Header parseHeader(CharArrayBuffer buffer)
482 throws ParseException {
483
484 // the actual parser code is in the constructor of BufferedHeader
485 return new BufferedHeader(buffer);
486 }
487
488
489 /**
490 * Helper to skip whitespace.
491 */
492 protected void skipWhitespace(final CharArrayBuffer buffer, final ParserCursor cursor) {
493 int pos = cursor.getPos();
494 int indexTo = cursor.getUpperBound();
495 while ((pos < indexTo) &&
496 HTTP.isWhitespace(buffer.charAt(pos))) {
497 pos++;
498 }
499 cursor.updatePos(pos);
500 }
501
502 } // class BasicLineParser

mercurial