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