marcsaeg    02/02/22 11:20:35

  Modified:    httpclient/src/java/org/apache/commons/httpclient
                        HttpMethodBase.java
  Log:
  1)  Support new HttpRecoverableException.
  2)  Fixed handling of 100 response after the body was sent.
  3)  Support for relative URIs in Location: header in non-strict mode.
  4)  Always recreate cookie headers.  A redirect may have added new cookies
  or altered the path.
  5)  Updated readResponseBody() to handle servers that send bodies without
  the expected header elements.
  6)  Added a readResponseBody() method that takes an OutputStream.  Derived
  classes can now more easily use the readResponseBody() method from the base
  class.
  
  Revision  Changes    Path
  1.26      +132 -30   
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.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- HttpMethodBase.java       17 Feb 2002 12:04:29 -0000      1.25
  +++ HttpMethodBase.java       22 Feb 2002 19:20:35 -0000      1.26
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v
 1.25 2002/02/17 12:04:29 dion Exp $
  - * $Revision: 1.25 $
  - * $Date: 2002/02/17 12:04:29 $
  + * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v
 1.26 2002/02/22 19:20:35 marcsaeg Exp $
  + * $Revision: 1.26 $
  + * $Date: 2002/02/22 19:20:35 $
    * ====================================================================
    *
    * The Apache Software License, Version 1.1
  @@ -111,7 +111,7 @@
    * @author Rodney Waldhoff
    * @author Sean C. Sullivan
    * @author <a href="mailto:[EMAIL PROTECTED]";>dIon Gillard</a>
  - * @version $Revision: 1.25 $ $Date: 2002/02/17 12:04:29 $
  + * @version $Revision: 1.26 $ $Date: 2002/02/22 19:20:35 $
    */
   public abstract class HttpMethodBase implements HttpMethod {
   
  @@ -158,6 +158,33 @@
       }
   
       /**
  +     * Turns strict mode on or off.  In strict mode (the default)
  +     * we following the letter of RFC 2616, the Http 1.1 specification.
  +     * If strict mode is turned off we attempt to violate the specification
  +     * in the same way that most Http user agent's do (and many HTTP servers
  +     * expect.
  +     *
  +     * NOTE:  StrictMode is currently experimental and its functionlaity may change 
in the future.
  +     *
  +     */
  +    public void setStrictMode(boolean strictMode)
  +    {
  +        this.strictMode = strictMode;
  +    }
  +
  +    /**
  +     * Returns the value of strictMode.
  +     *
  +     * NOTE:  StrictMode is currently experimental and its functionlaity may change 
in the future.
  +     *
  +     * @return true if strict mode is enabled.
  +     */
  +    public boolean isStrictMode()
  +    {
  +        return strictMode;
  +    }
  +
  +    /**
        * Set the specified request header, overwriting any
        * previous value.
        * Note that header-name matching is case-insensitive.
  @@ -423,7 +450,7 @@
   
           Set visited = new HashSet();
           Set realms = new HashSet();
  -
  +        int retryCount = 0;
           for(;;) {
               visited.add(connection.getHost() + ":" + connection.getPort() + "|" + 
HttpMethodBase.generateRequestLine(connection, 
getName(),getPath(),getQueryString(),(http11 ? "HTTP/1.1" : "HTTP/1.0")));
   
  @@ -431,28 +458,36 @@
                   log.debug("HttpMethodBase.execute(): looping.");
               }
   
  -            if (!connection.isOpen()) {
  -                if (log.isDebugEnabled()) {
  -                             log.debug("HttpMethodBase.execute(): opening 
connection.");
  +            try{
  +                if (!connection.isOpen()) {
  +                    if (log.isDebugEnabled()) {
  +                        log.debug("HttpMethodBase.execute(): opening connection.");
  +                    }
  +                    connection.open();
                   }
  -                connection.open();
  -            }
  -
  -            writeRequest(state,connection);
  -            used = true;
   
  -            // need to close output?, but when?
  -
  -            readResponse(state,connection);
  +                writeRequest(state,connection);
  +                used = true;
   
  +                // need to close output?, but when?
  +                readResponse(state,connection);
  +            }catch(HttpRecoverableException e){
  +                if(retryCount >= maxRetries){
  +                    throw new HttpException(e.toString());
  +                }
  +                retryCount++;
  +                connection.close();
  +                log.debug("HttpMethodBase.execute():  Caught recoverable exception, 
retrying...");
  +                continue;
  +            }
               if (HttpStatus.SC_CONTINUE == statusCode) {
                   if (!bodySent) {
                       bodySent = writeRequestBody(state,connection);
  -                    readResponse(state,connection);
                   } else {
                       log.warn("HttpMethodBase.execute(): received 100 response, but 
I've already sent the response.");
  -                    break;
  +                    // According to RFC 2616 this respose should be ignored
                   }
  +                readResponse(state,connection);
               }
   
               if (!http11) {
  @@ -533,6 +568,19 @@
                                       port = connection.isSecure() ? 443 : 80;
                                   }
                                   url = new 
URL(protocol,connection.getHost(),port,location.getValue());                           
     
  +                            } else if(!isStrictMode() && 
location.getValue().indexOf("://") < 0) {
  +                                /*
  +                                 * Location doesn't start with / but it doesn't 
contain a protocol.
  +                                 * Per RFC 2616, that's an error.  In non-strict 
mode we'll try
  +                                 * to build a URL relative to the current path.
  +                                 */
  +                                String protocol = connection.isSecure() ? "https" : 
