View Javadoc

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