olegk       2003/10/19 11:46:03

  Modified:    httpclient/src/java/org/apache/commons/httpclient
                        HttpMethodBase.java StatusLine.java
               httpclient/src/test/org/apache/commons/httpclient
                        TestNoHost.java TestStatusLine.java
  Added:       httpclient/src/test/org/apache/commons/httpclient
                        TestHttpParser.java
  Log:
  HTTP status line parser changed to be more robust when dealing with non-compliant 
HTTP responses (leading blanks before 'HTTP' signature)
  
  Problem reported by Tim McCune <tmccune at hmsonline.com>
  
  Contributed by Oleg Kalnichevski
  Reviewed by Michael Becke
  
  Revision  Changes    Path
  1.183     +11 -11    
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java
  
  Index: HttpMethodBase.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v
  retrieving revision 1.182
  retrieving revision 1.183
  diff -u -r1.182 -r1.183
  --- HttpMethodBase.java       13 Oct 2003 12:22:24 -0000      1.182
  +++ HttpMethodBase.java       19 Oct 2003 18:46:02 -0000      1.183
  @@ -1825,14 +1825,14 @@
           LOG.trace("enter HttpMethodBase.readStatusLine(HttpState, HttpConnection)");
   
           //read out the HTTP status string
  -        String statusString = conn.readLine();
  -        while ((statusString != null) && !statusString.startsWith("HTTP")) {
  +        String s = conn.readLine();
  +        while ((s != null) && !StatusLine.startsWithHTTP(s)) {
               if (Wire.enabled()) {
  -                Wire.input(statusString + "\r\n");
  +                Wire.input(s + "\r\n");
               }
  -            statusString = conn.readLine();
  +            s = conn.readLine();
           }
  -        if (statusString == null) {
  +        if (s == null) {
               // A null statusString means the connection was lost before we got a
               // response.  Try again.
               throw new HttpRecoverableException("Error in parsing the status "
  @@ -1840,10 +1840,10 @@
                   + " \"HTTP\"");
           }
           if (Wire.enabled()) {
  -            Wire.input(statusString + "\r\n");
  +            Wire.input(s + "\r\n");
           }
           //create the status line from the status string
  -        statusLine = new StatusLine(statusString);
  +        statusLine = new StatusLine(s);
   
           //check for a valid HTTP-Version
           String versionStr = statusLine.getHttpVersion();
  
  
  
  1.11      +40 -17    
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/StatusLine.java
  
  Index: StatusLine.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/StatusLine.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- StatusLine.java   15 Jul 2003 02:19:58 -0000      1.10
  +++ StatusLine.java   19 Oct 2003 18:46:03 -0000      1.11
  @@ -112,28 +112,31 @@
        * @param statusLine the status line returned from the HTTP server
        * @throws HttpException if the status line is invalid
        */
  -    public StatusLine(String statusLine) 
  -    throws HttpException {
  +    public StatusLine(final String statusLine) throws HttpException {
   
  -        //save the original Status-Line
  -        this.statusLine = new String(statusLine);
           int length = statusLine.length();
  -
  -
  -        //check validity of the Status-Line
  -        if (!statusLine.startsWith("HTTP")) {
  -            throw new ProtocolException("Status-Line '" + statusLine 
  -                + "' does not start with HTTP");
  +        int at = 0;
  +        int start = 0;
  +        try {
  +            while (Character.isWhitespace(statusLine.charAt(at))) {
  +                ++at;
  +                ++start;
  +            }
  +            if (!"HTTP".equals(statusLine.substring(at, at += 4))) {
  +                throw new HttpException("Status-Line '" + statusLine 
  +                    + "' does not start with HTTP");
  +            }
  +        } catch (StringIndexOutOfBoundsException e) {
  +            throw new HttpException("Status-Line '" + statusLine + "' is not 
valid"); 
           }
  -
           //handle the HTTP-Version
  -        int at = statusLine.indexOf(" ");
  +        at = statusLine.indexOf(" ", at);
           if (at <= 0) {
               throw new ProtocolException(
                       "Unable to parse HTTP-Version from the status line: '"
                       + statusLine + "'");
           }
  -        this.httpVersion = (statusLine.substring(0, at)).toUpperCase();
  +        this.httpVersion = (statusLine.substring(start, at)).toUpperCase();
   
           //advance through spaces
           while (statusLine.charAt(at) == ' ') {
  @@ -165,6 +168,8 @@
               throw new ProtocolException("Status text not specified: '" 
                       + statusLine + "'");
           }
  +        //save the original Status-Line
  +        this.statusLine = new String(statusLine);
       }
   
   
  @@ -197,5 +202,23 @@
        */
       public final String toString() {
           return statusLine;
  +    }
  +
  +    /**
  +     * Tests if the string starts with 'HTTP' signature.
  +     * @param s string to test
  +     * @return <tt>true</tt> if the line starts with 'HTTP' 
  +     *   signature, <tt>false</tt> otherwise.
  +     */
  +    public static boolean startsWithHTTP(final String s) {
  +        try {
  +            int at = 0;
  +            while (Character.isWhitespace(s.charAt(at))) {
  +                ++at;
  +            }
  +            return ("HTTP".equals(s.substring(at, at + 4)));
  +        } catch (StringIndexOutOfBoundsException e) {
  +            return false;
  +        }
       }
   }
  
  
  
  1.26      +5 -4      
jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestNoHost.java
  
  Index: TestNoHost.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestNoHost.java,v
  retrieving revision 1.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- TestNoHost.java   5 Aug 2003 19:29:30 -0000       1.25
  +++ TestNoHost.java   19 Oct 2003 18:46:03 -0000      1.26
  @@ -107,6 +107,7 @@
           suite.addTest(TestMethodCharEncoding.suite());
           suite.addTest(TestExceptions.suite());
           suite.addTest(TestHttpVersion.suite());
  +        suite.addTest(TestHttpParser.suite());
           return suite;
       }
   
  
  
  
  1.7       +26 -4     
jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestStatusLine.java
  
  Index: TestStatusLine.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestStatusLine.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- TestStatusLine.java       7 Feb 2003 04:50:03 -0000       1.6
  +++ TestStatusLine.java       19 Oct 2003 18:46:03 -0000      1.7
  @@ -96,6 +96,16 @@
   
       // ----------------------------------------------------------- Test Methods
   
  +    public void testIfStatusLine() throws Exception {
  +        assertTrue(StatusLine.startsWithHTTP("HTTP"));
  +        assertTrue(StatusLine.startsWithHTTP("         HTTP"));
  +        assertTrue(StatusLine.startsWithHTTP("\rHTTP"));
  +        assertTrue(StatusLine.startsWithHTTP("\tHTTP"));
  +        assertFalse(StatusLine.startsWithHTTP("crap"));
  +        assertFalse(StatusLine.startsWithHTTP("HTT"));
  +        assertFalse(StatusLine.startsWithHTTP("http"));
  +    }
  +
       public void testSuccess() throws Exception {
           //typical status line
           statusLine = new StatusLine("HTTP/1.1 200 OK");
  @@ -131,6 +141,18 @@
           statusLine = new StatusLine("HTTP/1.1     200 OK");
           assertEquals(200, statusLine.getStatusCode());
           assertEquals("OK", statusLine.getReasonPhrase());
  +
  +        //this is not strictly valid, but is lienent
  +        statusLine = new StatusLine("\rHTTP/1.1 200 OK");
  +        assertEquals(200, statusLine.getStatusCode());
  +        assertEquals("OK", statusLine.getReasonPhrase());
  +        assertEquals("HTTP/1.1", statusLine.getHttpVersion());
  +
  +        //this is not strictly valid, but is lienent
  +        statusLine = new StatusLine("  HTTP/1.1 200 OK");
  +        assertEquals(200, statusLine.getStatusCode());
  +        assertEquals("OK", statusLine.getReasonPhrase());
  +        assertEquals("HTTP/1.1", statusLine.getHttpVersion());
       }
   
       public void testFailure() throws Exception {
  
  
  
  1.1                  
jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpParser.java
  
  Index: TestHttpParser.java
  ===================================================================
  /*
   * $Header: 
/home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpParser.java,v
 1.1 2003/10/19 18:46:03 olegk Exp $
   * $Revision: 1.1 $
   * $Date: 2003/10/19 18:46:03 $
   * ====================================================================
   *
   * 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", "Tomcat", 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 [EMAIL PROTECTED]
   *
   * 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
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  
  package org.apache.commons.httpclient;
  
  import java.io.ByteArrayInputStream;
  import java.io.InputStream;
  
  import junit.framework.*;
  
  /**
   * Simple tests for [EMAIL PROTECTED] HttpParser}.
   *
   * @author Oleg Kalnichevski
   * @version $Id: TestHttpParser.java,v 1.1 2003/10/19 18:46:03 olegk Exp $
   */
  public class TestHttpParser extends TestCase {
  
      // ------------------------------------------------------------ Constructor
      public TestHttpParser(String testName) {
          super(testName);
      }
  
      // ------------------------------------------------------------------- Main
      public static void main(String args[]) {
          String[] testCaseName = { TestHeader.class.getName() };
          junit.textui.TestRunner.main(testCaseName);
      }
  
      // ------------------------------------------------------- TestCase Methods
  
      public static Test suite() {
          return new TestSuite(TestHttpParser.class);
      }
  
      public void testReadHttpLine() throws Exception {
          InputStream instream = new ByteArrayInputStream(
              "\r\r\nstuff\r\n".getBytes("US-ASCII")); 
          assertEquals("\r", HttpParser.readLine(instream));
          assertEquals("stuff", HttpParser.readLine(instream));
          assertEquals(null, HttpParser.readLine(instream));
  
          instream = new ByteArrayInputStream(
              "\n\r\nstuff\r\n".getBytes("US-ASCII")); 
          assertEquals("", HttpParser.readLine(instream));
          assertEquals("", HttpParser.readLine(instream));
          assertEquals("stuff", HttpParser.readLine(instream));
          assertEquals(null, HttpParser.readLine(instream));
      }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to