View Javadoc

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: 179411 $
4    * $Date: 2005-06-01 16:04:58 -0400 (Wed, 01 Jun 2005) $
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  public class HttpParser {
51  
52      /*** Log object for this class. */
53      private static final Log LOG = LogFactory.getLog(HttpParser.class);
54      
55      /***
56       * Constructor for HttpParser.
57       */
58      private HttpParser() { }
59  
60      /***
61       * Return byte array from an (unchunked) input stream.
62       * Stop reading when <tt>"\n"</tt> terminator encountered 
63       * If the stream ends before the line terminator is found,
64       * the last part of the string will still be returned. 
65       * If no input data available, <code>null</code> is returned.
66       *
67       * @param inputStream the stream to read from
68       *
69       * @throws IOException if an I/O problem occurs
70       * @return a byte array from the stream
71       */
72      public static byte[] readRawLine(InputStream inputStream) throws IOException {
73          LOG.trace("enter HttpParser.readRawLine()");
74  
75          ByteArrayOutputStream buf = new ByteArrayOutputStream();
76          int ch;
77          while ((ch = inputStream.read()) >= 0) {
78              buf.write(ch);
79              if (ch == '\n') { // be tolerant (RFC-2616 Section 19.3)
80                  break;
81              }
82          }
83          if (buf.size() == 0) {
84              return null;
85          }
86          return buf.toByteArray();
87      }
88  
89      /***
90       * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
91       * If the stream ends before the line terminator is found,
92       * the last part of the string will still be returned.
93       * If no input data available, <code>null</code> is returned.
94       *
95       * @param inputStream the stream to read from
96       * @param charset charset of HTTP protocol elements
97       *
98       * @throws IOException if an I/O problem occurs
99       * @return a line from the stream
100      * 
101      * @since 3.0
102      */
103     public static String readLine(InputStream inputStream, String charset) throws IOException {
104         LOG.trace("enter HttpParser.readLine(InputStream, String)");
105         byte[] rawdata = readRawLine(inputStream);
106         if (rawdata == null) {
107             return null;
108         }
109         // strip CR and LF from the end
110         int len = rawdata.length;
111         int offset = 0;
112         if (len > 0) {
113             if (rawdata[len - 1] == '\n') {
114                 offset++;
115                 if (len > 1) {
116                     if (rawdata[len - 2] == '\r') {
117                         offset++;
118                     }
119                 }
120             }
121         }
122         return EncodingUtil.getString(rawdata, 0, len - offset, charset);
123     }
124 
125     /***
126      * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
127      * If the stream ends before the line terminator is found,
128      * the last part of the string will still be returned.
129      * If no input data available, <code>null</code> is returned
130      *
131      * @param inputStream the stream to read from
132      *
133      * @throws IOException if an I/O problem occurs
134      * @return a line from the stream
135      * 
136      * @deprecated use #readLine(InputStream, String)
137      */
138 
139     public static String readLine(InputStream inputStream) throws IOException {
140         LOG.trace("enter HttpParser.readLine(InputStream)");
141         return readLine(inputStream, "US-ASCII");
142     }
143     
144     /***
145      * Parses headers from the given stream.  Headers with the same name are not
146      * combined.
147      * 
148      * @param is the stream to read headers from
149      * @param charset the charset to use for reading the data
150      * 
151      * @return an array of headers in the order in which they were parsed
152      * 
153      * @throws IOException if an IO error occurs while reading from the stream
154      * @throws HttpException if there is an error parsing a header value
155      * 
156      * @since 3.0
157      */
158     public static Header[] parseHeaders(InputStream is, String charset) throws IOException, HttpException {
159         LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
160 
161         ArrayList headers = new ArrayList();
162         String name = null;
163         StringBuffer value = null;
164         for (; ;) {
165             String line = HttpParser.readLine(is, charset);
166             if ((line == null) || (line.trim().length() < 1)) {
167                 break;
168             }
169 
170             // Parse the header name and value
171             // Check for folded headers first
172             // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
173             // discussion on folded headers
174             if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) {
175                 // we have continuation folded header
176                 // so append value
177                 if (value != null) {
178                     value.append(' ');
179                     value.append(line.trim());
180                 }
181             } else {
182                 // make sure we save the previous name,value pair if present
183                 if (name != null) {
184                     headers.add(new Header(name, value.toString()));
185                 }
186 
187                 // Otherwise we should have normal HTTP header line
188                 // Parse the header name and value
189                 int colon = line.indexOf(":");
190                 if (colon < 0) {
191                     throw new ProtocolException("Unable to parse header: " + line);
192                 }
193                 name = line.substring(0, colon).trim();
194                 value = new StringBuffer(line.substring(colon + 1).trim());
195             }
196 
197         }
198 
199         // make sure we save the last name,value pair if present
200         if (name != null) {
201             headers.add(new Header(name, value.toString()));
202         }
203         
204         return (Header[]) headers.toArray(new Header[headers.size()]);    
205     }
206 
207     /***
208      * Parses headers from the given stream.  Headers with the same name are not
209      * combined.
210      * 
211      * @param is the stream to read headers from
212      * 
213      * @return an array of headers in the order in which they were parsed
214      * 
215      * @throws IOException if an IO error occurs while reading from the stream
216      * @throws HttpException if there is an error parsing a header value
217      * 
218      * @deprecated use #parseHeaders(InputStream, String)
219      */
220     public static Header[] parseHeaders(InputStream is) throws IOException, HttpException {
221         LOG.trace("enter HeaderParser.parseHeaders(InputStream, String)");
222         return parseHeaders(is, "US-ASCII");
223     }
224 }