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]