"http";
  +                                int port = connection.getPort();
  +                                if(-1 == port) {
  +                                    port = connection.isSecure() ? 443 : 80;
  +                                }
  +                                URL currentUrl = new 
URL(protocol,connection.getHost(),port,getPath());
  +                                url = new URL(currentUrl, location.getValue());
                               } else {
                                   url = new URL(location.getValue());
                               }                            
  @@ -749,16 +797,12 @@
       }
   
       /**
  -     * Adds a <tt>Cookie</tt> request containing the matching {@link Cookie}s,
  -     * if any, as long as no <tt>Cookie</tt> request header
  -     * already exists.
  +     * Adds a <tt>Cookie</tt> request containing the matching {@link Cookie}s.
        */
       protected void addCookieRequestHeader(HttpState state, HttpConnection conn) 
throws IOException, HttpException {
  -        if (!requestHeaders.containsKey("cookie")) {
  -            Header cookieHeader = Cookie.createCookieHeader(conn.getHost(), 
conn.getPort(), getPath(), conn.isSecure(), new Date(), state.getCookies());
  -            if (null != cookieHeader) {
  -                setRequestHeader(cookieHeader);
  -            }
  +        Header cookieHeader = Cookie.createCookieHeader(conn.getHost(), 
conn.getPort(), getPath(), conn.isSecure(), new Date(), state.getCookies());
  +        if(null != cookieHeader) {
  +            setRequestHeader(cookieHeader);
           }
       }
   
  @@ -912,7 +956,8 @@
               statusLine = conn.readLine();
           }
           if (statusLine == null) {
  -            throw new HttpException("Error in parsing the status line from the 
response: unable to find line starting with \"HTTP/\"");
  +            // A null statusLine means the connection was lost before we got a 
response.  Try again.
  +            throw new HttpRecoverableException("Error in parsing the status line 
from the response: unable to find line starting with \"HTTP/\"");
           }
   
           if ((!statusLine.startsWith("HTTP/1.1") &&
  @@ -1078,11 +1123,37 @@
        * @param conn the {@link HttpConnection} to read the response from
        */
       protected void readResponseBody(HttpState state, HttpConnection conn) throws 
IOException, HttpException {
  +        ByteArrayOutputStream out = new ByteArrayOutputStream();
  +        readResponseBody(state, conn, out);
  +
  +        out.close();
  +        responseBody = out.toByteArray();
  +    }
  +
  +    /**
  +     * Read the response body from the given {@link HttpConnection}.
  +     * <p>
  +     * The current implementation simply consumes the expected
  +     * response body (according to the values of the
  +     * <tt>Content-Length</tt> and <tt>Transfer-Encoding</tt>
  +     * headers, if any).
  +     * <p>
  +     * Subclasses may want to override this method to
  +     * to customize the processing.
  +     *
  +     * @see #readResponse
  +     * @see #processResponseBody
  +     *
  +     * @param state the client state
  +     * @param conn the {@link HttpConnection} to read the response from
  +     * @param out OutputStream to write the response body to
  +     */
  +    protected void readResponseBody(HttpState state, HttpConnection conn, 
OutputStream out) throws IOException {
           if (log.isDebugEnabled()) {
                log.debug("HttpMethodBase.readResponseBody(HttpState,HttpConnection)");
           }
  +
           responseBody = null;
  -        ByteArrayOutputStream out = new ByteArrayOutputStream();
           int expectedLength = 0;
           int foundLength = 0;
           {
  @@ -1098,6 +1169,15 @@
                   if ("chunked".equalsIgnoreCase(transferEncodingHeader.getValue())) {
                       expectedLength = -1;
                   }
  +            } else if(canResponseHaveBody(statusCode)){
  +                /*
  +                 * According to the specification, a response with neither 
Content-Length
  +                 * nor Transfer-Encoding indicates that the response has no body.  
In
  +                 * the real world, this usually means that the server just didn't 
know
  +                 * the content-length when it sent the response.  FIXME:  Should we 
do
  +                 * this only in non-strict mode?
  +                 */
  +                expectedLength = -1;
               }
           }
           InputStream is = conn.getResponseInputStream(this);
  @@ -1125,8 +1205,6 @@
                   }
               }
           }
  -        out.close();
  -        responseBody = out.toByteArray();
       }
   
       /**
  @@ -1245,6 +1323,26 @@
           }
       }
   
  +    /**
  +     * Per RFC 2616 section 4.3, some response can never contain
  +     * a message body.
  +     *
  +     * @param status - the HTTP status code
  +     * @return true if the message may contain a body, false if it can not contain 
a message body
  +     */
  +    private static boolean canResponseHaveBody(int status)
  +    {
  +        boolean result = true;
  +
  +        if((status >= 100 && status <= 199) ||    // 1XX
  +           status == 204 ||                       // NO CONTENT
  +           status == 304){                        // NOT MODIFIED
  +            result = false;
  +        }
  +
  +        return result;
  +    }
  +
       // ----------------------------------------------------- Instance Variables
       /** My request path. */
       private String path = null;
  @@ -1268,6 +1366,10 @@
       private boolean bodySent = false;
       /** The response body, assuming it has not be intercepted by a sub-class. */
       private byte[] responseBody = null;
  +    /** The maximum number of attempts to attempt recovery from an 
HttpRecoverableException. */
  +    private int maxRetries = 3;
  +    /** True if we're in strict mode. */
  +    private boolean strictMode = true;
   
       // -------------------------------------------------------------- Constants
   
  
  
  

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

Reply via email to