Skip to content

Commit

Permalink
Implemented HTTP2Client connect timeout.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbordet committed Feb 9, 2015
1 parent 85edb7e commit f974c74
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 186 deletions.
Expand Up @@ -88,6 +88,7 @@ public void succeeded(Connection result)
public void failed(Throwable x)
{
connect.set(ConnectState.DISCONNECTED);
abort(x);
}

protected boolean process(final C connection, boolean dispatch)
Expand Down
Expand Up @@ -28,7 +28,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLEngine;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -299,114 +298,6 @@ protected boolean onReadTimeout()
}
}

@Slow
@Test
public void testConnectTimeoutFailsRequest() throws Exception
{
String host = "10.255.255.1";
int port = 80;
int connectTimeout = 1000;
assumeConnectTimeout(host, port, connectTimeout);

start(new EmptyServerHandler());
client.stop();
client.setConnectTimeout(connectTimeout);
client.start();

final CountDownLatch latch = new CountDownLatch(1);
Request request = client.newRequest(host, port);
request.scheme(scheme)
.send(new Response.CompleteListener()
{
@Override
public void onComplete(Result result)
{
if (result.isFailed())
latch.countDown();
}
});

Assert.assertTrue(latch.await(2 * connectTimeout, TimeUnit.MILLISECONDS));
Assert.assertNotNull(request.getAbortCause());
}

@Slow
@Test
public void testConnectTimeoutIsCancelledByShorterRequestTimeout() throws Exception
{
String host = "10.255.255.1";
int port = 80;
int connectTimeout = 2000;
assumeConnectTimeout(host, port, connectTimeout);

start(new EmptyServerHandler());
client.stop();
client.setConnectTimeout(connectTimeout);
client.start();

final AtomicInteger completes = new AtomicInteger();
final CountDownLatch latch = new CountDownLatch(2);
Request request = client.newRequest(host, port);
request.scheme(scheme)
.timeout(connectTimeout / 2, TimeUnit.MILLISECONDS)
.send(new Response.CompleteListener()
{
@Override
public void onComplete(Result result)
{
completes.incrementAndGet();
latch.countDown();
}
});

Assert.assertFalse(latch.await(2 * connectTimeout, TimeUnit.MILLISECONDS));
Assert.assertEquals(1, completes.get());
Assert.assertNotNull(request.getAbortCause());
}

@Test
public void retryAfterConnectTimeout() throws Exception
{
final String host = "10.255.255.1";
final int port = 80;
int connectTimeout = 1000;
assumeConnectTimeout(host, port, connectTimeout);

start(new EmptyServerHandler());
client.stop();
client.setConnectTimeout(connectTimeout);
client.start();

final CountDownLatch latch = new CountDownLatch(1);
Request request = client.newRequest(host, port);
request.scheme(scheme)
.send(new Response.CompleteListener()
{
@Override
public void onComplete(Result result)
{
if (result.isFailed())
{
// Retry
client.newRequest(host, port)
.scheme(scheme)
.send(new Response.CompleteListener()
{
@Override
public void onComplete(Result result)
{
if (result.isFailed())
latch.countDown();
}
});
}
}
});

Assert.assertTrue(latch.await(333 * connectTimeout, TimeUnit.MILLISECONDS));
Assert.assertNotNull(request.getAbortCause());
}

@Test
public void testVeryShortTimeout() throws Exception
{
Expand Down
Expand Up @@ -176,6 +176,9 @@ public long getConnectTimeout()
public void setConnectTimeout(long connectTimeout)
{
this.connectTimeout = connectTimeout;
SelectorManager selector = this.selector;
if (selector != null)
selector.setConnectTimeout(connectTimeout);
}

public void connect(InetSocketAddress address, Session.Listener listener, Promise<Session> promise)
Expand Down Expand Up @@ -265,5 +268,21 @@ public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object

return factory.newConnection(endpoint, context);
}

@Override
protected void connectionFailed(SocketChannel channel, Throwable failure, Object attachment)
{
@SuppressWarnings("unchecked")
Map<String, Object> context = (Map<String, Object>)attachment;
if (LOG.isDebugEnabled())
{
Object host = context.get(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY);
Object port = context.get(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY);
LOG.debug("Could not connect to {}:{}", host, port);
}
@SuppressWarnings("unchecked")
Promise<Session> promise = (Promise<Session>)context.get(HTTP2ClientConnectionFactory.SESSION_PROMISE_CONTEXT_KEY);
promise.failed(failure);
}
}
}
@@ -0,0 +1,86 @@
//
// ========================================================================
// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//

package org.eclipse.jetty.http2.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.util.Promise;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;

public class ConnectTimeoutTest extends AbstractTest
{
@Test
public void testConnectTimeout() throws Exception
{
final String host = "10.255.255.1";
final int port = 80;
int connectTimeout = 1000;
assumeConnectTimeout(host, port, connectTimeout);

start(new ServerSessionListener.Adapter());
client.setConnectTimeout(connectTimeout);

InetSocketAddress address = new InetSocketAddress(host, port);
final CountDownLatch latch = new CountDownLatch(1);
client.connect(address, new Session.Listener.Adapter(), new Promise.Adapter<Session>()
{
@Override
public void failed(Throwable x)
{
Assert.assertTrue(x instanceof SocketTimeoutException);
latch.countDown();
}
});

Assert.assertTrue(latch.await(2 * connectTimeout, TimeUnit.MILLISECONDS));
}

private void assumeConnectTimeout(String host, int port, int connectTimeout) throws IOException
{
try (Socket socket = new Socket())
{
// Try to connect to a private address in the 10.x.y.z range.
// These addresses are usually not routed, so an attempt to
// connect to them will hang the connection attempt, which is
// what we want to simulate in this test.
socket.connect(new InetSocketAddress(host, port), connectTimeout);
// Abort the test if we can connect.
Assume.assumeTrue(false);
}
catch (SocketTimeoutException x)
{
// Expected timeout during connect, continue the test.
Assume.assumeTrue(true);
}
catch (Throwable x)
{
// Abort if any other exception happens.
Assume.assumeTrue(false);
}
}
}
Expand Up @@ -58,6 +58,8 @@ public HttpDestination newHttpDestination(Origin origin)
@Override
public void connect(SocketAddress address, Map<String, Object> context)
{
client.setConnectTimeout(httpClient.getConnectTimeout());

final HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
@SuppressWarnings("unchecked")
final Promise<Connection> connection = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
Expand Down Expand Up @@ -85,6 +87,7 @@ public void failed(Throwable failure)
connection.failed(failure);
}
};

client.connect(httpClient.getSslContextFactory(), (InetSocketAddress)address, listener, promise, context);
}

Expand Down
Expand Up @@ -49,9 +49,10 @@ public abstract class AbstractTest

static
{
http2Client = new HTTP2Client();
QueuedThreadPool clientThreads = new QueuedThreadPool();
clientThreads.setName("h2-client");
http2Client = HTTP2Client.builder().executor(clientThreads).build();
http2Client.setExecutor(clientThreads);
}

@Parameterized.Parameters(name = "{index}: mod:{0}")
Expand Down

0 comments on commit f974c74

Please sign in to comment.