HTTPCLIENT-1120: moved test cases for abort handling to TestAbortHandling
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1179593 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a38281b7f2
commit
b1b85307a8
|
@ -66,7 +66,10 @@ import org.apache.http.protocol.HttpContext;
|
||||||
* expected to tolerate multiple calls to the release method.
|
* expected to tolerate multiple calls to the release method.
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
|
*
|
||||||
|
* @deprecated do not use
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
public abstract class AbstractClientConnAdapter implements ManagedClientConnection, HttpContext {
|
public abstract class AbstractClientConnAdapter implements ManagedClientConnection, HttpContext {
|
||||||
|
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
*
|
|
||||||
* 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
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.http.impl.client;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
|
||||||
import org.apache.http.HttpRequest;
|
|
||||||
import org.apache.http.HttpResponse;
|
|
||||||
import org.apache.http.HttpStatus;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
|
||||||
import org.apache.http.entity.StringEntity;
|
|
||||||
import org.apache.http.localserver.BasicServerTestBase;
|
|
||||||
import org.apache.http.localserver.LocalTestServer;
|
|
||||||
import org.apache.http.protocol.BasicHttpContext;
|
|
||||||
import org.apache.http.protocol.ExecutionContext;
|
|
||||||
import org.apache.http.protocol.HttpContext;
|
|
||||||
import org.apache.http.protocol.HttpRequestHandler;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for Abort handling.
|
|
||||||
*/
|
|
||||||
public class TestAbortHandling extends BasicServerTestBase {
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() throws Exception {
|
|
||||||
this.localServer = new LocalTestServer(null, null);
|
|
||||||
this.localServer.registerDefaultHandlers();
|
|
||||||
this.localServer.start();
|
|
||||||
this.httpclient = new DefaultHttpClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAbortRetry_HTTPCLIENT_1120() throws Exception {
|
|
||||||
int port = this.localServer.getServiceAddress().getPort();
|
|
||||||
final CountDownLatch wait = new CountDownLatch(1);
|
|
||||||
|
|
||||||
this.localServer.register("*", new HttpRequestHandler(){
|
|
||||||
public void handle(HttpRequest request, HttpResponse response,
|
|
||||||
HttpContext context) throws HttpException, IOException {
|
|
||||||
try {
|
|
||||||
wait.countDown(); // trigger abort
|
|
||||||
Thread.sleep(2000); // allow time for abort to happen
|
|
||||||
response.setStatusCode(HttpStatus.SC_OK);
|
|
||||||
StringEntity entity = new StringEntity("Whatever");
|
|
||||||
response.setEntity(entity);
|
|
||||||
} catch (Exception e) {
|
|
||||||
response.setStatusCode(HttpStatus.SC_REQUEST_TIMEOUT);
|
|
||||||
}
|
|
||||||
}});
|
|
||||||
|
|
||||||
String s = "http://localhost:" + port + "/path";
|
|
||||||
final HttpGet httpget = new HttpGet(s);
|
|
||||||
|
|
||||||
Thread t = new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run(){
|
|
||||||
try {
|
|
||||||
wait.await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
httpget.abort();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
t.start();
|
|
||||||
|
|
||||||
HttpContext context = new BasicHttpContext();
|
|
||||||
try {
|
|
||||||
this.httpclient.execute(getServerHttp(), httpget, context);
|
|
||||||
} catch (IOException e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequest reqWrapper = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
|
|
||||||
Assert.assertNotNull("Request should exist",reqWrapper);
|
|
||||||
Assert.assertEquals(1,((RequestWrapper) reqWrapper).getExecCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO add similar test for connection abort
|
|
||||||
}
|
|
|
@ -27,10 +27,6 @@ package org.apache.http.impl.client;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import org.apache.http.Header;
|
import org.apache.http.Header;
|
||||||
import org.apache.http.HttpClientConnection;
|
import org.apache.http.HttpClientConnection;
|
||||||
|
@ -40,33 +36,16 @@ import org.apache.http.HttpRequest;
|
||||||
import org.apache.http.HttpRequestInterceptor;
|
import org.apache.http.HttpRequestInterceptor;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.apache.http.ProtocolVersion;
|
|
||||||
import org.apache.http.client.ClientProtocolException;
|
import org.apache.http.client.ClientProtocolException;
|
||||||
import org.apache.http.client.HttpRequestRetryHandler;
|
import org.apache.http.client.HttpRequestRetryHandler;
|
||||||
import org.apache.http.client.NonRepeatableRequestException;
|
import org.apache.http.client.NonRepeatableRequestException;
|
||||||
import org.apache.http.client.methods.AbortableHttpRequest;
|
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.client.params.ClientPNames;
|
import org.apache.http.client.params.ClientPNames;
|
||||||
import org.apache.http.conn.ClientConnectionManager;
|
|
||||||
import org.apache.http.conn.ClientConnectionRequest;
|
|
||||||
import org.apache.http.conn.ConnectionPoolTimeoutException;
|
|
||||||
import org.apache.http.conn.ConnectionReleaseTrigger;
|
|
||||||
import org.apache.http.conn.ManagedClientConnection;
|
|
||||||
import org.apache.http.conn.routing.HttpRoute;
|
|
||||||
import org.apache.http.conn.scheme.PlainSocketFactory;
|
|
||||||
import org.apache.http.conn.scheme.Scheme;
|
|
||||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
|
||||||
import org.apache.http.entity.InputStreamEntity;
|
import org.apache.http.entity.InputStreamEntity;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.impl.conn.ClientConnAdapterMockup;
|
|
||||||
import org.apache.http.impl.conn.PoolingClientConnectionManager;
|
|
||||||
import org.apache.http.localserver.BasicServerTestBase;
|
import org.apache.http.localserver.BasicServerTestBase;
|
||||||
import org.apache.http.localserver.LocalTestServer;
|
import org.apache.http.localserver.LocalTestServer;
|
||||||
import org.apache.http.message.BasicHeader;
|
|
||||||
import org.apache.http.mockup.SocketFactoryMockup;
|
|
||||||
import org.apache.http.params.BasicHttpParams;
|
|
||||||
import org.apache.http.params.HttpParams;
|
|
||||||
import org.apache.http.protocol.BasicHttpContext;
|
import org.apache.http.protocol.BasicHttpContext;
|
||||||
import org.apache.http.protocol.ExecutionContext;
|
import org.apache.http.protocol.ExecutionContext;
|
||||||
import org.apache.http.protocol.HttpContext;
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
@ -90,441 +69,6 @@ public class TestDefaultClientRequestDirector extends BasicServerTestBase {
|
||||||
this.httpclient = new DefaultHttpClient();
|
this.httpclient = new DefaultHttpClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that if abort is called on an {@link AbortableHttpRequest} while
|
|
||||||
* {@link DefaultRequestDirector} is allocating a connection, that the
|
|
||||||
* connection is properly aborted.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testAbortInAllocate() throws Exception {
|
|
||||||
CountDownLatch connLatch = new CountDownLatch(1);
|
|
||||||
CountDownLatch awaitLatch = new CountDownLatch(1);
|
|
||||||
final ConMan conMan = new ConMan(connLatch, awaitLatch);
|
|
||||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
|
|
||||||
final CountDownLatch getLatch = new CountDownLatch(1);
|
|
||||||
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
|
||||||
final HttpContext context = new BasicHttpContext();
|
|
||||||
final HttpGet httpget = new HttpGet("http://www.example.com/a");
|
|
||||||
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
client.execute(httpget, context);
|
|
||||||
} catch(Throwable t) {
|
|
||||||
throwableRef.set(t);
|
|
||||||
} finally {
|
|
||||||
getLatch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
httpget.abort();
|
|
||||||
|
|
||||||
Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
|
|
||||||
Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
|
|
||||||
throwableRef.get() instanceof IOException);
|
|
||||||
Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(),
|
|
||||||
throwableRef.get().getCause() instanceof InterruptedException);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that an abort called after the connection has been retrieved
|
|
||||||
* but before a release trigger is set does still abort the request.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testAbortAfterAllocateBeforeRequest() throws Exception {
|
|
||||||
this.localServer.register("*", new BasicService());
|
|
||||||
|
|
||||||
CountDownLatch releaseLatch = new CountDownLatch(1);
|
|
||||||
SchemeRegistry registry = new SchemeRegistry();
|
|
||||||
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
|
||||||
|
|
||||||
PoolingClientConnectionManager conMan = new PoolingClientConnectionManager(registry);
|
|
||||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
|
|
||||||
final CountDownLatch getLatch = new CountDownLatch(1);
|
|
||||||
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
|
||||||
final HttpContext context = new BasicHttpContext();
|
|
||||||
final HttpGet httpget = new CustomGet("a", releaseLatch);
|
|
||||||
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
client.execute(getServerHttp(), httpget, context);
|
|
||||||
} catch(Throwable t) {
|
|
||||||
throwableRef.set(t);
|
|
||||||
} finally {
|
|
||||||
getLatch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
Thread.sleep(100); // Give it a little time to proceed to release...
|
|
||||||
|
|
||||||
httpget.abort();
|
|
||||||
|
|
||||||
releaseLatch.countDown();
|
|
||||||
|
|
||||||
Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
|
|
||||||
Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
|
|
||||||
throwableRef.get() instanceof IOException);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that an abort called completely before execute
|
|
||||||
* still aborts the request.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testAbortBeforeExecute() throws Exception {
|
|
||||||
this.localServer.register("*", new BasicService());
|
|
||||||
|
|
||||||
SchemeRegistry registry = new SchemeRegistry();
|
|
||||||
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
|
||||||
|
|
||||||
PoolingClientConnectionManager conMan = new PoolingClientConnectionManager(registry);
|
|
||||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
|
|
||||||
final CountDownLatch getLatch = new CountDownLatch(1);
|
|
||||||
final CountDownLatch startLatch = new CountDownLatch(1);
|
|
||||||
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
|
||||||
final HttpContext context = new BasicHttpContext();
|
|
||||||
final HttpGet httpget = new HttpGet("a");
|
|
||||||
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
if(!startLatch.await(1, TimeUnit.SECONDS))
|
|
||||||
throw new RuntimeException("Took too long to start!");
|
|
||||||
} catch(InterruptedException interrupted) {
|
|
||||||
throw new RuntimeException("Never started!", interrupted);
|
|
||||||
}
|
|
||||||
client.execute(getServerHttp(), httpget, context);
|
|
||||||
} catch(Throwable t) {
|
|
||||||
throwableRef.set(t);
|
|
||||||
} finally {
|
|
||||||
getLatch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
httpget.abort();
|
|
||||||
startLatch.countDown();
|
|
||||||
|
|
||||||
Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
|
|
||||||
Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
|
|
||||||
throwableRef.get() instanceof IOException);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that an abort called after a redirect has found a new host
|
|
||||||
* still aborts in the correct place (while trying to get the new
|
|
||||||
* host's route, not while doing the subsequent request).
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testAbortAfterRedirectedRoute() throws Exception {
|
|
||||||
final int port = this.localServer.getServiceAddress().getPort();
|
|
||||||
this.localServer.register("*", new BasicRedirectService(port));
|
|
||||||
|
|
||||||
SchemeRegistry registry = new SchemeRegistry();
|
|
||||||
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
|
||||||
|
|
||||||
CountDownLatch connLatch = new CountDownLatch(1);
|
|
||||||
CountDownLatch awaitLatch = new CountDownLatch(1);
|
|
||||||
ConnMan4 conMan = new ConnMan4(registry, connLatch, awaitLatch);
|
|
||||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
|
|
||||||
final CountDownLatch getLatch = new CountDownLatch(1);
|
|
||||||
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
|
||||||
final HttpContext context = new BasicHttpContext();
|
|
||||||
final HttpGet httpget = new HttpGet("a");
|
|
||||||
|
|
||||||
new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
HttpHost host = new HttpHost("127.0.0.1", port);
|
|
||||||
client.execute(host, httpget, context);
|
|
||||||
} catch(Throwable t) {
|
|
||||||
throwableRef.set(t);
|
|
||||||
} finally {
|
|
||||||
getLatch.countDown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
httpget.abort();
|
|
||||||
|
|
||||||
Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
|
|
||||||
Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
|
|
||||||
throwableRef.get() instanceof IOException);
|
|
||||||
Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(),
|
|
||||||
throwableRef.get().getCause() instanceof InterruptedException);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests that if a socket fails to connect, the allocated connection is
|
|
||||||
* properly released back to the connection manager.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testSocketConnectFailureReleasesConnection() throws Exception {
|
|
||||||
final ConnMan2 conMan = new ConnMan2();
|
|
||||||
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
|
||||||
final HttpContext context = new BasicHttpContext();
|
|
||||||
final HttpGet httpget = new HttpGet("http://www.example.com/a");
|
|
||||||
|
|
||||||
try {
|
|
||||||
client.execute(httpget, context);
|
|
||||||
Assert.fail("expected IOException");
|
|
||||||
} catch(IOException expected) {}
|
|
||||||
|
|
||||||
Assert.assertNotNull(conMan.allocatedConnection);
|
|
||||||
Assert.assertSame(conMan.allocatedConnection, conMan.releasedConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BasicService implements HttpRequestHandler {
|
|
||||||
public void handle(final HttpRequest request,
|
|
||||||
final HttpResponse response,
|
|
||||||
final HttpContext context) throws HttpException, IOException {
|
|
||||||
response.setStatusCode(200);
|
|
||||||
response.setEntity(new StringEntity("Hello World"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BasicRedirectService implements HttpRequestHandler {
|
|
||||||
private int statuscode = HttpStatus.SC_SEE_OTHER;
|
|
||||||
private int port;
|
|
||||||
|
|
||||||
public BasicRedirectService(int port) {
|
|
||||||
this.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handle(final HttpRequest request,
|
|
||||||
final HttpResponse response, final HttpContext context)
|
|
||||||
throws HttpException, IOException {
|
|
||||||
ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
|
|
||||||
response.setStatusLine(ver, this.statuscode);
|
|
||||||
response.addHeader(new BasicHeader("Location", "http://localhost:"
|
|
||||||
+ this.port + "/newlocation/"));
|
|
||||||
response.addHeader(new BasicHeader("Connection", "close"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ConnMan4 extends PoolingClientConnectionManager {
|
|
||||||
private final CountDownLatch connLatch;
|
|
||||||
private final CountDownLatch awaitLatch;
|
|
||||||
|
|
||||||
public ConnMan4(SchemeRegistry schreg,
|
|
||||||
CountDownLatch connLatch, CountDownLatch awaitLatch) {
|
|
||||||
super(schreg);
|
|
||||||
this.connLatch = connLatch;
|
|
||||||
this.awaitLatch = awaitLatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientConnectionRequest requestConnection(HttpRoute route, Object state) {
|
|
||||||
// If this is the redirect route, stub the return value
|
|
||||||
// so-as to pretend the host is waiting on a slot...
|
|
||||||
if(route.getTargetHost().getHostName().equals("localhost")) {
|
|
||||||
final Thread currentThread = Thread.currentThread();
|
|
||||||
|
|
||||||
return new ClientConnectionRequest() {
|
|
||||||
|
|
||||||
public void abortRequest() {
|
|
||||||
currentThread.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ManagedClientConnection getConnection(
|
|
||||||
long timeout, TimeUnit tunit)
|
|
||||||
throws InterruptedException,
|
|
||||||
ConnectionPoolTimeoutException {
|
|
||||||
connLatch.countDown(); // notify waiter that we're getting a connection
|
|
||||||
|
|
||||||
// zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
|
|
||||||
if(timeout == 0)
|
|
||||||
timeout = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
if(!awaitLatch.await(timeout, tunit))
|
|
||||||
throw new ConnectionPoolTimeoutException();
|
|
||||||
|
|
||||||
return new ClientConnAdapterMockup(ConnMan4.this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return super.requestConnection(route, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static class ConnMan2 implements ClientConnectionManager {
|
|
||||||
|
|
||||||
private ManagedClientConnection allocatedConnection;
|
|
||||||
private ManagedClientConnection releasedConnection;
|
|
||||||
|
|
||||||
public ConnMan2() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeIdleConnections(long idletime, TimeUnit tunit) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeExpiredConnections() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ManagedClientConnection getConnection(HttpRoute route) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ManagedClientConnection getConnection(HttpRoute route,
|
|
||||||
long timeout, TimeUnit tunit) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientConnectionRequest requestConnection(
|
|
||||||
final HttpRoute route,
|
|
||||||
final Object state) {
|
|
||||||
|
|
||||||
return new ClientConnectionRequest() {
|
|
||||||
|
|
||||||
public void abortRequest() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ManagedClientConnection getConnection(
|
|
||||||
long timeout, TimeUnit unit)
|
|
||||||
throws InterruptedException,
|
|
||||||
ConnectionPoolTimeoutException {
|
|
||||||
allocatedConnection = new ClientConnAdapterMockup(ConnMan2.this) {
|
|
||||||
@Override
|
|
||||||
public void open(HttpRoute route, HttpContext context,
|
|
||||||
HttpParams params) throws IOException {
|
|
||||||
throw new ConnectException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return allocatedConnection;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpParams getParams() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public SchemeRegistry getSchemeRegistry() {
|
|
||||||
SchemeRegistry registry = new SchemeRegistry();
|
|
||||||
registry.register(new Scheme("http", 80, new SocketFactoryMockup(null)));
|
|
||||||
return registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
|
|
||||||
this.releasedConnection = conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ConMan implements ClientConnectionManager {
|
|
||||||
private final CountDownLatch connLatch;
|
|
||||||
private final CountDownLatch awaitLatch;
|
|
||||||
|
|
||||||
public ConMan(CountDownLatch connLatch, CountDownLatch awaitLatch) {
|
|
||||||
this.connLatch = connLatch;
|
|
||||||
this.awaitLatch = awaitLatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeIdleConnections(long idletime, TimeUnit tunit) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeExpiredConnections() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ManagedClientConnection getConnection(HttpRoute route) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ManagedClientConnection getConnection(HttpRoute route,
|
|
||||||
long timeout, TimeUnit tunit) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientConnectionRequest requestConnection(
|
|
||||||
final HttpRoute route,
|
|
||||||
final Object state) {
|
|
||||||
|
|
||||||
final Thread currentThread = Thread.currentThread();
|
|
||||||
|
|
||||||
return new ClientConnectionRequest() {
|
|
||||||
|
|
||||||
public void abortRequest() {
|
|
||||||
currentThread.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ManagedClientConnection getConnection(
|
|
||||||
long timeout, TimeUnit tunit)
|
|
||||||
throws InterruptedException,
|
|
||||||
ConnectionPoolTimeoutException {
|
|
||||||
connLatch.countDown(); // notify waiter that we're getting a connection
|
|
||||||
|
|
||||||
// zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
|
|
||||||
if(timeout == 0)
|
|
||||||
timeout = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
if(!awaitLatch.await(timeout, tunit))
|
|
||||||
throw new ConnectionPoolTimeoutException();
|
|
||||||
|
|
||||||
return new ClientConnAdapterMockup(ConMan.this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpParams getParams() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public SchemeRegistry getSchemeRegistry() {
|
|
||||||
SchemeRegistry registry = new SchemeRegistry();
|
|
||||||
registry.register(new Scheme("http", 80, new SocketFactoryMockup(null)));
|
|
||||||
return registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class CustomGet extends HttpGet {
|
|
||||||
private final CountDownLatch releaseTriggerLatch;
|
|
||||||
|
|
||||||
public CustomGet(String uri, CountDownLatch releaseTriggerLatch) {
|
|
||||||
super(uri);
|
|
||||||
this.releaseTriggerLatch = releaseTriggerLatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setReleaseTrigger(ConnectionReleaseTrigger releaseTrigger) throws IOException {
|
|
||||||
try {
|
|
||||||
if(!releaseTriggerLatch.await(1, TimeUnit.SECONDS))
|
|
||||||
throw new RuntimeException("Waited too long...");
|
|
||||||
} catch(InterruptedException ie) {
|
|
||||||
throw new RuntimeException(ie);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.setReleaseTrigger(releaseTrigger);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SimpleService implements HttpRequestHandler {
|
private static class SimpleService implements HttpRequestHandler {
|
||||||
|
|
||||||
public SimpleService() {
|
public SimpleService() {
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* ====================================================================
|
|
||||||
* 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
|
|
||||||
* <http://www.apache.org/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.http.impl.conn;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.apache.http.HttpHost;
|
|
||||||
import org.apache.http.conn.ClientConnectionManager;
|
|
||||||
import org.apache.http.conn.routing.HttpRoute;
|
|
||||||
import org.apache.http.params.HttpParams;
|
|
||||||
import org.apache.http.protocol.HttpContext;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mockup connection adapter.
|
|
||||||
*/
|
|
||||||
public class ClientConnAdapterMockup extends AbstractClientConnAdapter {
|
|
||||||
|
|
||||||
public ClientConnAdapterMockup(ClientConnectionManager mgr) {
|
|
||||||
super(mgr, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpRoute getRoute() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void layerProtocol(HttpContext context, HttpParams params) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void open(HttpRoute route, HttpContext context, HttpParams params) throws IOException {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tunnelTarget(boolean secure, HttpParams params) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tunnelProxy(HttpHost next, boolean secure, HttpParams params) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getState() {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setState(Object state) {
|
|
||||||
throw new UnsupportedOperationException("just a mockup");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,510 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.http.impl.conn;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.HttpHost;
|
||||||
|
import org.apache.http.HttpRequest;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.apache.http.ProtocolVersion;
|
||||||
|
import org.apache.http.client.methods.AbortableHttpRequest;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.conn.ClientConnectionManager;
|
||||||
|
import org.apache.http.conn.ClientConnectionRequest;
|
||||||
|
import org.apache.http.conn.ConnectionPoolTimeoutException;
|
||||||
|
import org.apache.http.conn.ConnectionReleaseTrigger;
|
||||||
|
import org.apache.http.conn.ManagedClientConnection;
|
||||||
|
import org.apache.http.conn.routing.HttpRoute;
|
||||||
|
import org.apache.http.conn.scheme.PlainSocketFactory;
|
||||||
|
import org.apache.http.conn.scheme.Scheme;
|
||||||
|
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.apache.http.impl.client.DefaultRequestDirector;
|
||||||
|
import org.apache.http.impl.client.RequestWrapper;
|
||||||
|
import org.apache.http.localserver.BasicServerTestBase;
|
||||||
|
import org.apache.http.localserver.LocalTestServer;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.mockup.SocketFactoryMockup;
|
||||||
|
import org.apache.http.params.BasicHttpParams;
|
||||||
|
import org.apache.http.params.HttpParams;
|
||||||
|
import org.apache.http.protocol.BasicHttpContext;
|
||||||
|
import org.apache.http.protocol.ExecutionContext;
|
||||||
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
import org.apache.http.protocol.HttpRequestHandler;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for Abort handling.
|
||||||
|
*/
|
||||||
|
public class TestAbortHandling extends BasicServerTestBase {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
this.localServer = new LocalTestServer(null, null);
|
||||||
|
this.localServer.registerDefaultHandlers();
|
||||||
|
this.localServer.start();
|
||||||
|
this.httpclient = new DefaultHttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortRetry_HTTPCLIENT_1120() throws Exception {
|
||||||
|
int port = this.localServer.getServiceAddress().getPort();
|
||||||
|
final CountDownLatch wait = new CountDownLatch(1);
|
||||||
|
|
||||||
|
this.localServer.register("*", new HttpRequestHandler(){
|
||||||
|
public void handle(HttpRequest request, HttpResponse response,
|
||||||
|
HttpContext context) throws HttpException, IOException {
|
||||||
|
try {
|
||||||
|
wait.countDown(); // trigger abort
|
||||||
|
Thread.sleep(2000); // allow time for abort to happen
|
||||||
|
response.setStatusCode(HttpStatus.SC_OK);
|
||||||
|
StringEntity entity = new StringEntity("Whatever");
|
||||||
|
response.setEntity(entity);
|
||||||
|
} catch (Exception e) {
|
||||||
|
response.setStatusCode(HttpStatus.SC_REQUEST_TIMEOUT);
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
|
||||||
|
String s = "http://localhost:" + port + "/path";
|
||||||
|
final HttpGet httpget = new HttpGet(s);
|
||||||
|
|
||||||
|
Thread t = new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run(){
|
||||||
|
try {
|
||||||
|
wait.await();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
httpget.abort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
HttpContext context = new BasicHttpContext();
|
||||||
|
try {
|
||||||
|
this.httpclient.execute(getServerHttp(), httpget, context);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequest reqWrapper = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
|
||||||
|
Assert.assertNotNull("Request should exist",reqWrapper);
|
||||||
|
Assert.assertEquals(1,((RequestWrapper) reqWrapper).getExecCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that if abort is called on an {@link AbortableHttpRequest} while
|
||||||
|
* {@link DefaultRequestDirector} is allocating a connection, that the
|
||||||
|
* connection is properly aborted.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAbortInAllocate() throws Exception {
|
||||||
|
CountDownLatch connLatch = new CountDownLatch(1);
|
||||||
|
CountDownLatch awaitLatch = new CountDownLatch(1);
|
||||||
|
final ConMan conMan = new ConMan(connLatch, awaitLatch);
|
||||||
|
final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
|
||||||
|
final CountDownLatch getLatch = new CountDownLatch(1);
|
||||||
|
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
||||||
|
final HttpContext context = new BasicHttpContext();
|
||||||
|
final HttpGet httpget = new HttpGet("http://www.example.com/a");
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
client.execute(httpget, context);
|
||||||
|
} catch(Throwable t) {
|
||||||
|
throwableRef.set(t);
|
||||||
|
} finally {
|
||||||
|
getLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
|
||||||
|
|
||||||
|
httpget.abort();
|
||||||
|
|
||||||
|
Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
|
||||||
|
throwableRef.get() instanceof IOException);
|
||||||
|
Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(),
|
||||||
|
throwableRef.get().getCause() instanceof InterruptedException);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that an abort called after the connection has been retrieved
|
||||||
|
* but before a release trigger is set does still abort the request.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAbortAfterAllocateBeforeRequest() throws Exception {
|
||||||
|
this.localServer.register("*", new BasicService());
|
||||||
|
|
||||||
|
CountDownLatch releaseLatch = new CountDownLatch(1);
|
||||||
|
SchemeRegistry registry = new SchemeRegistry();
|
||||||
|
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
||||||
|
|
||||||
|
PoolingClientConnectionManager conMan = new PoolingClientConnectionManager(registry);
|
||||||
|
final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
|
||||||
|
final CountDownLatch getLatch = new CountDownLatch(1);
|
||||||
|
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
||||||
|
final HttpContext context = new BasicHttpContext();
|
||||||
|
final HttpGet httpget = new CustomGet("a", releaseLatch);
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
client.execute(getServerHttp(), httpget, context);
|
||||||
|
} catch(Throwable t) {
|
||||||
|
throwableRef.set(t);
|
||||||
|
} finally {
|
||||||
|
getLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
Thread.sleep(100); // Give it a little time to proceed to release...
|
||||||
|
|
||||||
|
httpget.abort();
|
||||||
|
|
||||||
|
releaseLatch.countDown();
|
||||||
|
|
||||||
|
Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
|
||||||
|
throwableRef.get() instanceof IOException);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that an abort called completely before execute
|
||||||
|
* still aborts the request.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAbortBeforeExecute() throws Exception {
|
||||||
|
this.localServer.register("*", new BasicService());
|
||||||
|
|
||||||
|
SchemeRegistry registry = new SchemeRegistry();
|
||||||
|
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
||||||
|
|
||||||
|
PoolingClientConnectionManager conMan = new PoolingClientConnectionManager(registry);
|
||||||
|
final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
|
||||||
|
final CountDownLatch getLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch startLatch = new CountDownLatch(1);
|
||||||
|
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
||||||
|
final HttpContext context = new BasicHttpContext();
|
||||||
|
final HttpGet httpget = new HttpGet("a");
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
if(!startLatch.await(1, TimeUnit.SECONDS))
|
||||||
|
throw new RuntimeException("Took too long to start!");
|
||||||
|
} catch(InterruptedException interrupted) {
|
||||||
|
throw new RuntimeException("Never started!", interrupted);
|
||||||
|
}
|
||||||
|
client.execute(getServerHttp(), httpget, context);
|
||||||
|
} catch(Throwable t) {
|
||||||
|
throwableRef.set(t);
|
||||||
|
} finally {
|
||||||
|
getLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
httpget.abort();
|
||||||
|
startLatch.countDown();
|
||||||
|
|
||||||
|
Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
|
||||||
|
throwableRef.get() instanceof IOException);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that an abort called after a redirect has found a new host
|
||||||
|
* still aborts in the correct place (while trying to get the new
|
||||||
|
* host's route, not while doing the subsequent request).
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testAbortAfterRedirectedRoute() throws Exception {
|
||||||
|
final int port = this.localServer.getServiceAddress().getPort();
|
||||||
|
this.localServer.register("*", new BasicRedirectService(port));
|
||||||
|
|
||||||
|
SchemeRegistry registry = new SchemeRegistry();
|
||||||
|
registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
|
||||||
|
|
||||||
|
CountDownLatch connLatch = new CountDownLatch(1);
|
||||||
|
CountDownLatch awaitLatch = new CountDownLatch(1);
|
||||||
|
ConnMan4 conMan = new ConnMan4(registry, connLatch, awaitLatch);
|
||||||
|
final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
|
||||||
|
final CountDownLatch getLatch = new CountDownLatch(1);
|
||||||
|
final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
|
||||||
|
final HttpContext context = new BasicHttpContext();
|
||||||
|
final HttpGet httpget = new HttpGet("a");
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
HttpHost host = new HttpHost("127.0.0.1", port);
|
||||||
|
client.execute(host, httpget, context);
|
||||||
|
} catch(Throwable t) {
|
||||||
|
throwableRef.set(t);
|
||||||
|
} finally {
|
||||||
|
getLatch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
|
||||||
|
|
||||||
|
httpget.abort();
|
||||||
|
|
||||||
|
Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
|
||||||
|
throwableRef.get() instanceof IOException);
|
||||||
|
Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(),
|
||||||
|
throwableRef.get().getCause() instanceof InterruptedException);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that if a socket fails to connect, the allocated connection is
|
||||||
|
* properly released back to the connection manager.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSocketConnectFailureReleasesConnection() throws Exception {
|
||||||
|
ManagedClientConnection conn = Mockito.mock(ManagedClientConnection.class);
|
||||||
|
Mockito.doThrow(new ConnectException()).when(conn).open(
|
||||||
|
Mockito.any(HttpRoute.class),
|
||||||
|
Mockito.any(HttpContext.class),
|
||||||
|
Mockito.any(HttpParams.class));
|
||||||
|
ClientConnectionRequest connrequest = Mockito.mock(ClientConnectionRequest.class);
|
||||||
|
Mockito.when(connrequest.getConnection(
|
||||||
|
Mockito.anyInt(), Mockito.any(TimeUnit.class))).thenReturn(conn);
|
||||||
|
ClientConnectionManager connmgr = Mockito.mock(ClientConnectionManager.class);
|
||||||
|
|
||||||
|
SchemeRegistry schemeRegistry = SchemeRegistryFactory.createDefault();
|
||||||
|
|
||||||
|
Mockito.when(connmgr.requestConnection(
|
||||||
|
Mockito.any(HttpRoute.class), Mockito.any())).thenReturn(connrequest);
|
||||||
|
Mockito.when(connmgr.getSchemeRegistry()).thenReturn(schemeRegistry);
|
||||||
|
|
||||||
|
final DefaultHttpClient client = new DefaultHttpClient(connmgr, new BasicHttpParams());
|
||||||
|
final HttpContext context = new BasicHttpContext();
|
||||||
|
final HttpGet httpget = new HttpGet("http://www.example.com/a");
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.execute(httpget, context);
|
||||||
|
Assert.fail("expected IOException");
|
||||||
|
} catch(IOException expected) {}
|
||||||
|
|
||||||
|
Mockito.verify(conn).abortConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BasicService implements HttpRequestHandler {
|
||||||
|
public void handle(final HttpRequest request,
|
||||||
|
final HttpResponse response,
|
||||||
|
final HttpContext context) throws HttpException, IOException {
|
||||||
|
response.setStatusCode(200);
|
||||||
|
response.setEntity(new StringEntity("Hello World"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BasicRedirectService implements HttpRequestHandler {
|
||||||
|
private int statuscode = HttpStatus.SC_SEE_OTHER;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
public BasicRedirectService(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(final HttpRequest request,
|
||||||
|
final HttpResponse response, final HttpContext context)
|
||||||
|
throws HttpException, IOException {
|
||||||
|
ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
|
||||||
|
response.setStatusLine(ver, this.statuscode);
|
||||||
|
response.addHeader(new BasicHeader("Location", "http://localhost:"
|
||||||
|
+ this.port + "/newlocation/"));
|
||||||
|
response.addHeader(new BasicHeader("Connection", "close"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ConnMan4 extends PoolingClientConnectionManager {
|
||||||
|
private final CountDownLatch connLatch;
|
||||||
|
private final CountDownLatch awaitLatch;
|
||||||
|
|
||||||
|
public ConnMan4(SchemeRegistry schreg,
|
||||||
|
CountDownLatch connLatch, CountDownLatch awaitLatch) {
|
||||||
|
super(schreg);
|
||||||
|
this.connLatch = connLatch;
|
||||||
|
this.awaitLatch = awaitLatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientConnectionRequest requestConnection(HttpRoute route, Object state) {
|
||||||
|
// If this is the redirect route, stub the return value
|
||||||
|
// so-as to pretend the host is waiting on a slot...
|
||||||
|
if(route.getTargetHost().getHostName().equals("localhost")) {
|
||||||
|
final Thread currentThread = Thread.currentThread();
|
||||||
|
|
||||||
|
return new ClientConnectionRequest() {
|
||||||
|
|
||||||
|
public void abortRequest() {
|
||||||
|
currentThread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagedClientConnection getConnection(
|
||||||
|
long timeout, TimeUnit tunit)
|
||||||
|
throws InterruptedException,
|
||||||
|
ConnectionPoolTimeoutException {
|
||||||
|
connLatch.countDown(); // notify waiter that we're getting a connection
|
||||||
|
|
||||||
|
// zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
|
||||||
|
if(timeout == 0)
|
||||||
|
timeout = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
if(!awaitLatch.await(timeout, tunit))
|
||||||
|
throw new ConnectionPoolTimeoutException();
|
||||||
|
|
||||||
|
return Mockito.mock(ManagedClientConnection.class);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return super.requestConnection(route, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class ConMan implements ClientConnectionManager {
|
||||||
|
private final CountDownLatch connLatch;
|
||||||
|
private final CountDownLatch awaitLatch;
|
||||||
|
|
||||||
|
public ConMan(CountDownLatch connLatch, CountDownLatch awaitLatch) {
|
||||||
|
this.connLatch = connLatch;
|
||||||
|
this.awaitLatch = awaitLatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeIdleConnections(long idletime, TimeUnit tunit) {
|
||||||
|
throw new UnsupportedOperationException("just a mockup");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeExpiredConnections() {
|
||||||
|
throw new UnsupportedOperationException("just a mockup");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagedClientConnection getConnection(HttpRoute route) {
|
||||||
|
throw new UnsupportedOperationException("just a mockup");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagedClientConnection getConnection(HttpRoute route,
|
||||||
|
long timeout, TimeUnit tunit) {
|
||||||
|
throw new UnsupportedOperationException("just a mockup");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientConnectionRequest requestConnection(
|
||||||
|
final HttpRoute route,
|
||||||
|
final Object state) {
|
||||||
|
|
||||||
|
final Thread currentThread = Thread.currentThread();
|
||||||
|
|
||||||
|
return new ClientConnectionRequest() {
|
||||||
|
|
||||||
|
public void abortRequest() {
|
||||||
|
currentThread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagedClientConnection getConnection(
|
||||||
|
long timeout, TimeUnit tunit)
|
||||||
|
throws InterruptedException,
|
||||||
|
ConnectionPoolTimeoutException {
|
||||||
|
connLatch.countDown(); // notify waiter that we're getting a connection
|
||||||
|
|
||||||
|
// zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
|
||||||
|
if(timeout == 0)
|
||||||
|
timeout = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
if(!awaitLatch.await(timeout, tunit))
|
||||||
|
throw new ConnectionPoolTimeoutException();
|
||||||
|
|
||||||
|
return Mockito.mock(ManagedClientConnection.class);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpParams getParams() {
|
||||||
|
throw new UnsupportedOperationException("just a mockup");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SchemeRegistry getSchemeRegistry() {
|
||||||
|
SchemeRegistry registry = new SchemeRegistry();
|
||||||
|
registry.register(new Scheme("http", 80, new SocketFactoryMockup(null)));
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
|
||||||
|
throw new UnsupportedOperationException("just a mockup");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
throw new UnsupportedOperationException("just a mockup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CustomGet extends HttpGet {
|
||||||
|
private final CountDownLatch releaseTriggerLatch;
|
||||||
|
|
||||||
|
public CustomGet(String uri, CountDownLatch releaseTriggerLatch) {
|
||||||
|
super(uri);
|
||||||
|
this.releaseTriggerLatch = releaseTriggerLatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReleaseTrigger(ConnectionReleaseTrigger releaseTrigger) throws IOException {
|
||||||
|
try {
|
||||||
|
if(!releaseTriggerLatch.await(1, TimeUnit.SECONDS))
|
||||||
|
throw new RuntimeException("Waited too long...");
|
||||||
|
} catch(InterruptedException ie) {
|
||||||
|
throw new RuntimeException(ie);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setReleaseTrigger(releaseTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ import org.apache.http.params.HttpParams;
|
||||||
import org.apache.http.params.HttpProtocolParams;
|
import org.apache.http.params.HttpProtocolParams;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for <code>ThreadSafeClientConnManager</code> that do not require
|
* Tests for <code>ThreadSafeClientConnManager</code> that do not require
|
||||||
|
@ -211,7 +212,8 @@ public class TestTSCCMNoServer {
|
||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
mgr.releaseConnection(new ClientConnAdapterMockup(null), -1, null);
|
ManagedClientConnection conn = Mockito.mock(ManagedClientConnection.class);
|
||||||
|
mgr.releaseConnection(conn, -1, null);
|
||||||
Assert.fail("foreign connection adapter not detected");
|
Assert.fail("foreign connection adapter not detected");
|
||||||
} catch (IllegalArgumentException iax) {
|
} catch (IllegalArgumentException iax) {
|
||||||
// expected
|
// expected
|
||||||
|
|
Loading…
Reference in New Issue