diff --git a/src/examples/org/apache/http/examples/client/ClientExecuteDirect.java b/src/examples/org/apache/http/examples/client/ClientExecuteDirect.java index f174a4c21..872b70b6c 100644 --- a/src/examples/org/apache/http/examples/client/ClientExecuteDirect.java +++ b/src/examples/org/apache/http/examples/client/ClientExecuteDirect.java @@ -47,6 +47,7 @@ import org.apache.http.protocol.RequestContent; import org.apache.http.protocol.RequestExpectContinue; import org.apache.http.protocol.RequestTargetHost; import org.apache.http.protocol.RequestUserAgent; +//import org.apache.http.util.EntityUtils; import org.apache.http.conn.Scheme; import org.apache.http.conn.SchemeRegistry; @@ -113,12 +114,24 @@ public class ClientExecuteDirect { } System.out.println("----------------------------------------"); - //@@@ there is no entity, so we can't call close() there - //@@@ there is no need to call close() either, since the - //@@@ connection will have been released immediately + if (rsp.getEntity() != null) { + // the entity is probably tied to the connection + rsp.getEntity().getContent().close(); + // If you want to see the content, use the line below + // _instead_ of the close() call above. The utility class + // will read to the end of the stream and close it, + // thereby releasing the underlying connection. + //System.out.println(EntityUtils.toString(rsp.getEntity())); + //@@@ in case of an exception from EntityUtils.toString, + //@@@ there currently is no way of releasing the connection. + //@@@ The stream can be obtained from the entity only once. + } + // if there is no entity, the connection is already released } finally { //@@@ any kind of cleanup that should be performed? + //@@@ if content is read instead of being ignored, this is + //@@@ the place for connection release in case of an error } } // main @@ -187,7 +200,8 @@ public class ClientExecuteDirect { private final static HttpRequest createRequest(HttpHost target) { HttpRequest req = new BasicHttpRequest - ("OPTIONS", "*", HttpVersion.HTTP_1_1); + ("GET", "/", HttpVersion.HTTP_1_1); + //("OPTIONS", "*", HttpVersion.HTTP_1_1); req.addHeader("Host", target.getHostName()); diff --git a/src/examples/org/apache/http/examples/client/ClientExecuteProxy.java b/src/examples/org/apache/http/examples/client/ClientExecuteProxy.java index 86e40afd7..e810ce12e 100644 --- a/src/examples/org/apache/http/examples/client/ClientExecuteProxy.java +++ b/src/examples/org/apache/http/examples/client/ClientExecuteProxy.java @@ -47,6 +47,7 @@ import org.apache.http.protocol.RequestContent; import org.apache.http.protocol.RequestExpectContinue; import org.apache.http.protocol.RequestTargetHost; import org.apache.http.protocol.RequestUserAgent; +//import org.apache.http.util.EntityUtils; import org.apache.http.conn.Scheme; import org.apache.http.conn.SchemeRegistry; @@ -125,12 +126,24 @@ public class ClientExecuteProxy { } System.out.println("----------------------------------------"); - //@@@ there is no entity, so we can't call close() there - //@@@ there is no need to call close() either, since the - //@@@ connection will have been released immediately + if (rsp.getEntity() != null) { + // the entity is probably tied to the connection + rsp.getEntity().getContent().close(); + // If you want to see the content, use the line below + // _instead_ of the close() call above. The utility class + // will read to the end of the stream and close it, + // thereby releasing the underlying connection. + //System.out.println(EntityUtils.toString(rsp.getEntity())); + //@@@ in case of an exception from EntityUtils.toString, + //@@@ there currently is no way of releasing the connection. + //@@@ The stream can be obtained from the entity only once. + } + // if there is no entity, the connection is already released } finally { //@@@ any kind of cleanup that should be performed? + //@@@ if content is read instead of being ignored, this is + //@@@ the place for connection release in case of an error } } // main @@ -201,7 +214,8 @@ public class ClientExecuteProxy { private final static HttpRequest createRequest(HttpHost target) { HttpRequest req = new BasicHttpRequest - ("OPTIONS", "*", HttpVersion.HTTP_1_1); + ("GET", "/", HttpVersion.HTTP_1_1); + //("OPTIONS", "*", HttpVersion.HTTP_1_1); req.addHeader("Host", target.getHostName()); diff --git a/src/java/org/apache/http/conn/BasicEofSensorWatcher.java b/src/java/org/apache/http/conn/BasicEofSensorWatcher.java new file mode 100644 index 000000000..0fb6435e6 --- /dev/null +++ b/src/java/org/apache/http/conn/BasicEofSensorWatcher.java @@ -0,0 +1,122 @@ +/* + * $HeadURL$ + * $Revision $ + * $Date$ + * + * ==================================================================== + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +package org.apache.http.conn; + +import java.io.InputStream; +import java.io.IOException; + + +/** + * Basic implementation of {@link EofSensorWatcher EofSensorWatcher}. + * The underlying connection is released on close or EOF. + * + * @author Roland Weber + * + * + * + * @version $Revision$ + * + * @since 4.0 + */ +public class BasicEofSensorWatcher implements EofSensorWatcher { + + + /** The connection to auto-release. */ + protected ManagedClientConnection managedConn; + + /** The connection manager to release to. */ + protected ClientConnectionManager connManager; + + /** Whether to keep the connection alive. */ + protected boolean attemptReuse; + + + + /** + * Creates a new watcher for auto-releasing a connection. + * + * @param conn the connection to auto-release + * @param mgr the connection manager to release to + * @param reuse whether the connection should be re-used + */ + public BasicEofSensorWatcher(ManagedClientConnection conn, + ClientConnectionManager mgr, + boolean reuse) { + if (conn == null) + throw new IllegalArgumentException + ("Connection may not be null."); + if (mgr == null) + throw new IllegalArgumentException + ("Connection manager may not be null."); + + managedConn = conn; + //@@@ put a release method in the connection interface? + connManager = mgr; + attemptReuse = reuse; + } + + + // non-javadoc, see interface EofSensorWatcher + public boolean eofDetected(InputStream wrapped) + throws IOException { + + try { + if (attemptReuse) { + // there may be some cleanup required, such as + // reading trailers after the response body: + wrapped.close(); + managedConn.markReusable(); + } + } finally { + connManager.releaseConnection(managedConn); + } + return false; + } + + + // non-javadoc, see interface EofSensorWatcher + public boolean streamClosed(InputStream wrapped) + throws IOException { + + try { + if (attemptReuse) { + // this assumes that closing the stream will + // consume the remainder of the response body: + wrapped.close(); + managedConn.markReusable(); + } + } finally { + connManager.releaseConnection(managedConn); + } + return false; + } + +} // class BasicEofSensorWatcher diff --git a/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java b/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java index e0f5949e7..6cfc92f07 100644 --- a/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java +++ b/src/java/org/apache/http/impl/client/DefaultClientRequestDirector.java @@ -37,6 +37,8 @@ import org.apache.http.HttpVersion; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpException; +import org.apache.http.HttpEntity; +import org.apache.http.entity.BasicHttpEntity; import org.apache.http.message.BasicHttpRequest; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; @@ -45,6 +47,9 @@ import org.apache.http.conn.HttpRoute; import org.apache.http.conn.RouteDirector; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.ManagedClientConnection; +import org.apache.http.conn.EofSensorInputStream; +import org.apache.http.conn.EofSensorWatcher; +import org.apache.http.conn.BasicEofSensorWatcher; import org.apache.http.client.RoutedRequest; import org.apache.http.client.ClientRequestDirector; @@ -407,6 +412,25 @@ public class DefaultClientRequestDirector managedConn = null; mcc.markReusable(); connManager.releaseConnection(mcc); + } else { + HttpEntity entity = response.getEntity(); + if (!(entity instanceof BasicHttpEntity)) { + //@@@ fall back to an entity wrapper in this case? + managedConn = null; + connManager.releaseConnection(mcc); + throw new IllegalStateException + ("Unsupported entity type: " + + entity.getClass().getName()); + } + // install an auto-release handler + BasicHttpEntity bhe = (BasicHttpEntity) entity; + //@@@ evaluate connection re-use strategy + boolean reuse = false; + BasicEofSensorWatcher esw = new BasicEofSensorWatcher + (mcc, connManager, reuse); + EofSensorInputStream esis = new EofSensorInputStream + (bhe.getContent(), esw); + bhe.setContent(esis); } } else { // we got here as the result of an exception diff --git a/src/java/org/apache/http/impl/client/DefaultHttpClient.java b/src/java/org/apache/http/impl/client/DefaultHttpClient.java index 3473fc9e5..8eb2b7725 100644 --- a/src/java/org/apache/http/impl/client/DefaultHttpClient.java +++ b/src/java/org/apache/http/impl/client/DefaultHttpClient.java @@ -123,14 +123,13 @@ public class DefaultHttpClient extends AbstractHttpClient { ClientRequestDirector director = createDirector(context); HttpResponse response = director.execute(roureq, context); + // If the response depends on the connection, the director + // will have set up an auto-release input stream. + //@@@ or move that logic here into the client? //@@@ "finalize" response, to allow for buffering of entities? //@@@ here or in director? - System.out.println("@@@ what about "+director.getConnection()+"?"); - //@@@ return a "connected response" with a release() callback? - //@@@ use a special response entity to implement the release()? - return response; } // execute