Skip to content

Commit

Permalink
Fixes #12348 - HttpClientTransportDynamic does not initialize low-lev…
Browse files Browse the repository at this point in the history
…el clients.

Fixed initialization for HTTP/2 and HTTP/3.

Signed-off-by: Simone Bordet <[email protected]>
  • Loading branch information
sbordet committed Oct 4, 2024
1 parent c2c2735 commit 3f903e2
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1147,4 +1147,13 @@ public void close() throws Exception
{
stop();
}

/**
* <p>Implementations of this interface are made aware of
* the {@code HttpClient} instance while it is starting.</p>
*/
public interface Aware
{
void setHttpClient(HttpClient httpClient);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* but the HTTP exchange may also be carried using the FCGI protocol, the HTTP/2 protocol or,
* in future, other protocols.
*/
public interface HttpClientTransport extends ClientConnectionFactory
public interface HttpClientTransport extends ClientConnectionFactory, HttpClient.Aware
{
public static final String HTTP_DESTINATION_CONTEXT_KEY = "org.eclipse.jetty.client.destination";
public static final String HTTP_CONNECTION_PROMISE_CONTEXT_KEY = "org.eclipse.jetty.client.connection.promise";
Expand All @@ -45,6 +45,7 @@ public interface HttpClientTransport extends ClientConnectionFactory
*
* @param client the {@link HttpClient} that uses this transport.
*/
@Override
public void setHttpClient(HttpClient client);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
import org.eclipse.jetty.client.AbstractConnectorHttpClientTransport;
import org.eclipse.jetty.client.Destination;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.MultiplexConnectionPool;
import org.eclipse.jetty.client.Origin;
Expand Down Expand Up @@ -129,6 +130,16 @@ private static ClientConnector findClientConnector(ClientConnectionFactory.Info[
.orElseGet(ClientConnector::new);
}

@Override
public void setHttpClient(HttpClient client)
{
super.setHttpClient(client);
infos.stream()
.filter(info -> info instanceof HttpClient.Aware)
.map(HttpClient.Aware.class::cast)
.forEach(info -> info.setHttpClient(getHttpClient()));
}

@Override
public Origin newOrigin(Request request)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Map;

import org.eclipse.jetty.client.Connection;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.transport.HttpClientConnectionFactory;
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
Expand All @@ -35,7 +36,7 @@
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;

public class ClientConnectionFactoryOverHTTP2 extends ContainerLifeCycle implements ClientConnectionFactory
public class ClientConnectionFactoryOverHTTP2 extends ContainerLifeCycle implements ClientConnectionFactory, HttpClient.Aware
{
private final ClientConnectionFactory factory = new HTTP2ClientConnectionFactory();
private final HTTP2Client http2Client;
Expand All @@ -46,6 +47,12 @@ public ClientConnectionFactoryOverHTTP2(HTTP2Client http2Client)
installBean(http2Client);
}

@Override
public void setHttpClient(HttpClient httpClient)
{
HttpClientTransportOverHTTP2.configure(httpClient, http2Client);
}

@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
Expand All @@ -61,7 +68,7 @@ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<Stri
*
* @see HttpClientConnectionFactory#HTTP11
*/
public static class HTTP2 extends Info
public static class HTTP2 extends Info implements HttpClient.Aware
{
private static final List<String> protocols = List.of("h2", "h2c");
private static final List<String> h2c = List.of("h2c");
Expand All @@ -71,6 +78,13 @@ public HTTP2(HTTP2Client http2Client)
super(new ClientConnectionFactoryOverHTTP2(http2Client));
}

@Override
public void setHttpClient(HttpClient httpClient)
{
ClientConnectionFactoryOverHTTP2 factory = (ClientConnectionFactoryOverHTTP2)getClientConnectionFactory();
factory.setHttpClient(httpClient);
}

@Override
public List<String> getProtocols(boolean secure)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,25 @@ public void setUseALPN(boolean useALPN)
protected void doStart() throws Exception
{
if (!http2Client.isStarted())
{
HttpClient httpClient = getHttpClient();
http2Client.setExecutor(httpClient.getExecutor());
http2Client.setScheduler(httpClient.getScheduler());
http2Client.setByteBufferPool(httpClient.getByteBufferPool());
http2Client.setConnectTimeout(httpClient.getConnectTimeout());
http2Client.setIdleTimeout(httpClient.getIdleTimeout());
http2Client.setInputBufferSize(httpClient.getResponseBufferSize());
http2Client.setUseInputDirectByteBuffers(httpClient.isUseInputDirectByteBuffers());
http2Client.setUseOutputDirectByteBuffers(httpClient.isUseOutputDirectByteBuffers());
http2Client.setConnectBlocking(httpClient.isConnectBlocking());
http2Client.setBindAddress(httpClient.getBindAddress());
}
configure(getHttpClient(), getHTTP2Client());
super.doStart();
}

static void configure(HttpClient httpClient, HTTP2Client http2Client)
{
http2Client.setExecutor(httpClient.getExecutor());
http2Client.setScheduler(httpClient.getScheduler());
http2Client.setByteBufferPool(httpClient.getByteBufferPool());
http2Client.setConnectTimeout(httpClient.getConnectTimeout());
http2Client.setIdleTimeout(httpClient.getIdleTimeout());
http2Client.setInputBufferSize(httpClient.getResponseBufferSize());
http2Client.setUseInputDirectByteBuffers(httpClient.isUseInputDirectByteBuffers());
http2Client.setUseOutputDirectByteBuffers(httpClient.isUseOutputDirectByteBuffers());
http2Client.setConnectBlocking(httpClient.isConnectBlocking());
http2Client.setBindAddress(httpClient.getBindAddress());
http2Client.setMaxResponseHeadersSize(httpClient.getMaxResponseHeadersSize());
}

@Override
public Origin newOrigin(Request request)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.Destination;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpProxy;
import org.eclipse.jetty.client.InputStreamResponseListener;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.Response;
import org.eclipse.jetty.client.Result;
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
Expand All @@ -60,6 +62,7 @@
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2;
import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.http2.client.transport.internal.HttpChannelOverHTTP2;
import org.eclipse.jetty.http2.client.transport.internal.HttpConnectionOverHTTP2;
Expand Down Expand Up @@ -105,10 +108,24 @@
public class HttpClientTransportOverHTTP2Test extends AbstractTest
{
@Test
public void testPropertiesAreForwarded() throws Exception
public void testPropertiesAreForwardedOverHTTP2() throws Exception
{
HTTP2Client http2Client = new HTTP2Client();
try (HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP2(http2Client)))
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
testPropertiesAreForwarded(http2Client, new HttpClientTransportOverHTTP2(http2Client));
}

@Test
public void testPropertiesAreForwardedDynamic() throws Exception
{
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
testPropertiesAreForwarded(http2Client, new HttpClientTransportDynamic(clientConnector, new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client)));
}

private void testPropertiesAreForwarded(HTTP2Client http2Client, HttpClientTransport httpClientTransport) throws Exception
{
try (HttpClient httpClient = new HttpClient(httpClientTransport))
{
Executor executor = new QueuedThreadPool();
httpClient.setExecutor(executor);
Expand All @@ -127,6 +144,7 @@ public void testPropertiesAreForwarded() throws Exception
assertEquals(httpClient.getIdleTimeout(), http2Client.getIdleTimeout());
assertEquals(httpClient.isUseInputDirectByteBuffers(), http2Client.isUseInputDirectByteBuffers());
assertEquals(httpClient.isUseOutputDirectByteBuffers(), http2Client.isUseOutputDirectByteBuffers());
assertEquals(httpClient.getMaxResponseHeadersSize(), http2Client.getMaxResponseHeadersSize());
}
assertTrue(http2Client.isStopped());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.List;
import java.util.Map;

import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.transport.HttpClientConnectionFactory;
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
import org.eclipse.jetty.http3.client.HTTP3Client;
Expand All @@ -29,15 +30,23 @@
import org.eclipse.jetty.quic.common.QuicSession;
import org.eclipse.jetty.util.component.ContainerLifeCycle;

public class ClientConnectionFactoryOverHTTP3 extends ContainerLifeCycle implements ClientConnectionFactory
public class ClientConnectionFactoryOverHTTP3 extends ContainerLifeCycle implements ClientConnectionFactory, HttpClient.Aware
{
private final HTTP3ClientConnectionFactory factory = new HTTP3ClientConnectionFactory();
private final HTTP3Client http3Client;

public ClientConnectionFactoryOverHTTP3(HTTP3Client http3Client)
{
this.http3Client = http3Client;
installBean(http3Client);
}

@Override
public void setHttpClient(HttpClient httpClient)
{
HttpClientTransportOverHTTP3.configure(httpClient, http3Client);
}

@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context)
{
Expand All @@ -49,7 +58,7 @@ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<Stri
*
* @see HttpClientConnectionFactory#HTTP11
*/
public static class HTTP3 extends Info implements ProtocolSession.Factory
public static class HTTP3 extends Info implements ProtocolSession.Factory, HttpClient.Aware
{
private static final List<String> protocols = List.of("h3");

Expand All @@ -61,6 +70,13 @@ public HTTP3(HTTP3Client client)
http3Client = client;
}

@Override
public void setHttpClient(HttpClient httpClient)
{
ClientConnectionFactoryOverHTTP3 factory = (ClientConnectionFactoryOverHTTP3)getClientConnectionFactory();
factory.setHttpClient(httpClient);
}

public HTTP3Client getHTTP3Client()
{
return http3Client;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,24 +66,27 @@ public HTTP3Client getHTTP3Client()
protected void doStart() throws Exception
{
if (!http3Client.isStarted())
{
HttpClient httpClient = getHttpClient();
ClientConnector clientConnector = this.http3Client.getClientConnector();
clientConnector.setExecutor(httpClient.getExecutor());
clientConnector.setScheduler(httpClient.getScheduler());
clientConnector.setByteBufferPool(httpClient.getByteBufferPool());
clientConnector.setConnectTimeout(Duration.ofMillis(httpClient.getConnectTimeout()));
clientConnector.setConnectBlocking(httpClient.isConnectBlocking());
clientConnector.setBindAddress(httpClient.getBindAddress());
clientConnector.setIdleTimeout(Duration.ofMillis(httpClient.getIdleTimeout()));
HTTP3Configuration configuration = http3Client.getHTTP3Configuration();
configuration.setInputBufferSize(httpClient.getResponseBufferSize());
configuration.setUseInputDirectByteBuffers(httpClient.isUseInputDirectByteBuffers());
configuration.setUseOutputDirectByteBuffers(httpClient.isUseOutputDirectByteBuffers());
}
configure(getHttpClient(), http3Client);
super.doStart();
}

static void configure(HttpClient httpClient, HTTP3Client http3Client)
{
ClientConnector clientConnector = http3Client.getClientConnector();
clientConnector.setExecutor(httpClient.getExecutor());
clientConnector.setScheduler(httpClient.getScheduler());
clientConnector.setByteBufferPool(httpClient.getByteBufferPool());
clientConnector.setConnectTimeout(Duration.ofMillis(httpClient.getConnectTimeout()));
clientConnector.setConnectBlocking(httpClient.isConnectBlocking());
clientConnector.setBindAddress(httpClient.getBindAddress());
clientConnector.setIdleTimeout(Duration.ofMillis(httpClient.getIdleTimeout()));
HTTP3Configuration configuration = http3Client.getHTTP3Configuration();
configuration.setInputBufferSize(httpClient.getResponseBufferSize());
configuration.setUseInputDirectByteBuffers(httpClient.isUseInputDirectByteBuffers());
configuration.setUseOutputDirectByteBuffers(httpClient.isUseOutputDirectByteBuffers());
configuration.setMaxResponseHeadersSize(httpClient.getMaxResponseHeadersSize());
}

@Override
public Origin newOrigin(Request request)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,87 @@
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.Response;
import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http3.HTTP3Configuration;
import org.eclipse.jetty.http3.client.HTTP3Client;
import org.eclipse.jetty.http3.client.transport.ClientConnectionFactoryOverHTTP3;
import org.eclipse.jetty.http3.client.transport.HttpClientTransportOverHTTP3;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.quic.client.ClientQuicConfiguration;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class HttpClientTransportOverHTTP3Test extends AbstractClientServerTest
{
@Test
public void testPropertiesAreForwardedOverHTTP3() throws Exception
{
ClientConnector clientConnector = new ClientConnector();
HTTP3Client http3Client = new HTTP3Client(new ClientQuicConfiguration(new SslContextFactory.Client(), null), clientConnector);
testPropertiesAreForwarded(http3Client, new HttpClientTransportOverHTTP3(http3Client));
}

@Test
public void testPropertiesAreForwardedDynamic() throws Exception
{
ClientConnector clientConnector = new ClientConnector();
HTTP3Client http3Client = new HTTP3Client(new ClientQuicConfiguration(new SslContextFactory.Client(), null), clientConnector);
testPropertiesAreForwarded(http3Client, new HttpClientTransportDynamic(clientConnector, new ClientConnectionFactoryOverHTTP3.HTTP3(http3Client)));
}

private void testPropertiesAreForwarded(HTTP3Client http3Client, HttpClientTransport httpClientTransport) throws Exception
{
try (HttpClient httpClient = new HttpClient(httpClientTransport))
{
Executor executor = new QueuedThreadPool();
httpClient.setExecutor(executor);
httpClient.setConnectTimeout(13);
httpClient.setIdleTimeout(17);
httpClient.setUseInputDirectByteBuffers(false);
httpClient.setUseOutputDirectByteBuffers(false);

httpClient.start();

assertTrue(http3Client.isStarted());
ClientConnector clientConnector = http3Client.getClientConnector();
assertSame(httpClient.getExecutor(), clientConnector.getExecutor());
assertSame(httpClient.getScheduler(), clientConnector.getScheduler());
assertSame(httpClient.getByteBufferPool(), clientConnector.getByteBufferPool());
assertEquals(httpClient.getConnectTimeout(), clientConnector.getConnectTimeout().toMillis());
assertEquals(httpClient.getIdleTimeout(), clientConnector.getIdleTimeout().toMillis());
HTTP3Configuration http3Configuration = http3Client.getHTTP3Configuration();
assertEquals(httpClient.isUseInputDirectByteBuffers(), http3Configuration.isUseInputDirectByteBuffers());
assertEquals(httpClient.isUseOutputDirectByteBuffers(), http3Configuration.isUseOutputDirectByteBuffers());
assertEquals(httpClient.getMaxResponseHeadersSize(), http3Configuration.getMaxResponseHeadersSize());
}
assertTrue(http3Client.isStopped());
}

@Test
public void testRequestHasHTTP3Version() throws Exception
{
Expand Down

0 comments on commit 3f903e2

Please sign in to comment.