001: /*
002: * $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 $
003: * $Revision: 533405 $
004: * $Date: 2007-04-28 20:19:29 +0200 (Sat, 28 Apr 2007) $
005: *
006: * ====================================================================
007: *
008: * Licensed to the Apache Software Foundation (ASF) under one or more
009: * contributor license agreements. See the NOTICE file distributed with
010: * this work for additional information regarding copyright ownership.
011: * The ASF licenses this file to You under the Apache License, Version 2.0
012: * (the "License"); you may not use this file except in compliance with
013: * the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing, software
018: * distributed under the License is distributed on an "AS IS" BASIS,
019: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
020: * See the License for the specific language governing permissions and
021: * limitations under the License.
022: * ====================================================================
023: *
024: * This software consists of voluntary contributions made by many
025: * individuals on behalf of the Apache Software Foundation. For more
026: * information on the Apache Software Foundation, please see
027: * <http://www.apache.org/>.
028: *
029: */
030:
031: package org.apache.commons.httpclient;
032:
033: import java.io.IOException;
034: import java.io.InputStream;
035: import java.io.ByteArrayOutputStream;
036: import java.util.ArrayList;
037:
038: import org.apache.commons.httpclient.util.EncodingUtil;
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041:
042: /**
043: * A utility class for parsing http header values according to
044: * RFC-2616 Section 4 and 19.3.
045: *
046: * @author Michael Becke
047: * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
048: *
049: * @since 2.0beta1
050: */
051: public class HttpParser {
052:
053: /** Log object for this class. */
054: private static final Log LOG = LogFactory.getLog(HttpParser.class);
055:
056: /**
057: * Constructor for HttpParser.
058: */
059: private HttpParser() {
060: }
061:
062: /**
063: * Return byte array from an (unchunked) input stream.
064: * Stop reading when <tt>"\n"</tt> terminator encountered
065: * If the stream ends before the line terminator is found,
066: * the last part of the string will still be returned.
067: * If no input data available, <code>null</code> is returned.
068: *
069: * @param inputStream the stream to read from
070: *
071: * @throws IOException if an I/O problem occurs
072: * @return a byte array from the stream
073: */
074: public static byte[] readRawLine(InputStream inputStream)
075: throws IOException {
076: LOG.trace("enter HttpParser.readRawLine()");
077:
078: ByteArrayOutputStream buf = new ByteArrayOutputStream();
079: int ch;
080: while ((ch = inputStream.read()) >= 0) {
081: buf.write(ch);
082: if (ch == '\n') { // be tolerant (RFC-2616 Section 19.3)
083: break;
084: }
085: }
086: if (buf.size() == 0) {
087: return null;
088: }
089: return buf.toByteArray();
090: }
091:
092: /**
093: * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
094: * If the stream ends before the line terminator is found,
095: * the last part of the string will still be returned.
096: * If no input data available, <code>null</code> is returned.
097: *
098: * @param inputStream the stream to read from
099: * @param charset charset of HTTP protocol elements
100: *
101: * @throws IOException if an I/O problem occurs
102: * @return a line from the stream
103: *
104: * @since 3.0
105: */
106: public static String readLine(InputStream inputStream,
107: String charset) throws IOException {
108: LOG.trace("enter HttpParser.readLine(InputStream, String)");
109: byte[] rawdata = readRawLine(inputStream);
110: if (rawdata == null) {
111: return null;
112: }
113: // strip CR and LF from the end
114: int len = rawdata.length;
115: int offset = 0;
116: if (len > 0) {
117: if (rawdata[len - 1] == '\n') {
118: offset++;
119: if (len > 1) {
120: if (rawdata[len - 2] == '\r') {
121: offset++;
122: }
123: }
124: }
125: }
126: final String result = EncodingUtil.getString(rawdata, 0, len
127: - offset, charset);
128: if (Wire.HEADER_WIRE.enabled()) {
129: String logoutput = result;
130: if (offset == 2)
131: logoutput = result + "\r\n";
132: else if (offset == 1)
133: logoutput = result + "\n";
134: Wire.HEADER_WIRE.input(logoutput);
135: }
136: return result;
137: }
138:
139: /**
140: * Read up to <tt>"\n"</tt> from an (unchunked) input stream.
141: * If the stream ends before the line terminator is found,
142: * the last part of the string will still be returned.
143: * If no input data available, <code>null</code> is returned
144: *
145: * @param inputStream the stream to read from
146: *
147: * @throws IOException if an I/O problem occurs
148: * @return a line from the stream
149: *
150: * @deprecated use #readLine(InputStream, String)
151: */
152:
153: public static String readLine(InputStream inputStream)
154: throws IOException {
155: LOG.trace("enter HttpParser.readLine(InputStream)");
156: return readLine(inputStream, "US-ASCII");
157: }
158:
159: /**
160: * Parses headers from the given stream. Headers with the same name are not
161: * combined.
162: *
163: * @param is the stream to read headers from
164: * @param charset the charset to use for reading the data
165: *
166: * @return an array of headers in the order in which they were parsed
167: *
168: * @throws IOException if an IO error occurs while reading from the stream
169: * @throws HttpException if there is an error parsing a header value
170: *
171: * @since 3.0
172: */
173: public static Header[] parseHeaders(InputStream is, String charset)
174: throws IOException, HttpException {
175: LOG
176: .trace("enter HeaderParser.parseHeaders(InputStream, String)");
177:
178: ArrayList headers = new ArrayList();
179: String name = null;
180: StringBuffer value = null;
181: for (;;) {
182: String line = HttpParser.readLine(is, charset);
183: if ((line == null) || (line.trim().length() < 1)) {
184: break;
185: }
186:
187: // Parse the header name and value
188: // Check for folded headers first
189: // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
190: // discussion on folded headers
191: if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) {
192: // we have continuation folded header
193: // so append value
194: if (value != null) {
195: value.append(' ');
196: value.append(line.trim());
197: }
198: } else {
199: // make sure we save the previous name,value pair if present
200: if (name != null) {
201: headers.add(new Header(name, value.toString()));
202: }
203:
204: // Otherwise we should have normal HTTP header line
205: // Parse the header name and value
206: int colon = line.indexOf(":");
207: if (colon < 0) {
208: throw new ProtocolException(
209: "Unable to parse header: " + line);
210: }
211: name = line.substring(0, colon).trim();
212: value = new StringBuffer(line.substring(colon + 1)
213: .trim());
214: }
215:
216: }
217:
218: // make sure we save the last name,value pair if present
219: if (name != null) {
220: headers.add(new Header(name, value.toString()));
221: }
222:
223: return (Header[]) headers.toArray(new Header[headers.size()]);
224: }
225:
226: /**
227: * Parses headers from the given stream. Headers with the same name are not
228: * combined.
229: *
230: * @param is the stream to read headers from
231: *
232: * @return an array of headers in the order in which they were parsed
233: *
234: * @throws IOException if an IO error occurs while reading from the stream
235: * @throws HttpException if there is an error parsing a header value
236: *
237: * @deprecated use #parseHeaders(InputStream, String)
238: */
239: public static Header[] parseHeaders(InputStream is)
240: throws IOException, HttpException {
241: LOG
242: .trace("enter HeaderParser.parseHeaders(InputStream, String)");
243: return parseHeaders(is, "US-ASCII");
244: }
245: }
|