    1   /*
    2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/HttpParser.java,v 1.13 2005/01/11 13:57:06 oglueck Exp $
    3    * $Revision: 5562 $
    4    * $Date: 2007-11-16 00:53:10 +0000 (Fri, 16 Nov 2007) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Copyright 1999-2004 The Apache Software Foundation
    9    *
    10   *  Licensed under the Apache License, Version 2.0 (the "License");
    11   *  you may not use this file except in compliance with the License.
    12   *  You may obtain a copy of the License at
    13   *
    14   *      http://www.apache.org/licenses/LICENSE-2.0
    15   *
    16   *  Unless required by applicable law or agreed to in writing, software
    17   *  distributed under the License is distributed on an "AS IS" BASIS,
    18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    19   *  See the License for the specific language governing permissions and
    20   *  limitations under the License.
    21   * ====================================================================
    22   *
    23   * This software consists of voluntary contributions made by many
    24   * individuals on behalf of the Apache Software Foundation.  For more
    25   * information on the Apache Software Foundation, please see
    26   * <http://www.apache.org/>.
    27   *
    28   */
    29
    30  package org.apache.commons.httpclient;
    31
    32  import java.io.IOException;
    33  import java.io.InputStream;
    34  import java.io.ByteArrayOutputStream;
    35  import java.util.ArrayList;
    36
    37  import org.apache.commons.httpclient.util.EncodingUtil;
    38  import org.apache.commons.logging.Log;
    39  import org.apache.commons.logging.LogFactory;
    40
    41  /***
    42   * A utility class for parsing http header values according to
    43   * RFC-2616 Section 4 and 19.3.
    44   *
    45   * @author Michael Becke
    46   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
    47   *
    48   * @since 2.0beta1
    49   */
    50  @SuppressWarnings("unchecked") // <- IA/HERITRIX CHANGE
    51  public class HttpParser {
    52
    53      /*** Log object for this class. */
    54      private static final Log LOG = LogFactory.getLog(HttpParser.class);
    55
    56      /***
    57       * Constructor for HttpParser.
    58       */
    59      private HttpParser() { }
    60
    61      /***
    62       * Return byte array from an (unchunked) input stream.
    63       * Stop reading when <tt>"\n"</tt> terminator encountered
    64       * If the stream ends before the line terminator is found,
    65       * the last part of the string will still be returned.
    66       * If no input data available, <code>null</code> is returned.
    67       *
    68       * @param inputStream the stream to read from
    69       *
    70       * @throws IOException if an I/O problem occurs
    71       * @return a byte array from the stream
    72       */
    73      public static byte[] readRawLine(InputStream inputStream) throws IOException {
    74          LOG.trace("enter HttpParser.readRawLine()");
    75
    76          ByteArrayOutputStream buf = new ByteArrayOutputStream();
    77          int ch;
    78          while ((ch = inputStream.read()) >= 0) {
    79              buf.write(ch);
    80              if (ch == '\n') { // be tolerant (RFC-2616 Section 19.3)
    81                  break;
    82              }
    83          }
    84          if (buf.size() == 0) {
    85              return null;
    86          }
    87          return buf.toByteArray();
    88      }
    89
    90      /***
    91       * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
    92       * If the stream ends before the line terminator is found,
    93       * the last part of the string will still be returned.
    94       * If no input data available, <code>null</code> is returned.
    95       *
    96       * @param inputStream the stream to read from
    97       * @param charset charset of HTTP protocol elements
    98       *
    99       * @throws IOException if an I/O problem occurs
    100      * @return a line from the stream
    101      *
    102      * @since 3.0
    103      */
    104     public static String readLine(InputStream inputStream, String charset) throws IOException {
    105         LOG.trace("enter HttpParser.readLine(InputStream, String)");
    106         byte[] rawdata = readRawLine(inputStream);
    107         if (rawdata == null) {
    108             return null;
    109         }
    110         // strip CR and LF from the end
    111         int len = rawdata.length;
    112         int offset = 0;
    113         if (len > 0) {
    114             if (rawdata[len - 1] == '\n') {
    115                 offset++;
    116                 if (len > 1) {
    117                     if (rawdata[len - 2] == '\r') {
    118                         offset++;
    119                     }
    120                 }
    121             }
    122         }
    123         return EncodingUtil.getString(rawdata, 0, len - offset, charset);
    124     }
    125
    126     /***
    127      * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
    128      * If the stream ends before the line terminator is found,
    129      * the last part of the string will still be returned.
    130      * If no input data available, <code>null</code> is returned
    131      *
    132      * @param inputStream the stream to read from
    133      *
    134      * @throws IOException if an I/O problem occurs
    135      * @return a line from the stream
    136      *
    137      * @deprecated use #readLine(InputStream, String)
    138      */
    139
    140     public static String readLine(InputStream inputStream) throws IOException {
    141         LOG.trace("enter HttpParser.readLine(InputStream)");
    142         return readLine(inputStream, "US-ASCII");
    143     }
    144
    145     /***
    146      * Parses headers from the given stream.  Headers with the same name are not
    147      * combined.
    148      *
    149      * @param is the stream to read headers from
    150      * @param charset the charset to use for reading the data
    151      *
    152      * @return an array of headers in the order in which they were parsed
    153      *
    154      * @throws IOException if an IO error occurs while reading from the stream
    155      * @throws HttpException if there is an error parsing a header value
    156      *
    157      * @since 3.0
    158      */
    159     public static Header[] parseHeaders(InputStream is, String charset) throws IOException, HttpException {
    160         LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
    161
    162         ArrayList headers = new ArrayList();
    163         String name = null;
    164         StringBuffer value = null;
    165         for (; ;) {
    166             String line = HttpParser.readLine(is, charset);
    167             if ((line == null) || (line.trim().length() < 1)) {
    168                 break;
    169             }
    170
    171             // Parse the header name and value
    172             // Check for folded headers first
    173             // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
    174             // discussion on folded headers
    175             if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) {
    176                 // we have continuation folded header
    177                 // so append value
    178                 if (value != null) {
    179                     value.append(' ');
    180                     value.append(line.trim());
    181                 }
    182             } else {
    183                 // make sure we save the previous name,value pair if present
    184                 if (name != null) {
    185                     headers.add(new Header(name, value.toString()));
    186                 }
    187
    188                 // Otherwise we should have normal HTTP header line
    189                 // Parse the header name and value
    190                 int colon = line.indexOf(":");
    191
    192                 // START IA/HERITRIX change
    193                 // Don't throw an exception if can't parse.  We want to keep
    194                 // going even though header is bad. Rather, create
    195                 // pseudo-header.
    196                 if (colon < 0) {
    197                     // throw new ProtocolException("Unable to parse header: " +
    198                     //      line);
    199                     name = "HttpClient-Bad-Header-Line-Failed-Parse";
    200                     value = new StringBuffer(line);
    201
    202                 } else {
    203                 name = line.substring(0, colon).trim();
    204                 value = new StringBuffer(line.substring(colon + 1).trim());
    205                 }
    206                 // END IA/HERITRIX change
    207             }
    208
    209         }
    210
    211         // make sure we save the last name,value pair if present
    212         if (name != null) {
    213             headers.add(new Header(name, value.toString()));
    214         }
    215
    216         return (Header[]) headers.toArray(new Header[headers.size()]);
    217     }
    218
    219     /***
    220      * Parses headers from the given stream.  Headers with the same name are not
    221      * combined.
    222      *
    223      * @param is the stream to read headers from
    224      *
    225      * @return an array of headers in the order in which they were parsed
    226      *
    227      * @throws IOException if an IO error occurs while reading from the stream
    228      * @throws HttpException if there is an error parsing a header value
    229      *
    230      * @deprecated use #parseHeaders(InputStream, String)
    231      */
    232     public static Header[] parseHeaders(InputStream is) throws IOException, HttpException {
    233         LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
    234         return parseHeaders(is, "US-ASCII");
    235     }
    236 }