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

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial