This patch is a partial rewrite of the gnu/java/net/protocol/http package.
It fixes:
* classpath/24086 - HttpURLConnection broken for large response bodies.
* classpath/24091 - HttpURLConnection sends too much data on POST.
* Failure on unsolicited 100 Continue response.
* Broken logic for connection re-use.
The main problem was that response bodies were being buffered in memory
with ResponseBodyReader and friends.
The patch gets rid of the buffering of response body data and controls
access to the connection socket with a new class
(LimitedLengthInputStream). The LimitedLengthInputStream prevents the
client from reading too much from or closing the connection's socket.
If connection keep-alives are enabled, the LimitedLengthInputStream also
controls returning the connection to the connection pool when it is idle.
I also got rid of the gnu/java/net/protocol/http/event package as it is
not used for anything.
The fixes for classpath/24091 and the unsolicited 100 Continue
responses that are sent by WindowsMediaConnect UPnP servers are
intertwined with the rest of the rewrite so so I threw them in as well.
Tested with gcj(HEAD)+mauve+jacks on i686-pc-linux-gnu with no
regressions. Additional ad hoc testing to verify connection re-use with
Connection: keep-alive for multiple GET and POST requests.
2005-09-29 David Daney <[EMAIL PROTECTED]>
PR classpath/24086, PR classpath/24091:
* gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java: Removed.
* gnu/java/net/protocol/http/ResponseBodyReader.java: Removed.
* gnu/java/net/protocol/http/event/ConnectionEvent.java: Removed.
* gnu/java/net/protocol/http/event/ConnectionListener.java: Removed.
* gnu/java/net/protocol/http/event/RequestEvent.java: Removed.
* gnu/java/net/protocol/http/event/RequestListener.java: Removed.
* gnu/java/net/protocol/http/event/package.html: Removed.
* gnu/java/net/protocol/http/HTTPConnection.java: Cleaned up imports.
(connectionListeners): Removed.
(requestListeners): Removed.
(pool): New field.
(Constructor): Don't initialize connectionListeners or
requestListeners.
(getPoolKey): New method.
(setPool): New method.
(release): New method.
(newRequest): Don't call fireRequestEvent.
(close): Don't call fireConnectionEvent.
(addConnectionListener): Removed.
(removeConnectionListener): Removed.
(fireConnectionEvent): Removed.
(addRequestListener): Removed.
(removeRequestListener): Removed.
(fireRequestEvent): Removed.
* gnu/java/net/protocol/http/HTTPURLConnection.java:Cleaned up imports.
(connectionPool): Changed type to LinkedHashMap.
(maxConnections): Made static.
(responseSink): Changed type to InputStream.
(errorSink): Likewise.
(connect): Eliminate reader. Get responseSink from response.
(getConnection): Rewrote.
(getHeaderFields): Use null as key for status line.
* gnu/java/net/protocol/http/Headers.java (getLongValue): New method.
* gnu/java/net/protocol/http/LimitedLengthInputStream.java: New class.
* gnu/java/net/protocol/http/Request.java: Cleaned up imports.
(responseBodyReader): Removed.
(setResponseBodyReader): Removed.
(dispatch): Don't call fireRequestEvent. Don't append CRLF to Request
body. Handle unsolicited 100 Continue response.
(readResponse): Rewrote.
(readResponseBody): Rewrote.
* gnu/java/net/protocol/http/Response.java: Cleaned up imports.
(codeClass): Removed.
(body): New field.
(Constructor): Don't initialize codeClass. Initialize body.
(getCodeClass): Rewrote.
(getLongHeader): New method.
(getBody): New method.
OK to commit?
David Daney.
Index: gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java
===================================================================
RCS file: gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java
diff -N gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java
--- gnu/java/net/protocol/http/ByteArrayResponseBodyReader.java 2 Jul 2005
20:32:13 -0000 1.2
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,123 +0,0 @@
-/* Authenticator.java --ByteArrayResponseBodyReader.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
-
-
-package gnu.java.net.protocol.http;
-
-/**
- * Simple response body reader that stores content in a byte array.
- *
- * @author Chris Burdess ([EMAIL PROTECTED])
- */
-public class ByteArrayResponseBodyReader
- implements ResponseBodyReader
-{
-
- /**
- * The content.
- */
- protected byte[] content;
-
- /**
- * The position in the content at which the next write will occur.
- */
- protected int pos;
-
- /**
- * The length of the buffer.
- */
- protected int len;
-
- /**
- * Constructs a new byte array response body reader.
- */
- public ByteArrayResponseBodyReader()
- {
- this(4096);
- }
-
- /**
- * Constructs a new byte array response body reader with the specified
- * initial buffer size.
- * @param size the initial buffer size
- */
- public ByteArrayResponseBodyReader(int size)
- {
- content = new byte[size];
- pos = len = 0;
- }
-
- /**
- * This reader accepts all responses.
- */
- public boolean accept(Request request, Response response)
- {
- return true;
- }
-
- public void read(byte[] buffer, int offset, int length)
- {
- int l = length - offset;
- if (pos + l > content.length)
- {
- byte[] tmp = new byte[content.length * 2];
- System.arraycopy(content, 0, tmp, 0, pos);
- content = tmp;
- }
- System.arraycopy(buffer, offset, content, pos, l);
- pos += l;
- len = pos;
- }
-
- public void close()
- {
- pos = 0;
- }
-
- /**
- * Retrieves the content of this reader as a byte array.
- * The size of the returned array is the number of bytes read.
- */
- public byte[] toByteArray()
- {
- byte[] ret = new byte[len];
- System.arraycopy(content, 0, ret, 0, len);
- return ret;
- }
-
-}
-
Index: gnu/java/net/protocol/http/HTTPConnection.java
===================================================================
RCS file:
/cvsroot/classpath/classpath/gnu/java/net/protocol/http/HTTPConnection.java,v
retrieving revision 1.9
diff -u -p -r1.9 HTTPConnection.java
--- gnu/java/net/protocol/http/HTTPConnection.java 7 Jul 2005 06:29:30
-0000 1.9
+++ gnu/java/net/protocol/http/HTTPConnection.java 29 Sep 2005 23:05:14
-0000
@@ -41,10 +41,6 @@ package gnu.java.net.protocol.http;
import gnu.classpath.Configuration;
import gnu.classpath.SystemProperties;
import gnu.java.net.EmptyX509TrustManager;
-import gnu.java.net.protocol.http.event.ConnectionEvent;
-import gnu.java.net.protocol.http.event.ConnectionListener;
-import gnu.java.net.protocol.http.event.RequestEvent;
-import gnu.java.net.protocol.http.event.RequestListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -57,6 +53,7 @@ import java.security.GeneralSecurityExce
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -131,8 +128,6 @@ public class HTTPConnection
*/
protected int minorVersion;
- private final List connectionListeners;
- private final List requestListeners;
private final List handshakeCompletedListeners;
/**
@@ -165,6 +160,12 @@ public class HTTPConnection
*/
protected CookieManager cookieManager;
+
+ /**
+ * The pool that this connection is a member of (if any).
+ */
+ private LinkedHashMap pool;
+
/**
* Creates a new HTTP connection.
* @param hostname the name of the host to connect to
@@ -236,8 +237,6 @@ public class HTTPConnection
this.connectionTimeout = connectionTimeout;
this.timeout = timeout;
majorVersion = minorVersion = 1;
- connectionListeners = new ArrayList(4);
- requestListeners = new ArrayList(4);
handshakeCompletedListeners = new ArrayList(2);
}
@@ -331,6 +330,46 @@ public class HTTPConnection
return cookieManager;
}
+ static Object getPoolKey(String h, int p, boolean sec)
+ {
+ StringBuffer buf = new StringBuffer(sec ? "https://" : "http://");
+ buf.append(h);
+ buf.append(':');
+ buf.append(p);
+ return buf.toString();
+ }
+
+ void setPool(LinkedHashMap p)
+ {
+ pool = p;
+ }
+
+ void release()
+ {
+ if (pool != null)
+ {
+ synchronized (pool)
+ {
+ Object key = HTTPConnection.getPoolKey(hostname, port, secure);
+ pool.put(key, this);
+ while (pool.size() >= HTTPURLConnection.maxConnections)
+ {
+ // maxConnections must always be >= 1
+ Object lru = pool.keySet().iterator().next();
+ HTTPConnection c = (HTTPConnection)pool.remove(lru);
+ try
+ {
+ c.closeConnection();
+ }
+ catch (IOException ioe)
+ {
+ // Ignore it. We are just cleaning up.
+ }
+ }
+ }
+ }
+ }
+
/**
* Creates a new request using this connection.
* @param method the HTTP method to invoke
@@ -378,7 +417,6 @@ public class HTTPConnection
ret.setHeader("Cookie", buf.toString());
}
}
- fireRequestEvent(RequestEvent.REQUEST_CREATED, ret);
return ret;
}
@@ -388,14 +426,7 @@ public class HTTPConnection
public void close()
throws IOException
{
- try
- {
- closeConnection();
- }
- finally
- {
- fireConnectionEvent(ConnectionEvent.CONNECTION_CLOSED);
- }
+ closeConnection();
}
/**
@@ -584,84 +615,6 @@ public class HTTPConnection
// -- Events --
- public void addConnectionListener(ConnectionListener l)
- {
- synchronized (connectionListeners)
- {
- connectionListeners.add(l);
- }
- }
-
- public void removeConnectionListener(ConnectionListener l)
- {
- synchronized (connectionListeners)
- {
- connectionListeners.remove(l);
- }
- }
-
- protected void fireConnectionEvent(int type)
- {
- ConnectionEvent event = new ConnectionEvent(this, type);
- ConnectionListener[] l = null;
- synchronized (connectionListeners)
- {
- l = new ConnectionListener[connectionListeners.size()];
- connectionListeners.toArray(l);
- }
- for (int i = 0; i < l.length; i++)
- {
- switch (type)
- {
- case ConnectionEvent.CONNECTION_CLOSED:
- l[i].connectionClosed(event);
- break;
- }
- }
- }
-
- public void addRequestListener(RequestListener l)
- {
- synchronized (requestListeners)
- {
- requestListeners.add(l);
- }
- }
-
- public void removeRequestListener(RequestListener l)
- {
- synchronized (requestListeners)
- {
- requestListeners.remove(l);
- }
- }
-
- protected void fireRequestEvent(int type, Request request)
- {
- RequestEvent event = new RequestEvent(this, type, request);
- RequestListener[] l = null;
- synchronized (requestListeners)
- {
- l = new RequestListener[requestListeners.size()];
- requestListeners.toArray(l);
- }
- for (int i = 0; i < l.length; i++)
- {
- switch (type)
- {
- case RequestEvent.REQUEST_CREATED:
- l[i].requestCreated(event);
- break;
- case RequestEvent.REQUEST_SENDING:
- l[i].requestSent(event);
- break;
- case RequestEvent.REQUEST_SENT:
- l[i].requestSent(event);
- break;
- }
- }
- }
-
void addHandshakeCompletedListener(HandshakeCompletedListener l)
{
synchronized (handshakeCompletedListeners)
Index: gnu/java/net/protocol/http/HTTPURLConnection.java
===================================================================
RCS file:
/cvsroot/classpath/classpath/gnu/java/net/protocol/http/HTTPURLConnection.java,v
retrieving revision 1.14
diff -u -p -r1.14 HTTPURLConnection.java
--- gnu/java/net/protocol/http/HTTPURLConnection.java 13 Jul 2005 23:22:30
-0000 1.14
+++ gnu/java/net/protocol/http/HTTPURLConnection.java 29 Sep 2005 23:05:14
-0000
@@ -37,8 +37,6 @@ exception statement from your version. *
package gnu.java.net.protocol.http;
-
-import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -75,7 +73,8 @@ public class HTTPURLConnection
/**
* Pool of reusable connections, used if keepAlive is true.
*/
- private static final Map connectionPool = new LinkedHashMap();
+ private static final LinkedHashMap connectionPool = new LinkedHashMap();
+ static int maxConnections;
/*
* The underlying connection.
@@ -87,7 +86,6 @@ public class HTTPURLConnection
int proxyPort;
String agent;
boolean keepAlive;
- int maxConnections;
private Request request;
private Headers requestHeaders;
@@ -95,8 +93,8 @@ public class HTTPURLConnection
private boolean requestMethodSetExplicitly;
private Response response;
- private ByteArrayInputStream responseSink;
- private ByteArrayInputStream errorSink;
+ private InputStream responseSink;
+ private InputStream errorSink;
private HandshakeCompletedEvent handshakeEvent;
@@ -218,8 +216,6 @@ public class HTTPURLConnection
RequestBodyWriter writer = new ByteArrayRequestBodyWriter(content);
request.setRequestBodyWriter(writer);
}
- ByteArrayResponseBodyReader reader = new ByteArrayResponseBodyReader();
- request.setResponseBodyReader(reader);
if (creds != null)
{
request.setAuthenticator(new Authenticator() {
@@ -307,7 +303,7 @@ public class HTTPURLConnection
}
else
{
- responseSink = new ByteArrayInputStream(reader.toByteArray ());
+ responseSink = response.getBody();
if (response.getCode() == 404)
{
errorSink = responseSink;
@@ -328,27 +324,14 @@ public class HTTPURLConnection
HTTPConnection connection;
if (keepAlive)
{
- StringBuffer buf = new StringBuffer(secure ? "https://" : "http://");
- buf.append(Thread.currentThread().hashCode());
- buf.append('@');
- buf.append(host);
- buf.append(':');
- buf.append(port);
- String key = buf.toString();
+ Object key = HTTPConnection.getPoolKey(host, port, secure);
synchronized (connectionPool)
{
- connection = (HTTPConnection) connectionPool.get(key);
+ connection = (HTTPConnection) connectionPool.remove(key);
if (connection == null)
{
connection = new HTTPConnection(host, port, secure);
- // Good housekeeping
- if (connectionPool.size() == maxConnections)
- {
- // maxConnections must always be >= 1
- Object lru = connectionPool.keySet().iterator().next();
- connectionPool.remove(lru);
- }
- connectionPool.put(key, connection);
+ connection.setPool(connectionPool);
}
}
}
@@ -504,7 +487,7 @@ public class HTTPURLConnection
}
Map headers = response.getHeaders();
Map ret = new LinkedHashMap();
- ret.put("", Collections.singletonList(getStatusLine(response)));
+ ret.put(null, Collections.singletonList(getStatusLine(response)));
for (Iterator i = headers.entrySet().iterator(); i.hasNext(); )
{
Map.Entry entry = (Map.Entry) i.next();
Index: gnu/java/net/protocol/http/Headers.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/net/protocol/http/Headers.java,v
retrieving revision 1.4
diff -u -p -r1.4 Headers.java
--- gnu/java/net/protocol/http/Headers.java 2 Jul 2005 20:32:13 -0000
1.4
+++ gnu/java/net/protocol/http/Headers.java 29 Sep 2005 23:05:14 -0000
@@ -205,6 +205,27 @@ public class Headers
}
/**
+ * Returns the value of the specified header as an integer,
+ * or -1 if the header is not present or not an integer.
+ */
+ public long getLongValue(String header)
+ {
+ String val = getValue(header);
+ if (val == null)
+ {
+ return -1;
+ }
+ try
+ {
+ return Long.parseLong(val);
+ }
+ catch (NumberFormatException e)
+ {
+ }
+ return -1;
+ }
+
+ /**
* Returns the value of the specified header as a date,
* or <code>null</code> if the header is not present or not a date.
*/
Index: gnu/java/net/protocol/http/LimitedLengthInputStream.java
===================================================================
RCS file: gnu/java/net/protocol/http/LimitedLengthInputStream.java
diff -N gnu/java/net/protocol/http/LimitedLengthInputStream.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gnu/java/net/protocol/http/LimitedLengthInputStream.java 29 Sep 2005
23:05:14 -0000
@@ -0,0 +1,229 @@
+/* LimitedLengthInputStream.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.net.protocol.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * InputStream that limits the total number of bytes that can be read
+ * from an underlying stream. In addition to limiting the number of
+ * bytes read, close() is not propagated to the underlying stream.
+ *
+ * @author David Daney ([EMAIL PROTECTED])
+ */
+class LimitedLengthInputStream
+ extends InputStream
+{
+ private long remainingLen;
+ private boolean restrictLen;
+ private HTTPConnection connection;
+ private boolean eof;
+ private InputStream in;
+ private boolean doClose;
+
+
+ private void handleClose()
+ throws IOException
+ {
+ eof = true;
+ if (doClose)
+ {
+ in.close();
+ }
+ else
+ {
+ connection.release();
+ }
+ in = null;
+ connection = null;
+ }
+
+ /**
+ * Constructor.
+ * @param in the underlying stream
+ * @param maxLen the maximum number of bytes to read
+ * @param con the HTTPConnection associated with this stream
+ * @param doClose if true con will be closed when finished reading,
+ * else it will be placed back in the connection pool.
+ */
+ LimitedLengthInputStream(InputStream in,
+ long maxLen,
+ boolean restrictLen,
+ HTTPConnection con,
+ boolean doClose)
+ throws IOException
+
+ {
+ this.in = in;
+ this.remainingLen = maxLen;
+ this.restrictLen = restrictLen;
+ this.connection = con;
+ this.doClose = doClose;
+
+ if (restrictLen)
+ {
+ if (maxLen < 0)
+ throw new IllegalArgumentException();
+ else if (maxLen == 0)
+ handleClose(); // Nothing to do, release the connection.
+ }
+ }
+
+ public synchronized int read()
+ throws IOException
+ {
+ if (eof)
+ return -1; // EOF
+
+ int r;
+
+ if (restrictLen)
+ {
+ r = in.read();
+ if (-1 != r)
+ remainingLen--;
+
+ if (0 == remainingLen)
+ handleClose();
+ }
+ else
+ {
+ r = in.read();
+ if (r == -1)
+ handleClose();
+ }
+
+ return r;
+ }
+
+ public int read(byte[] buffer)
+ throws IOException
+ {
+ return read(buffer, 0, buffer.length);
+ }
+
+ public synchronized int read(byte[] buffer, int offset, int length)
+ throws IOException
+ {
+ if (eof)
+ return -1; // EOF
+
+ if (restrictLen && length > remainingLen)
+ length = (int) remainingLen;
+
+ int r = in.read(buffer, offset, length);
+
+ if (-1 == r)
+ handleClose();
+
+ if (restrictLen && r > 0)
+ {
+ remainingLen -= r;
+ if (0 == remainingLen)
+ handleClose();
+ }
+ return r;
+ }
+
+ public synchronized long skip(long n)
+ throws IOException
+ {
+
+ if (eof)
+ return 0;
+
+ if (restrictLen && n > remainingLen)
+ n = remainingLen;
+
+ long r = in.skip(n);
+
+ if (restrictLen)
+ {
+ remainingLen -= r;
+ if (0 == remainingLen)
+ handleClose();
+ }
+ return r;
+ }
+
+ public synchronized int available()
+ throws IOException
+ {
+ if (eof)
+ return 0;
+
+ int a = in.available();
+ if (restrictLen && a > remainingLen)
+ a = (int)remainingLen;
+ return a;
+ }
+
+ public synchronized void close()
+ throws IOException
+ {
+ if (eof)
+ return;
+
+ try
+ {
+ while (restrictLen && remainingLen > 0)
+ {
+ long r = in.skip(remainingLen);
+ remainingLen -= r;
+ if (r == 0)
+ doClose = true; // Something is broken, close the stream.
+ }
+ }
+ catch (IOException ioe)
+ {
+ // Something is broken, close the stream.
+ doClose = true;
+ }
+
+ handleClose();
+ }
+
+ protected void finalize()
+ throws Throwable
+ {
+ close();
+ }
+
+}
Index: gnu/java/net/protocol/http/Request.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/net/protocol/http/Request.java,v
retrieving revision 1.5
diff -u -p -r1.5 Request.java
--- gnu/java/net/protocol/http/Request.java 2 Jul 2005 20:32:13 -0000
1.5
+++ gnu/java/net/protocol/http/Request.java 29 Sep 2005 23:05:14 -0000
@@ -1,5 +1,5 @@
/* Request.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -40,7 +40,6 @@ package gnu.java.net.protocol.http;
import gnu.java.net.BASE64;
import gnu.java.net.LineInputStream;
-import gnu.java.net.protocol.http.event.RequestEvent;
import java.io.IOException;
import java.io.InputStream;
@@ -100,11 +99,6 @@ public class Request
protected int requestBodyNegotiationThreshold;
/**
- * The response body reader.
- */
- protected ResponseBodyReader responseBodyReader;
-
- /**
* Map of response header handlers.
*/
protected Map responseHeaderHandlers;
@@ -236,16 +230,6 @@ public class Request
}
/**
- * Sets the response body reader.
- * @param responseBodyReader the handler to receive notifications of
- * response body content
- */
- public void setResponseBodyReader(ResponseBodyReader responseBodyReader)
- {
- this.responseBodyReader = responseBodyReader;
- }
-
- /**
* Sets a callback handler to be invoked for the specified header name.
* @param name the header name
* @param handler the handler to receive the value for the header
@@ -324,8 +308,6 @@ public class Request
do
{
retry = false;
- // Send request
- connection.fireRequestEvent(RequestEvent.REQUEST_SENDING, this);
// Get socket output and input streams
OutputStream out = connection.getOutputStream();
@@ -369,28 +351,42 @@ public class Request
count += len;
}
while (len > -1 && count < contentLength);
- out.write(CRLF.getBytes(US_ASCII));
}
out.flush();
- // Sent event
- connection.fireRequestEvent(RequestEvent.REQUEST_SENT, this);
// Get response
- response = readResponse(in);
- int sc = response.getCode();
- if (sc == 401 && authenticator != null)
- {
- if (authenticate(response, attempts++))
- {
- retry = true;
- }
- }
- else if (sc == 100 && expectingContinue)
- {
- requestHeaders.remove("Expect");
- setHeader("Content-Length", Integer.toString(contentLength));
- expectingContinue = false;
- retry = true;
- }
+ while(true)
+ {
+ response = readResponse(in);
+ int sc = response.getCode();
+ if (sc == 401 && authenticator != null)
+ {
+ if (authenticate(response, attempts++))
+ {
+ retry = true;
+ }
+ }
+ else if (sc == 100)
+ {
+ if (expectingContinue)
+ {
+ requestHeaders.remove("Expect");
+ setHeader("Content-Length",
+ Integer.toString(contentLength));
+ expectingContinue = false;
+ retry = true;
+ }
+ else
+ {
+ // A conforming server can send an unsoliceted
+ // Continue response but *should* not (RFC 2616
+ // sec 8.2.3). Ignore the bogus Continue
+ // response and get the real response that
+ // should follow
+ continue;
+ }
+ }
+ break;
+ }
}
while (retry);
}
@@ -440,28 +436,23 @@ public class Request
Headers responseHeaders = new Headers();
responseHeaders.parse(in);
notifyHeaderHandlers(responseHeaders);
- // Construct response
- int codeClass = code / 100;
- Response ret = new Response(majorVersion, minorVersion, code,
- codeClass, message, responseHeaders);
+ InputStream body = null;
+
switch (code)
{
+ case 100:
case 204:
case 205:
case 304:
break;
default:
- // Does response body reader want body?
- boolean notify = (responseBodyReader != null);
- if (notify)
- {
- if (!responseBodyReader.accept(this, ret))
- {
- notify = false;
- }
- }
- readResponseBody(ret, in, notify);
+ body = readResponseBody(responseHeaders, majorVersion,
+ minorVersion, in);
}
+
+ // Construct response
+ Response ret = new Response(majorVersion, minorVersion, code,
+ message, responseHeaders, body);
return ret;
}
@@ -487,25 +478,40 @@ public class Request
}
}
- void readResponseBody(Response response, InputStream in,
- boolean notify)
+ private InputStream readResponseBody(Headers responseHeaders,
+ int majorVersion,
+ int minorVersion,
+ InputStream in)
throws IOException
{
- byte[] buffer = new byte[4096];
- int contentLength = -1;
+ long contentLength = -1;
Headers trailer = null;
- String transferCoding = response.getHeader("Transfer-Encoding");
+ // Persistent connections are the default in HTTP/1.1
+ boolean doClose = "close".equalsIgnoreCase(getHeader("Connection")) ||
+ "close".equalsIgnoreCase(responseHeaders.getValue("Connection")) ||
+ (connection.majorVersion == 1 && connection.minorVersion == 0) ||
+ (majorVersion == 1 && minorVersion == 0);
+
+ String transferCoding = responseHeaders.getValue("Transfer-Encoding");
if ("chunked".equalsIgnoreCase(transferCoding))
{
- trailer = new Headers();
- in = new ChunkedInputStream(in, trailer);
+ in = new LimitedLengthInputStream(in, -1, false, connection, doClose);
+
+ in = new ChunkedInputStream(in, responseHeaders);
}
else
{
- contentLength = response.getIntHeader("Content-Length");
+ contentLength = responseHeaders.getLongValue("Content-Length");
+
+ if (contentLength < 0)
+ doClose = true; // No Content-Length, must close.
+
+ in = new LimitedLengthInputStream(in, contentLength,
+ contentLength >= 0,
+ connection, doClose);
}
- String contentCoding = response.getHeader("Content-Encoding");
+ String contentCoding = responseHeaders.getValue("Content-Encoding");
if (contentCoding != null && !"identity".equals(contentCoding))
{
if ("gzip".equals(contentCoding))
@@ -522,51 +528,7 @@ public class Request
contentCoding);
}
}
-
- // Persistent connections are the default in HTTP/1.1
- boolean doClose = "close".equalsIgnoreCase(getHeader("Connection")) ||
- "close".equalsIgnoreCase(response.getHeader("Connection")) ||
- (connection.majorVersion == 1 && connection.minorVersion == 0) ||
- (response.majorVersion == 1 && response.minorVersion == 0);
-
- int count = contentLength;
- int len = (count > -1) ? count : buffer.length;
- len = (len > buffer.length) ? buffer.length : len;
- while (len > -1)
- {
- len = in.read(buffer, 0, len);
- if (len < 0)
- {
- // EOF
- connection.closeConnection();
- break;
- }
- if (notify)
- {
- responseBodyReader.read(buffer, 0, len);
- }
- if (count > -1)
- {
- count -= len;
- if (count < 1)
- {
- if (doClose)
- {
- connection.closeConnection();
- }
- break;
- }
- }
- }
- if (notify)
- {
- responseBodyReader.close();
- }
- if (trailer != null)
- {
- response.getHeaders().putAll(trailer);
- notifyHeaderHandlers(trailer);
- }
+ return in;
}
boolean authenticate(Response response, int attempts)
Index: gnu/java/net/protocol/http/Response.java
===================================================================
RCS file:
/cvsroot/classpath/classpath/gnu/java/net/protocol/http/Response.java,v
retrieving revision 1.2
diff -u -p -r1.2 Response.java
--- gnu/java/net/protocol/http/Response.java 2 Jul 2005 20:32:13 -0000
1.2
+++ gnu/java/net/protocol/http/Response.java 29 Sep 2005 23:05:14 -0000
@@ -38,6 +38,7 @@ exception statement from your version. *
package gnu.java.net.protocol.http;
+import java.io.InputStream;
import java.util.Date;
/**
@@ -64,19 +65,6 @@ public class Response
protected final int code;
/**
- * The class of the response. This is the most significant digit of the
- * status code.
- * <dl>
- * <dt><code>1xx</code></dt> <dd>Informational response</dd>
- * <dt><code>2xx</code></dt> <dd>Success</dd>
- * <dt><code>3xx</code></dt> <dd>Redirection</dd>
- * <dt><code>4xx</code></dt> <dd>Client error</dd>
- * <dt><code>5xx</code></dt> <dd>Server error</dd>
- * </dl>
- */
- protected final int codeClass;
-
- /**
* Human-readable text of the response.
*/
protected final String message;
@@ -87,18 +75,22 @@ public class Response
protected final Headers headers;
/**
+ * An InputStream that returns the body of the response.
+ */
+ protected final InputStream body;
+
+ /**
* Constructs a new response with the specified parameters.
*/
protected Response(int majorVersion, int minorVersion, int code,
- int codeClass, String message,
- Headers headers)
+ String message, Headers headers, InputStream body)
{
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.code = code;
- this.codeClass = codeClass;
this.message = message;
this.headers = headers;
+ this.body = body;
}
/**
@@ -129,12 +121,19 @@ public class Response
}
/**
- * Returns the class of the response.
- * @see #codeClass
+ * Returns the class of the response. This is the most significant
+ * digit of the status code.
+ * <dl>
+ * <dt><code>1xx</code></dt> <dd>Informational response</dd>
+ * <dt><code>2xx</code></dt> <dd>Success</dd>
+ * <dt><code>3xx</code></dt> <dd>Redirection</dd>
+ * <dt><code>4xx</code></dt> <dd>Client error</dd>
+ * <dt><code>5xx</code></dt> <dd>Server error</dd>
+ * </dl>
*/
public int getCodeClass()
{
- return codeClass;
+ return code / 100;
}
/**
@@ -173,6 +172,15 @@ public class Response
}
/**
+ * Returns the header value for the specified name as a long.
+ * @param name the header name
+ */
+ public long getLongHeader(String name)
+ {
+ return headers.getLongValue(name);
+ }
+
+ /**
* Returns the header value for the specified name as a date.
* @param name the header name
*/
@@ -181,5 +189,14 @@ public class Response
return headers.getDateValue(name);
}
+ /**
+ * Returns an InputStream that returns the body of the response.
+ *
+ * @return the body of the response
+ */
+ public InputStream getBody()
+ {
+ return body;
+ }
}
Index: gnu/java/net/protocol/http/ResponseBodyReader.java
===================================================================
RCS file: gnu/java/net/protocol/http/ResponseBodyReader.java
diff -N gnu/java/net/protocol/http/ResponseBodyReader.java
--- gnu/java/net/protocol/http/ResponseBodyReader.java 2 Jul 2005 20:32:13
-0000 1.2
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,70 +0,0 @@
-/* ResponseBodyReader.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
-
-
-package gnu.java.net.protocol.http;
-
-/**
- * Callback interface for receiving notification of response body content.
- *
- * @author Chris Burdess ([EMAIL PROTECTED])
- */
-public interface ResponseBodyReader
-{
-
- /**
- * Indicate whether this reader is interested in the specified response.
- * If it returns false, it will not receive body content notifications for
- * that response.
- */
- boolean accept(Request request, Response response);
-
- /**
- * Receive notification of body content.
- * @param buffer the content buffer
- * @param offset the offset within the buffer that content starts
- * @param length the length of the content
- */
- void read(byte[] buffer, int offset, int length);
-
- /**
- * Notifies the reader that the end of the content was reached.
- */
- void close();
-
-}
-
Index: gnu/java/net/protocol/http/event/ConnectionEvent.java
===================================================================
RCS file: gnu/java/net/protocol/http/event/ConnectionEvent.java
diff -N gnu/java/net/protocol/http/event/ConnectionEvent.java
--- gnu/java/net/protocol/http/event/ConnectionEvent.java 2 Jul 2005
20:32:13 -0000 1.2
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,81 +0,0 @@
-/* ConnectionEvent.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
-
-
-package gnu.java.net.protocol.http.event;
-
-import java.util.EventObject;
-
-/**
- * A connection event.
- *
- * @author Chris Burdess ([EMAIL PROTECTED])
- */
-public class ConnectionEvent
- extends EventObject
-{
-
- /**
- * The connection closed event type.
- */
- public static final int CONNECTION_CLOSED = 0;
-
- /**
- * The type of this event.
- */
- protected int type;
-
- /**
- * Constructs a connection event with the specified source and type.
- */
- public ConnectionEvent(Object source, int type)
- {
- super(source);
- this.type = type;
- }
-
- /**
- * Returns the type of this event.
- * @see #type
- */
- public int getType()
- {
- return type;
- }
-
-}
-
Index: gnu/java/net/protocol/http/event/ConnectionListener.java
===================================================================
RCS file: gnu/java/net/protocol/http/event/ConnectionListener.java
diff -N gnu/java/net/protocol/http/event/ConnectionListener.java
--- gnu/java/net/protocol/http/event/ConnectionListener.java 2 Jul 2005
20:32:13 -0000 1.2
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,58 +0,0 @@
-/* ConnectionListener.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
-
-
-package gnu.java.net.protocol.http.event;
-
-import java.util.EventListener;
-
-/**
- * A connection listener.
- *
- * @author Chris Burdess ([EMAIL PROTECTED])
- */
-public interface ConnectionListener
- extends EventListener
-{
-
- /**
- * Callback invoked when the associated connection is closed.
- */
- void connectionClosed(ConnectionEvent event);
-
-}
-
Index: gnu/java/net/protocol/http/event/RequestEvent.java
===================================================================
RCS file: gnu/java/net/protocol/http/event/RequestEvent.java
diff -N gnu/java/net/protocol/http/event/RequestEvent.java
--- gnu/java/net/protocol/http/event/RequestEvent.java 2 Jul 2005 20:32:13
-0000 1.2
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,107 +0,0 @@
-/* RequestEvent.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
-
-
-package gnu.java.net.protocol.http.event;
-
-import gnu.java.net.protocol.http.Request;
-
-import java.util.EventObject;
-
-/**
- * A request event.
- *
- * @author Chris Burdess ([EMAIL PROTECTED])
- */
-public class RequestEvent
- extends EventObject
-{
-
- /**
- * The request created event type.
- */
- public static final int REQUEST_CREATED = 0;
-
- /**
- * The request sending event type.
- */
- public static final int REQUEST_SENDING = 1;
-
- /**
- * The request sent event type.
- */
- public static final int REQUEST_SENT = 2;
-
- /**
- * The type of this event.
- */
- protected int type;
-
- /**
- * The request associated with this event.
- */
- protected Request request;
-
- /**
- * Constructs a request event with the specified source, type, and request.
- */
- public RequestEvent(Object source, int type, Request request)
- {
- super(source);
- this.type = type;
- this.request = request;
- }
-
- /**
- * Returns the type of this event.
- * @see #type
- */
- public int getType()
- {
- return type;
- }
-
- /**
- * Returns the request associated with this event.
- */
- public Request getRequest()
- {
- return request;
- }
-
-}
-
Index: gnu/java/net/protocol/http/event/RequestListener.java
===================================================================
RCS file: gnu/java/net/protocol/http/event/RequestListener.java
diff -N gnu/java/net/protocol/http/event/RequestListener.java
--- gnu/java/net/protocol/http/event/RequestListener.java 2 Jul 2005
20:32:13 -0000 1.2
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,70 +0,0 @@
-/* RequestListener.java --
- Copyright (C) 2004 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
-
-
-package gnu.java.net.protocol.http.event;
-
-import java.util.EventListener;
-
-/**
- * A request listener.
- *
- * @author Chris Burdess ([EMAIL PROTECTED])
- */
-public interface RequestListener
- extends EventListener
-{
-
- /**
- * Callback invoked when a request is created from the associated
- * connection.
- */
- void requestCreated(RequestEvent event);
-
- /**
- * Callback invoked when the request has been initialised with all data
- * and before sending this data to the server.
- */
- void requestSending(RequestEvent event);
-
- /**
- * Callback invoked after all request data has been sent to the server.
- */
- void requestSent(RequestEvent event);
-
-}
-
Index: gnu/java/net/protocol/http/event/package.html
===================================================================
RCS file: gnu/java/net/protocol/http/event/package.html
diff -N gnu/java/net/protocol/http/event/package.html
--- gnu/java/net/protocol/http/event/package.html 2 Jul 2005 20:32:13
-0000 1.2
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,46 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
-<!-- package.html - describes classes in gnu.java.net.protocol.http.event
package.
- Copyright (C) 2005 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. -->
-
-<html>
-<head><title>GNU Classpath - gnu.java.net.protocol.http.event</title></head>
-
-<body>
-<p></p>
-
-</body>
-</html>
_______________________________________________
Classpath-patches mailing list
Classpath-patches@gnu.org
http://lists.gnu.org/mailman/listinfo/classpath-patches