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]