Issue #6728 - QUIC and HTTP/3

- More fixes and improvement to HTTP client transport tests.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2021-10-27 20:55:11 +02:00
parent aac4232e20
commit baab1a15b9
6 changed files with 41 additions and 39 deletions

View File

@ -24,7 +24,7 @@ public class LeakTrackingConnectionPool extends DuplexConnectionPool
{
private static final Logger LOG = LoggerFactory.getLogger(LeakTrackingConnectionPool.class);
private final LeakDetector<Connection> leakDetector = new LeakDetector<Connection>()
private final LeakDetector<Connection> leakDetector = new LeakDetector<>()
{
@Override
protected void leaked(LeakInfo leakInfo)
@ -35,7 +35,7 @@ public class LeakTrackingConnectionPool extends DuplexConnectionPool
public LeakTrackingConnectionPool(HttpDestination destination, int maxConnections, Callback requester)
{
super((HttpDestination)destination, maxConnections, requester);
super(destination, maxConnections, requester);
addBean(leakDetector);
}
@ -60,7 +60,7 @@ public class LeakTrackingConnectionPool extends DuplexConnectionPool
LOG.info("Connection {}@{} released but not acquired", connection, leakDetector.id(connection));
}
protected void leaked(LeakDetector.LeakInfo leakInfo)
protected void leaked(LeakDetector<Connection>.LeakInfo leakInfo)
{
LOG.info("Connection {} leaked at:", leakInfo.getResourceDescription(), leakInfo.getStackFrames());
}

View File

@ -18,6 +18,7 @@ import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.LeakTrackingConnectionPool;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ByteBufferPool;
@ -71,7 +72,7 @@ public abstract class AbstractHttpClientServerTest
transport.setConnectionPoolFactory(destination -> new LeakTrackingConnectionPool(destination, client.getMaxConnectionsPerDestination(), destination)
{
@Override
protected void leaked(LeakDetector.LeakInfo leakInfo)
protected void leaked(LeakDetector<Connection>.LeakInfo leakInfo)
{
connectionLeaks.incrementAndGet();
}

View File

@ -26,7 +26,6 @@ import org.eclipse.jetty.quic.common.QuicConnection;
import org.eclipse.jetty.quic.common.QuicSession;
import org.eclipse.jetty.quic.quiche.QuicheConfig;
import org.eclipse.jetty.quic.quiche.QuicheConnection;
import org.eclipse.jetty.quic.quiche.ffi.LibQuiche;
import org.eclipse.jetty.quic.server.internal.SimpleTokenMinter;
import org.eclipse.jetty.quic.server.internal.SimpleTokenValidator;
import org.eclipse.jetty.server.Connector;
@ -70,8 +69,7 @@ public class ServerQuicConnection extends QuicConnection
QuicheConnection quicheConnection = QuicheConnection.tryAccept(quicheConfig, new SimpleTokenValidator((InetSocketAddress)remoteAddress), cipherBuffer, remoteAddress);
if (quicheConnection == null)
{
// TODO make the buffer size configurable
ByteBuffer negotiationBuffer = byteBufferPool.acquire(LibQuiche.QUICHE_MIN_CLIENT_INITIAL_LEN, true);
ByteBuffer negotiationBuffer = byteBufferPool.acquire(getOutputBufferSize(), true);
int pos = BufferUtil.flipToFill(negotiationBuffer);
// TODO make the token minter configurable
if (!QuicheConnection.negotiate(new SimpleTokenMinter((InetSocketAddress)remoteAddress), cipherBuffer, negotiationBuffer))

View File

@ -126,7 +126,9 @@ public class HttpClientDemandTest extends AbstractTest<TransportScenario>
{
init(transport);
int bufferSize = 512;
// A small buffer size so the response content is
// read in multiple buffers, but big enough for HTTP/3.
int bufferSize = 1536;
byte[] content = new byte[10 * bufferSize];
new Random().nextBytes(content);
scenario.startServer(new EmptyServerHandler()
@ -140,7 +142,6 @@ public class HttpClientDemandTest extends AbstractTest<TransportScenario>
});
scenario.startClient(client ->
{
// A small buffer size so the response content is read in multiple buffers.
client.setByteBufferPool(new MappedByteBufferPool(bufferSize));
client.setResponseBufferSize(bufferSize);
});
@ -287,7 +288,7 @@ public class HttpClientDemandTest extends AbstractTest<TransportScenario>
{
init(transport);
int bufferSize = 512;
int bufferSize = 1536;
byte[] content = new byte[10 * bufferSize];
new Random().nextBytes(content);
scenario.startServer(new EmptyServerHandler()

View File

@ -31,18 +31,17 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.LeakTrackingConnectionPool;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http3.server.HTTP3ServerConnector;
import org.eclipse.jetty.io.ArrayByteBufferPool;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.LeakTrackingByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.server.Connector;
@ -368,58 +367,54 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
int selectors = Math.min(1, ProcessorUtils.availableProcessors() / 2);
ByteBufferPool byteBufferPool = new ArrayByteBufferPool();
byteBufferPool = new LeakTrackingByteBufferPool(byteBufferPool);
if (transport == Transport.UNIX_DOMAIN)
switch (transport)
{
UnixDomainServerConnector unixSocketConnector = new UnixDomainServerConnector(server, null, null, byteBufferPool, 1, selectors, provideServerConnectionFactory(transport));
unixSocketConnector.setUnixDomainPath(unixDomainPath);
return unixSocketConnector;
case HTTP:
case HTTPS:
case H2C:
case H2:
case FCGI:
return new ServerConnector(server, null, null, byteBufferPool, 1, selectors, provideServerConnectionFactory(transport));
case H3:
return new HTTP3ServerConnector(server, null, null, byteBufferPool, sslContextFactory, provideServerConnectionFactory(transport));
case UNIX_DOMAIN:
UnixDomainServerConnector unixSocketConnector = new UnixDomainServerConnector(server, null, null, byteBufferPool, 1, selectors, provideServerConnectionFactory(transport));
unixSocketConnector.setUnixDomainPath(unixDomainPath);
return unixSocketConnector;
default:
throw new IllegalStateException();
}
return new ServerConnector(server, null, null, byteBufferPool, 1, selectors, provideServerConnectionFactory(transport));
}
@Override
public HttpClientTransport provideClientTransport(Transport transport, SslContextFactory.Client sslContextFactory)
{
HttpClientTransport clientTransport = super.provideClientTransport(transport, sslContextFactory);
switch (transport)
{
case HTTP:
case HTTPS:
case FCGI:
case UNIX_DOMAIN:
{
ClientConnector clientConnector = new ClientConnector();
clientConnector.setSelectors(1);
clientConnector.setSslContextFactory(sslContextFactory);
HttpClientTransport clientTransport = new HttpClientTransportOverHTTP(clientConnector);
// Track connection leaking only for non-multiplexed transports.
clientTransport.setConnectionPoolFactory(destination -> new LeakTrackingConnectionPool(destination, client.getMaxConnectionsPerDestination(), destination)
{
@Override
protected void leaked(LeakDetector.LeakInfo leakInfo)
protected void leaked(LeakDetector<Connection>.LeakInfo leakInfo)
{
super.leaked(leakInfo);
connectionLeaks.incrementAndGet();
}
});
return clientTransport;
}
case FCGI:
{
HttpClientTransport clientTransport = new HttpClientTransportOverFCGI(1, "");
clientTransport.setConnectionPoolFactory(destination -> new LeakTrackingConnectionPool(destination, client.getMaxConnectionsPerDestination(), destination)
{
@Override
protected void leaked(LeakDetector.LeakInfo leakInfo)
{
super.leaked(leakInfo);
connectionLeaks.incrementAndGet();
}
});
return clientTransport;
break;
}
default:
{
return super.provideClientTransport(transport, sslContextFactory);
break;
}
}
return clientTransport;
}
}
}

View File

@ -58,6 +58,7 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
@ -890,6 +891,9 @@ public class HttpClientStreamTest extends AbstractTest<TransportScenario>
@ArgumentsSource(TransportProvider.class)
public void testUploadWithOutputStreamFailureToConnect(Transport transport) throws Exception
{
// Failure to connect is based on InetSocket address failure, which Unix-Domain does not use.
Assumptions.assumeTrue(transport != Transport.UNIX_DOMAIN);
init(transport);
long connectTimeout = 1000;
@ -972,6 +976,9 @@ public class HttpClientStreamTest extends AbstractTest<TransportScenario>
@ArgumentsSource(TransportProvider.class)
public void testUploadWithConnectFailureClosesStream(Transport transport) throws Exception
{
// Failure to connect is based on InetSocket address failure, which Unix-Domain does not use.
Assumptions.assumeTrue(transport != Transport.UNIX_DOMAIN);
init(transport);
long connectTimeout = 1000;