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.

     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  */
    28 package ch.boye.httpclientandroidlib.message;
    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;
    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 {
    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();
    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;
    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     }
    90     /**
    91      * Creates a new line parser for HTTP.
    92      */
    93     public BasicLineParser() {
    94         this(null);
    95     }
    98     public final static
    99         ProtocolVersion parseProtocolVersion(String value,
   100                                              LineParser parser)
   101         throws ParseException {
   103         if (value == null) {
   104             throw new IllegalArgumentException
   105                 ("Value to parse may not be null.");
   106         }
   108         if (parser == null)
   109             parser = BasicLineParser.DEFAULT;
   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     }
   118     // non-javadoc, see interface LineParser
   119     public ProtocolVersion parseProtocolVersion(final CharArrayBuffer buffer,
   120                                                 final ParserCursor cursor)
   121         throws ParseException {
   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         }
   130         final String protoname = this.protocol.getProtocol();
   131         final int protolength  = protoname.length();
   133         int indexFrom = cursor.getPos();
   134         int indexTo = cursor.getUpperBound();
   136         skipWhitespace(buffer, cursor);
   138         int i = cursor.getPos();
   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         }
   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         }
   161         i += protolength+1;
   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;
   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         }
   192         cursor.updatePos(blank);
   194         return createProtocolVersion(major, minor);
   196     } // parseProtocolVersion
   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     }
   214     // non-javadoc, see interface LineParser
   215     public boolean hasProtocolVersion(final CharArrayBuffer buffer,
   216                                       final ParserCursor cursor) {
   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();
   226         final String protoname = this.protocol.getProtocol();
   227         final int  protolength = protoname.length();
   229         if (buffer.length() < protolength+4)
   230             return false; // not long enough for "HTTP/1.1"
   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
   245         if (index + protolength + 4 > buffer.length())
   246             return false;
   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         }
   258         return ok;
   259     }
   263     public final static
   264         RequestLine parseRequestLine(final String value,
   265                                      LineParser parser)
   266         throws ParseException {
   268         if (value == null) {
   269             throw new IllegalArgumentException
   270                 ("Value to parse may not be null.");
   271         }
   273         if (parser == null)
   274             parser = BasicLineParser.DEFAULT;
   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     }
   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 {
   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         }
   303         int indexFrom = cursor.getPos();
   304         int indexTo = cursor.getUpperBound();
   306         try {
   307             skipWhitespace(buffer, cursor);
   308             int i = cursor.getPos();
   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);
   318             skipWhitespace(buffer, cursor);
   319             i = cursor.getPos();
   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);
   329             ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
   331             skipWhitespace(buffer, cursor);
   332             if (!cursor.atEnd()) {
   333                 throw new ParseException("Invalid request line: " +
   334                         buffer.substring(indexFrom, indexTo));
   335             }
   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
   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     }
   363     public final static
   364         StatusLine parseStatusLine(final String value,
   365                                    LineParser parser)
   366         throws ParseException {
   368         if (value == null) {
   369             throw new IllegalArgumentException
   370                 ("Value to parse may not be null.");
   371         }
   373         if (parser == null)
   374             parser = BasicLineParser.DEFAULT;
   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     }
   383     // non-javadoc, see interface LineParser
   384     public StatusLine parseStatusLine(final CharArrayBuffer buffer,
   385                                       final ParserCursor cursor)
   386         throws ParseException {
   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         }
   395         int indexFrom = cursor.getPos();
   396         int indexTo = cursor.getUpperBound();
   398         try {
   399             // handle the HTTP-Version
   400             ProtocolVersion ver = parseProtocolVersion(buffer, cursor);
   402             // handle the Status-Code
   403             skipWhitespace(buffer, cursor);
   404             int i = cursor.getPos();
   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);
   436         } catch (IndexOutOfBoundsException e) {
   437             throw new ParseException("Invalid status line: " +
   438                                      buffer.substring(indexFrom, indexTo));
   439         }
   440     } // parseStatusLine
   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     }
   461     public final static
   462         Header parseHeader(final String value,
   463                            LineParser parser)
   464         throws ParseException {
   466         if (value == null) {
   467             throw new IllegalArgumentException
   468                 ("Value to parse may not be null");
   469         }
   471         if (parser == null)
   472             parser = BasicLineParser.DEFAULT;
   474         CharArrayBuffer buffer = new CharArrayBuffer(value.length());
   475         buffer.append(value);
   476         return parser.parseHeader(buffer);
   477     }
   480     // non-javadoc, see interface LineParser
   481     public Header parseHeader(CharArrayBuffer buffer)
   482         throws ParseException {
   484         // the actual parser code is in the constructor of BufferedHeader
   485         return new BufferedHeader(buffer);
   486     }
   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     }
   502 } // class BasicLineParser

mercurial