olegk       2003/03/05 23:49:04

  Modified:    httpclient/src/java/org/apache/commons/httpclient
                        HttpConnection.java HttpMethodBase.java
                        MultiThreadedHttpConnectionManager.java
               httpclient/src/java/org/apache/commons/httpclient/methods
                        EntityEnclosingMethod.java MultipartPostMethod.java
  Log:
  Changelog:
  - 'Expect: 100-continue' logic refactored
  - 'waitForResponse method is using busy wait' (bug #17487) fixed
  
  Contributed by Oleg Kalnichevski
  
  Revision  Changes    Path
  1.50      +23 -38    
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java
  
  Index: HttpConnection.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v
  retrieving revision 1.49
  retrieving revision 1.50
  diff -u -r1.49 -r1.50
  --- HttpConnection.java       28 Feb 2003 12:48:58 -0000      1.49
  +++ HttpConnection.java       6 Mar 2003 07:49:03 -0000       1.50
  @@ -459,6 +459,25 @@
       }
   
       /**
  +     * Return my [EMAIL PROTECTED] Socket}'s timeout, via [EMAIL PROTECTED] 
Socket#setSoTimeout}, if the
  +     * connection is already open. If no connection is open, return the value 
subsequent 
  +     * connection will use.
  +     * <p>
  +     * Note: This is not a connection timeout but a timeout on network traffic!
  +     *
  +     * @return the timeout value
  +     */
  +    public int getSoTimeout()
  +        throws SocketException, IllegalStateException {
  +        LOG.debug("HttpConnection.getSoTimeout()");
  +        if (this.socket != null) {
  +            return this.socket.getSoTimeout();
  +        } else {
  +            return this.soTimeout;
  +        }
  +    }
  +
  +    /**
        * Sets the connection timeout. This is the maximum time that may be spent
        * until a connection is established. The connection will fail after this
        * amount of time.
  @@ -673,40 +692,6 @@
           throws IOException {
           assertOpen();
           return this.inputStream.available() > 0;
  -    }
  -
  -
  -    /**
  -     * Waits for the specified number of milliseconds for input data to become 
available
  -     * 
  -     * @param timeout_ms Number of milliseconds to wait for input data.
  -     * 
  -     * @return boolean <tt>true</tt> if input data is availble, 
  -     *                 <tt>false</tt> otherwise.
  -     * 
  -     * @throws IOException If an IO problem occurs
  -     * @throws IllegalStateException If the connection isn't open.
  -     */
  -    public boolean waitForResponse(long timeout_ms)
  -        throws IOException, IllegalStateException {
  -        LOG.trace("enter HttpConnection.waitForResponse(int)");
  -        if (timeout_ms < 0) {
  -            throw new IllegalArgumentException("Timeout value may not be negative");
  -        }
  -        long overtime = System.currentTimeMillis() + timeout_ms;
  -        while (System.currentTimeMillis() < overtime) {
  -            if (isResponseAvailable()) {
  -                return true;
  -            } else {
  -                synchronized(this) {
  -                    try {
  -                        this.wait(50);
  -                    } catch (InterruptedException e) {}
  -                }
  -            }
  -        }
  -        LOG.debug("Waiting for response timeout");
  -        return false;
       }
   
       /**
  
  
  
  1.120     +76 -127   
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.119
  retrieving revision 1.120
  diff -u -r1.119 -r1.120
  --- HttpMethodBase.java       28 Feb 2003 12:48:58 -0000      1.119
  +++ HttpMethodBase.java       6 Mar 2003 07:49:03 -0000       1.120
  @@ -67,6 +67,7 @@
   import java.io.ByteArrayOutputStream;
   import java.io.IOException;
   import java.io.InputStream;
  +import java.io.InterruptedIOException;
   import java.net.MalformedURLException;
   import java.net.URL;
   import java.util.HashSet;
  @@ -192,9 +193,6 @@
       /** Buffer for the response */
       private byte[] responseBody = null;
   
  -    /** Whether or not the request body has been sent. */
  -    private boolean bodySent = false;
  -
       /** Whether or not I should automatically follow redirects. */
       private boolean followRedirects = false;
   
  @@ -229,7 +227,7 @@
       private boolean doneWithConnection = false;
   
       /** Number of milliseconds to wait for 100-contunue response */
  -    private static long RESPONSE_WAIT_TIME_MS = 3000;
  +    private static int RESPONSE_WAIT_TIME_MS = 3000;
   
       // ----------------------------------------------------------- Constructors
   
  @@ -953,12 +951,12 @@
                       LOG.debug("Execute loop try " + forwardCount);
                   }
   
  +                // Discard status line
  +                this.statusLine = null;
  +
                   //write the request and read the response, will retry
                   processRequest(state, conn);
   
  -                //if SC_CONTINUE write the request body
  -                writeRemainingRequestBody(state, conn);
  -
                   if (!isRetryNeeded(statusLine.getStatusCode(), state, conn)) {
                       // nope, no retry needed, exit loop.
                       break;
  @@ -1163,7 +1161,6 @@
           statusLine = null;
           used = false;
           http11 = true;
  -        bodySent = false;
           responseBody = null;
           recoverableExceptionCount = 0;
           inExecute = false;
  @@ -1724,10 +1721,22 @@
           LOG.trace(
               "enter HttpMethodBase.readResponse(HttpState, HttpConnection)");
           try {
  -            readStatusLine(state, conn);
  -            processStatusLine(state, conn);
  -            readResponseHeaders(state, conn);
  -            processResponseHeaders(state, conn);
  +            // Status line & line may have already been received
  +            // if 'expect - continue' handshake has been used
  +            while (this.statusLine == null) {
  +                readStatusLine(state, conn);
  +                processStatusLine(state, conn);
  +                readResponseHeaders(state, conn);
  +                processResponseHeaders(state, conn);
  +                
  +                int status = this.statusLine.getStatusCode();
  +                if ((status >= 100) && (status < 200)) {
  +                    if (LOG.isInfoEnabled()) {
  +                        LOG.info("Discarding unexpected response: " + 
this.statusLine.toString()); 
  +                    }
  +                    this.statusLine = null;
  +                }
  +            }
               readResponseBody(state, conn);
               processResponseBody(state, conn);
           } catch (IOException e) {
  @@ -2009,7 +2018,47 @@
           if (Wire.enabled()) {
               Wire.output("\r\n");
           }
  -        bodySent = writeRequestBody(state, conn);
  +
  +        Header expectheader = getRequestHeader("Expect");
  +        String expectvalue = null;
  +        if (expectheader != null) {
  +            expectvalue = expectheader.getValue();
  +        }
  +        if ((expectvalue != null) 
  +         && (expectvalue.compareToIgnoreCase("100-continue") == 0)) {
  +            if (this.isHttp11()) {
  +                int readTimeout = conn.getSoTimeout();
  +                try {
  +                    conn.setSoTimeout(RESPONSE_WAIT_TIME_MS);
  +                    readStatusLine(state, conn);
  +                    processStatusLine(state, conn);
  +                    readResponseHeaders(state, conn);
  +                    processResponseHeaders(state, conn);
  +
  +                    if (this.statusLine.getStatusCode() == HttpStatus.SC_CONTINUE) {
  +                        // Discard status line
  +                        this.statusLine = null;
  +                        LOG.debug("OK to continue received");
  +                    } else {
  +                        return;
  +                    }
  +                } catch(InterruptedIOException e) {
  +                    // Most probably Expect header is not recongnized
  +                    // Remove the header to signal the method 
  +                    // that it's okay to go ahead with sending data
  +                    removeRequestHeader("Expect");
  +                    LOG.info("100 (continue) read timeout. Resume sending the 
request");
  +                } finally {
  +                    conn.setSoTimeout(readTimeout);
  +                }
  +                
  +            } else {
  +                removeRequestHeader("Expect");
  +                LOG.info("'Expect: 100-continue' handshake is only supported by 
HTTP/1.1 or higher");
  +            }
  +        }
  +
  +        writeRequestBody(state, conn);
       }
   
       /**
  @@ -2131,43 +2180,6 @@
       }
   
       /**
  -     * Determines if the provided value is a valid IPv4 internet address.
  -     *
  -     * @param value - value to check
  -     *
  -     * @return boolean - true if value is valid, otherwise false
  -     */
  -    private static boolean isIpAddress(String value) {
  -        LOG.trace("enter HttpMethodBase.isIpAddress(String)");
  -
  -        value = value.trim();
  -
  -        // prevent input values of 127.0.0.1. or .127.0.0.1, etc.
  -        if (value.startsWith(".") || value.endsWith(".")) {
  -            return false;
  -        }
  -
  -        StringTokenizer tokenizer = new StringTokenizer(value, ".");
  -        if (tokenizer.countTokens() == 4) {
  -            while (tokenizer.hasMoreTokens()) {
  -                try {
  -                    int i = Integer.parseInt(tokenizer.nextToken());
  -                    if ((i < 0) || (i > 255)) {
  -                        // parsed section of address is not in the proper range
  -                        return false;
  -                    }
  -                } catch (NumberFormatException nfe) {
  -                    return false;
  -                }
  -            }
  -        } else {
  -            // wrong number of tokens
  -            return false;
  -        }
  -        return true;
  -    }
  -
  -    /**
        * Per RFC 2616 section 4.3, some response can never contain a message
        * body.
        *
  @@ -2327,83 +2339,20 @@
               }
           } while (retryCount <= maxRetries);
   
  -        
  -        // Should we expect a response at this point?
  -        boolean responseExpected = true;
  -        if (!this.bodySent) {
  -            if (connection.waitForResponse(RESPONSE_WAIT_TIME_MS)) {
  -                responseExpected = true;
  -                LOG.debug("Response available");
  -            } else {
  -                responseExpected = false;
  -                // Something is wrong. 
  -                if (getRequestHeader("Expect") != null) {
  -                    // Most probably Expect header is not recongnized
  -                    // Remove the header to signal the method 
  -                    // that it's okay to go ahead with sending data
  -                    removeRequestHeader("Expect");
  -                }
  -                LOG.debug("Response not available. Send the request body");
  -            }
  -        }
  -        if (responseExpected) {
  -            //try to do the read
  -            try {
  -                readResponse(state, connection);
  -            } catch (HttpRecoverableException httpre) {
  -                LOG.warn("Recoverable exception caught when reading response");
  -                if (LOG.isDebugEnabled()) {
  -                    LOG.debug("Closing the connection.");
  -                }
  -    
  -                connection.close();
  -                throw httpre;
  -            }
  -        }
  -        //everything should be OK at this point
  -    }
  -
  -    /**
  -     * On a [EMAIL PROTECTED] HttpStatus#SC_CONTINUE continue}, if there are more 
request
  -     * bytes to be sent, write them to the connection
  -     *
  -     * @param state the current state
  -     * @param connection the connection for communication
  -     *
  -     * @throws HttpException when errors occur as part of the HTTP protocol
  -     *         conversation
  -     * @throws IOException when an I/O error occurs communicating with the
  -     *         server
  -     */
  -    private void writeRemainingRequestBody(HttpState state,
  -                                           HttpConnection connection)
  -    throws HttpException, IOException {
  -        LOG.trace("enter writeRemainingRequestBody(HttpState, HttpConnection)");
  -
  -        boolean writeRemaining = false;
  -        boolean continueReceived = false;
  -        if (statusLine == null) {
  -            writeRemaining = true;
  -        } else {
  -            if (statusLine.getStatusCode() == HttpStatus.SC_CONTINUE) {
  -                writeRemaining = true;
  -                continueReceived = true;
  +        //try to do the read
  +        try {
  +            readResponse(state, connection);
  +        } catch (HttpRecoverableException httpre) {
  +            LOG.warn("Recoverable exception caught when reading response");
  +            if (LOG.isDebugEnabled()) {
  +                LOG.debug("Closing the connection.");
               }
  -        }
   
  -        if (writeRemaining) {
  -            if (!bodySent) {
  -                bodySent = writeRequestBody(state, connection);
  -            } else {
  -                if (continueReceived) {
  -                    LOG.warn("Received status CONTINUE but the body has already 
been sent");
  -                    // According to RFC 2616 this respose should be ignored
  -                }
  -            }
  -            readResponse(state, connection);
  +            connection.close();
  +            throw httpre;
           }
  +        //everything should be OK at this point
       }
  -
   
       /**
        * Return the character set from the header.
  
  
  
  1.12      +3 -12     
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java
  
  Index: MultiThreadedHttpConnectionManager.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- MultiThreadedHttpConnectionManager.java   26 Feb 2003 15:08:27 -0000      1.11
  +++ MultiThreadedHttpConnectionManager.java   6 Mar 2003 07:49:03 -0000       1.12
  @@ -797,15 +797,6 @@
               }
           }
   
  -        public boolean waitForResponse(long timeout_ms)
  -            throws IOException, IllegalStateException {
  -            if (hasConnection()) {
  -                return wrappedConnection.waitForResponse(timeout_ms);
  -            } else {
  -                return false;
  -            }
  -        }
  -
           public void write(byte[] data, int offset, int length)
               throws IOException, IllegalStateException, HttpRecoverableException {
               if (hasConnection()) {
  
  
  
  1.12      +4 -15     
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java
  
  Index: EntityEnclosingMethod.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- EntityEnclosingMethod.java        28 Feb 2003 12:48:58 -0000      1.11
  +++ EntityEnclosingMethod.java        6 Mar 2003 07:49:03 -0000       1.12
  @@ -392,17 +392,6 @@
           LOG.trace(
               "enter EntityEnclosingMethod.writeRequestBody(HttpState, 
HttpConnection)");
           
  -        if (getRequestHeader("Expect") != null) {
  -            if (getStatusLine() == null) {
  -                LOG.debug("Expecting response");
  -                return false;
  -            }
  -            if (getStatusLine().getStatusCode() != HttpStatus.SC_CONTINUE) {
  -                LOG.debug("Expecting 100-continue");
  -                return false;
  -            }
  -        }
  -
           int contentLength = getRequestContentLength();
   
           if ((contentLength == CONTENT_LENGTH_CHUNKED) && !isHttp11()) {
  
  
  
  1.13      +5 -16     
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java
  
  Index: MultipartPostMethod.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- MultipartPostMethod.java  28 Feb 2003 12:48:58 -0000      1.12
  +++ MultipartPostMethod.java  6 Mar 2003 07:49:03 -0000       1.13
  @@ -276,19 +276,8 @@
       protected boolean writeRequestBody(HttpState state, HttpConnection conn) 
       throws IOException, HttpException {
           LOG.trace("enter MultipartPostMethod.writeRequestBody(HttpState state, 
HttpConnection conn)");
  -        if (getRequestHeader("Expect") != null) {
  -            if (getStatusLine() == null) {
  -                LOG.debug("Expecting response");
  -                return false;
  -            }
  -            if (getStatusLine().getStatusCode() != HttpStatus.SC_CONTINUE) {
  -                LOG.debug("Expecting 100-continue");
  -                return false;
  -            }
  -        }
  -        OutputStream outstream = conn.getRequestOutputStream();
  -
  -        Part.sendParts(outstream, getParts());
  +        OutputStream out = conn.getRequestOutputStream();
  +        Part.sendParts(out, getParts());
           return true;
       }
   
  
  
  

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

Reply via email to