Index: org/apache/commons/httpclient/ConnectMethod.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ConnectMethod.java,v retrieving revision 1.22 diff -u -r1.22 ConnectMethod.java --- org/apache/commons/httpclient/ConnectMethod.java 10 Nov 2003 23:19:49 -0000 1.22 +++ org/apache/commons/httpclient/ConnectMethod.java 12 Nov 2003 10:21:34 -0000 @@ -238,7 +238,7 @@ * * @return true if the connection should be closed */ - protected boolean shouldCloseConnection(HttpConnection conn) { + protected boolean shouldCloseConnection(HttpConnection conn) throws ProtocolException { if (getStatusCode() == HttpStatus.SC_OK) { Header connectionHeader = null; if (!conn.isTransparent()) { Index: org/apache/commons/httpclient/HttpMethodBase.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v retrieving revision 1.189 diff -u -r1.189 HttpMethodBase.java --- org/apache/commons/httpclient/HttpMethodBase.java 5 Nov 2003 20:45:34 -0000 1.189 +++ org/apache/commons/httpclient/HttpMethodBase.java 12 Nov 2003 10:21:34 -0000 @@ -123,6 +123,7 @@ * @author Oleg Kalnichevski * @author Mike Bowler * @author Gary Gregory + * @author Christian Kohlschuetter * * @version $Revision: 1.189 $ $Date: 2003/11/05 20:45:34 $ */ @@ -697,6 +698,9 @@ setResponseStream(null); this.responseBody = outstream.toByteArray(); } + } catch (ProtocolException e) { + LOG.error("Protocol violation detected while reading response body", e); + this.responseBody = null; } catch (IOException e) { LOG.error("I/O failure reading response body", e); this.responseBody = null; @@ -879,14 +883,43 @@ * @param conn the connection in question * * @return boolean true if we should close the connection. + * @throws ProtocolException if we detect an intolerable protocol violation */ - protected boolean shouldCloseConnection(HttpConnection conn) { + protected boolean shouldCloseConnection(HttpConnection conn) throws ProtocolException { // Connection must be closed due to an abnormal circumstance if (isConnectionCloseForced()) { LOG.debug("Should force-close connection."); return true; } + + // At this point, no response data should be available. + // If there is data available, assume a protocol violation and + // close the connection + int detectSurplusData = + getParams().getIntParameter( + HttpMethodParams.DETECT_SURPLUS_RESPONSE_DATA, + 1); + if (detectSurplusData > 0) { + try { + if (conn.isResponseAvailable()) { + if (detectSurplusData == 2) { + throw new ProtocolException("Unexpected surplus response data available"); + } else { + LOG.debug("Unexpected surplus response data available"); + return true; + } + } + + } catch (ProtocolException e) { + throw e; + } catch (IOException e) { + if (LOG.isDebugEnabled()) { + LOG.debug("Unexpected IOException; closing connection", e); + } + return true; + } + } Header connectionHeader = null; // In case being connected via a proxy server @@ -1754,7 +1787,7 @@ result = new AutoCloseInputStream( result, new ResponseConsumedWatcher() { - public void responseConsumed() { + public void responseConsumed() throws ProtocolException { responseBodyConsumed(); } } @@ -1831,14 +1864,26 @@ throws IOException, HttpRecoverableException, HttpException { LOG.trace("enter HttpMethodBase.readStatusLine(HttpState, HttpConnection)"); + final int maxGarbageLines = getParams().getIntParameter(HttpMethodParams.STATUS_LINE_GARBAGE_LIMIT, Integer.MAX_VALUE); + //read out the HTTP status string - String s = conn.readLine(); - while ((s != null) && !StatusLine.startsWithHTTP(s)) { - if (Wire.enabled()) { - Wire.input(s + "\r\n"); - } + int attempts = 0; + String s; + do { s = conn.readLine(); - } + if(s != null) { + if (Wire.enabled()) { + Wire.input(s + "\r\n"); + } + + if(StatusLine.startsWithHTTP(s)) { + break; + } else { + s = null; + } + } + } while(attempts++ < maxGarbageLines); + if (s == null) { // A null statusString means the connection was lost before we got a // response. Try again. @@ -1846,9 +1891,7 @@ + " line from the response: unable to find line starting with" + " \"HTTP\""); } - if (Wire.enabled()) { - Wire.input(s + "\r\n"); - } + //create the status line from the status string statusLine = new StatusLine(s); @@ -2236,7 +2279,7 @@ * inside the execute call.

* */ - protected void responseBodyConsumed() { + protected void responseBodyConsumed() throws ProtocolException { // make sure this is the initial invocation of the notification, // ignore subsequent ones. @@ -2244,8 +2287,16 @@ if (responseConnection != null) { responseConnection.setLastResponseInputStream(null); - if (shouldCloseConnection(responseConnection)) { - responseConnection.close(); + boolean closeConn = false; + try { + closeConn = shouldCloseConnection(responseConnection); + } catch(ProtocolException e) { + closeConn = true; + throw e; + } finally { + if (closeConn) { + responseConnection.close(); + } } } this.connectionCloseForced = false; Index: org/apache/commons/httpclient/ResponseConsumedWatcher.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ResponseConsumedWatcher.java,v retrieving revision 1.3 diff -u -r1.3 ResponseConsumedWatcher.java --- org/apache/commons/httpclient/ResponseConsumedWatcher.java 28 Jan 2003 04:40:21 -0000 1.3 +++ org/apache/commons/httpclient/ResponseConsumedWatcher.java 12 Nov 2003 10:21:34 -0000 @@ -81,6 +81,8 @@ /** * A response has been consumed. + * + * @throws ProtocolException if we detected an intolerable protocol violation */ - void responseConsumed(); + void responseConsumed() throws ProtocolException; } Index: org/apache/commons/httpclient/params/HttpMethodParams.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/params/HttpMethodParams.java,v retrieving revision 1.5 diff -u -r1.5 HttpMethodParams.java --- org/apache/commons/httpclient/params/HttpMethodParams.java 22 Oct 2003 19:31:00 -0000 1.5 +++ org/apache/commons/httpclient/params/HttpMethodParams.java 12 Nov 2003 10:21:35 -0000 @@ -184,7 +184,48 @@ * This parameter expects a value of type {@link String}. *

*/ - public static final String COOKIE_POLICY = "http.protocol.cookie-policy"; + public static final String COOKIE_POLICY = "http.protocol.cookie-policy"; + + /** + * Defines HttpClient's behavior when a Response provides more bytes than + * expected (specified with Content-Length, for example). + *

+ * Such surplus data makes the HTTP connection unreliable for keep-alive + * requests, as malicious response data (faked headers etc.) can lead to undesired + * results on the next request using that connection. + *

+ *

+ * The following values are permitted: + *

+ *

+ * This parameter expects a value of type {@link Integer}. + *

+ */ + public static final String DETECT_SURPLUS_RESPONSE_DATA = "http.protocol.detect-surplus-response-data"; + + /** + * Defines the maximum number of ignorable lines before we expect + * a HTTP response's status code. + *

+ * With HTTP/1.1 persistent connections, the problem arises that + * broken scripts could return a wrong Content-Length + * (there are more bytes sent than specified).
+ * Unfortunately, in some cases, this is not possible after the bad response, + * but only before the next one.
+ * So, HttpClient must be able to skip those surplus lines this way. + *

+ *

+ * Set this to 0 to disallow any garbage lines before the status line.
+ * To specify no limit, use {@link Integer.MAX_VALUE}. + *

+ * + * This parameter expects a value of type {@link Integer}. + */ + public static final String STATUS_LINE_GARBAGE_LIMIT = "http.protocol.status-line-garbage-limit"; /** * Creates a new collection of parameters with the collection returned @@ -283,6 +324,8 @@ */ public void makeStrict() { setParameters(PROTOCOL_STRICTNESS_PARAMETERS, new Boolean(true)); + setIntParameter(STATUS_LINE_GARBAGE_LIMIT, 0); + setIntParameter(DETECT_SURPLUS_RESPONSE_DATA, 2); } /** @@ -293,6 +336,8 @@ */ public void makeLenient() { setParameters(PROTOCOL_STRICTNESS_PARAMETERS, new Boolean(false)); + setIntParameter(STATUS_LINE_GARBAGE_LIMIT, Integer.MAX_VALUE); + setIntParameter(DETECT_SURPLUS_RESPONSE_DATA, 1); } }