Index: PickyHttpParser.java
===================================================================
RCS file: PickyHttpParser.java
diff -N PickyHttpParser.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ PickyHttpParser.java 12 Dec 2003 12:58:34 -0000
@@ -0,0 +1,253 @@
+/*
+ * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpParser.java,v 1.8 2003/07/15 02:19:58 mbecke Exp $
+ * $Revision: 1.8 $
+ * $Date: 2003/07/15 02:19:58 $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ * [Additional notices, if required by prior licensing conditions]
+ *
+ */
+
+package org.apache.commons.httpclient.contrib.utils;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpConstants;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.ProtocolException;
+import org.apache.commons.httpclient.util.HttpParser;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A utility class for parsing http header values.
+ *
+ * This implementation differs from {@link DefaultHttpParser}
+ * in treating unusually long response headers.
+ *
+ * The maximum line length is 4096 bytes/8-bit-characters.
+ * The maximum number of headers is 1000.
+ * The maximum header value length is 4096 characters.
+ *
+ * A HttpException is thrown if at least one of these limits is exceeded.
+ *
+ * @author Michael Becke
+ * @author Oleg Kalnichevski
+ * @author Christian Kohlschuetter
+ */
+public class PickyHttpParser implements HttpParser {
+ /** Singleton handling */
+ private static HttpParser instance = null;
+ public static synchronized HttpParser getInstance() {
+ if(instance == null) {
+ instance = new PickyHttpParser();
+ }
+ return instance;
+ }
+
+ /** Log object for this class. */
+ private final Log LOG = LogFactory.getLog(PickyHttpParser.class);
+
+ private int lineLengthLimit = 4096;
+ private int headersLimit = 1000;
+ private int valueLengthLimit = 4096;
+
+ /**
+ * Constructor for HttpParser.
+ */
+ private PickyHttpParser() { }
+
+ /*
+ * Return byte array from an (unchunked) input stream.
+ * Stop reading when "\n" terminator encountered
+ * If the stream ends before the line terminator is found,
+ * the last part of the string will still be returned.
+ * If no input data available, null is returned
+ *
+ * @param inputStream the stream to read from
+ *
+ * @throws IOException if an I/O problem occurs
+ * @return a byte array from the stream
+ */
+ public byte[] readRawLine(InputStream inputStream) throws IOException {
+ LOG.trace("enter HttpParser.readRawLine()");
+
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ int ch;
+ int n = 0;
+ while ((ch = inputStream.read()) >= 0) {
+ buf.write(ch);
+ n++;
+ if (ch == '\n') {
+ break;
+ }
+ if(n > lineLengthLimit) {
+ throw new HttpException("Line too long (> "+lineLengthLimit+" bytes)");
+ }
+ }
+ if (buf.size() == 0) {
+ return null;
+ }
+ return buf.toByteArray();
+ }
+
+ /*
+ * Read up to "\n" from an (unchunked) input stream.
+ * If the stream ends before the line terminator is found,
+ * the last part of the string will still be returned.
+ * If no input data available, null is returned
+ *
+ * @param inputStream the stream to read from
+ *
+ * @throws IOException if an I/O problem occurs
+ * @return a line from the stream
+ */
+ public String readLine(InputStream inputStream) throws IOException {
+ LOG.trace("enter HttpParser.readLine()");
+ byte[] rawdata = readRawLine(inputStream);
+ if (rawdata == null) {
+ return null;
+ }
+ int len = rawdata.length;
+ int offset = 0;
+ if (len > 0) {
+ if (rawdata[len - 1] == '\n') {
+ offset++;
+ if (len > 1) {
+ if (rawdata[len - 2] == '\r') {
+ offset++;
+ }
+ }
+ }
+ }
+ return HttpConstants.getString(rawdata, 0, len - offset);
+ }
+
+ /*
+ * Parses headers from the given stream. Headers with the same name are not
+ * combined.
+ *
+ * @param is the stream to read headers from
+ *
+ * @return an array of headers in the order in which they were parsed
+ *
+ * @throws IOException if an IO error occurs while reading from the stream
+ * @throws HttpException if there is an error parsing a header value
+ */
+ public Header[] parseHeaders(InputStream is) throws IOException, HttpException {
+ LOG.trace("enter HeaderParser.parseHeaders(HttpConnection, HeaderGroup)");
+
+ ArrayList headers = new ArrayList();
+ String name = null;
+ StringBuffer value = null;
+ for (; ;) {
+ if(headers.size() > headersLimit) {
+ throw new HttpException("Too many headers (> "+headersLimit+")");
+ }
+ String line = readLine(is);
+ if ((line == null) || (line.length() < 1)) {
+ break;
+ }
+
+ // Parse the header name and value
+ // Check for folded headers first
+ // Detect LWS-char see HTTP/1.0 or HTTP/1.1 Section 2.2
+ // discussion on folded headers
+ if ((line.charAt(0) == ' ') || (line.charAt(0) == '\t')) {
+ // we have continuation folded header
+ // so append value
+ if (value != null) {
+ value.append(' ');
+ value.append(line.trim());
+ if(value.length() > valueLengthLimit) {
+ throw new HttpException("Header value too long (> "+valueLengthLimit+")");
+ }
+ }
+ } else {
+ // make sure we save the previous name,value pair if present
+ if (name != null) {
+ headers.add(new Header(name, value.toString()));
+ }
+
+ // Otherwise we should have normal HTTP header line
+ // Parse the header name and value
+ int colon = line.indexOf(":");
+ if (colon < 0) {
+ throw new ProtocolException("Unable to parse header: " + line);
+ }
+ name = line.substring(0, colon).trim();
+ value = new StringBuffer(line.substring(colon + 1).trim());
+ if(value.length() > valueLengthLimit) {
+ throw new HttpException("Header value too long (> "+valueLengthLimit+")");
+ }
+ }
+
+ }
+
+ // make sure we save the last name,value pair if present
+ if (name != null) {
+ headers.add(new Header(name, value.toString()));
+ if(headers.size() > headersLimit) {
+ throw new HttpException("Too many headers (> "+headersLimit+")");
+ }
+ }
+
+ return (Header[]) headers.toArray(new Header[headers.size()]);
+ }
+}