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]>