Improvements to the Jetty client documentation, protocols section.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2020-04-03 15:48:31 +02:00
parent eaf9d43a0b
commit 51c42f2849
17 changed files with 445 additions and 155 deletions

View File

@ -44,9 +44,6 @@ public class ALPNClientConnection extends NegotiatingClientConnection
public void selected(String protocol)
{
if (protocol == null || !protocols.contains(protocol))
close();
else
completed(protocol);
completed(protocol);
}
}

View File

@ -110,12 +110,4 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
}
throw new IllegalStateException("No ALPNProcessor for " + engine);
}
public static class ALPN extends Info
{
public ALPN(Executor executor, ClientConnectionFactory factory, List<String> protocols)
{
super(List.of("alpn"), new ALPNClientConnectionFactory(executor, factory, protocols));
}
}
}

View File

@ -75,8 +75,11 @@ public class JDK9ClientALPNProcessor implements ALPNProcessor.Client
{
String protocol = alpnConnection.getSSLEngine().getApplicationProtocol();
if (LOG.isDebugEnabled())
LOG.debug("selected protocol {}", protocol);
alpnConnection.selected(protocol);
LOG.debug("selected protocol '{}'", protocol);
if (protocol != null && !protocol.isEmpty())
alpnConnection.selected(protocol);
else
alpnConnection.selected(null);
}
}
}

View File

@ -19,12 +19,14 @@
package org.eclipse.jetty.client.dynamic;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jetty.alpn.client.ALPNClientConnection;
import org.eclipse.jetty.alpn.client.ALPNClientConnectionFactory;
@ -37,6 +39,7 @@ import org.eclipse.jetty.client.MultiplexConnectionPool;
import org.eclipse.jetty.client.MultiplexHttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
@ -105,7 +108,7 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
factoryInfos = new Info[]{HttpClientConnectionFactory.HTTP11};
this.factoryInfos = Arrays.asList(factoryInfos);
this.protocols = Arrays.stream(factoryInfos)
.flatMap(info -> info.getProtocols().stream())
.flatMap(info -> Stream.concat(info.getProtocols(false).stream(), info.getProtocols(true).stream()))
.distinct()
.map(p -> p.toLowerCase(Locale.ENGLISH))
.collect(Collectors.toList());
@ -117,9 +120,9 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
@Override
public Origin newOrigin(HttpRequest request)
{
boolean ssl = HttpClient.isSchemeSecure(request.getScheme());
boolean secure = HttpClient.isSchemeSecure(request.getScheme());
String http1 = "http/1.1";
String http2 = ssl ? "h2" : "h2c";
String http2 = secure ? "h2" : "h2c";
List<String> protocols = List.of();
if (request.isVersionExplicit())
{
@ -130,16 +133,23 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
}
else
{
if (ssl)
if (secure)
{
// There may be protocol negotiation, so preserve the order
// of protocols chosen by the application.
// We need to keep multiple protocols in case the protocol
// is negotiated: e.g. [http/1.1, h2] negotiates [h2], but
// here we don't know yet what will be negotiated.
List<String> http = List.of("http/1.1", "h2c", "h2");
protocols = this.protocols.stream()
.filter(p -> p.equals(http1) || p.equals(http2))
.collect(Collectors.toList());
.filter(http::contains)
.collect(Collectors.toCollection(ArrayList::new));
// The http/1.1 upgrade to http/2 over TLS implicitly
// "negotiates" [h2c], so we need to remove [h2]
// because we don't want to negotiate using ALPN.
if (request.getHeaders().contains(HttpHeader.UPGRADE, "h2c"))
protocols.remove("h2");
}
else
{
@ -149,7 +159,7 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
}
Origin.Protocol protocol = null;
if (!protocols.isEmpty())
protocol = new Origin.Protocol(protocols, ssl && protocols.contains(http2));
protocol = new Origin.Protocol(protocols, secure && protocols.contains(http2));
return getHttpClient().createOrigin(request, protocol);
}
@ -164,32 +174,33 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
{
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
Origin.Protocol protocol = destination.getOrigin().getProtocol();
ClientConnectionFactory.Info factoryInfo;
ClientConnectionFactory factory;
if (protocol == null)
{
// Use the default ClientConnectionFactory.
factoryInfo = factoryInfos.get(0);
factory = factoryInfos.get(0).getClientConnectionFactory();
}
else
{
if (destination.isSecure() && protocol.isNegotiate())
{
factoryInfo = new ALPNClientConnectionFactory.ALPN(getClientConnector().getExecutor(), this::newNegotiatedConnection, protocol.getProtocols());
factory = new ALPNClientConnectionFactory(getClientConnector().getExecutor(), this::newNegotiatedConnection, protocol.getProtocols());
}
else
{
factoryInfo = findClientConnectionFactoryInfo(protocol.getProtocols())
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for " + protocol));
factory = findClientConnectionFactoryInfo(protocol.getProtocols(), destination.isSecure())
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for " + protocol))
.getClientConnectionFactory();
}
}
return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
return factory.newConnection(endPoint, context);
}
public void upgrade(EndPoint endPoint, Map<String, Object> context)
{
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
Origin.Protocol protocol = destination.getOrigin().getProtocol();
Info info = findClientConnectionFactoryInfo(protocol.getProtocols())
Info info = findClientConnectionFactoryInfo(protocol.getProtocols(), destination.isSecure())
.orElseThrow(() -> new IllegalStateException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " to upgrade to " + protocol));
info.upgrade(endPoint, context);
}
@ -200,13 +211,22 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
{
ALPNClientConnection alpnConnection = (ALPNClientConnection)endPoint.getConnection();
String protocol = alpnConnection.getProtocol();
if (LOG.isDebugEnabled())
LOG.debug("ALPN negotiated {} among {}", protocol, alpnConnection.getProtocols());
if (protocol == null)
throw new IOException("Could not negotiate protocol among " + alpnConnection.getProtocols());
List<String> protocols = List.of(protocol);
Info factoryInfo = findClientConnectionFactoryInfo(protocols)
Info factoryInfo;
if (protocol != null)
{
if (LOG.isDebugEnabled())
LOG.debug("ALPN negotiated {} among {}", protocol, alpnConnection.getProtocols());
List<String> protocols = List.of(protocol);
factoryInfo = findClientConnectionFactoryInfo(protocols, true)
.orElseThrow(() -> new IOException("Cannot find " + ClientConnectionFactory.class.getSimpleName() + " for negotiated protocol " + protocol));
}
else
{
// Server does not support ALPN, let's try the first protocol.
factoryInfo = factoryInfos.get(0);
if (LOG.isDebugEnabled())
LOG.debug("No ALPN protocol, using {}", factoryInfo);
}
return factoryInfo.getClientConnectionFactory().newConnection(endPoint, context);
}
catch (Throwable failure)
@ -216,10 +236,10 @@ public class HttpClientTransportDynamic extends AbstractConnectorHttpClientTrans
}
}
private Optional<Info> findClientConnectionFactoryInfo(List<String> protocols)
private Optional<Info> findClientConnectionFactoryInfo(List<String> protocols, boolean secure)
{
return factoryInfos.stream()
.filter(info -> info.matches(protocols))
.filter(info -> info.matches(protocols, secure))
.findFirst();
}
}

View File

@ -21,12 +21,16 @@ package org.eclipse.jetty.client.http;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
public class HttpClientConnectionFactory implements ClientConnectionFactory
{
public static final Info HTTP11 = new Info(List.of("http/1.1"), new HttpClientConnectionFactory());
/**
* <p>Representation of the {@code HTTP/1.1} application protocol used by {@link HttpClientTransportDynamic}.</p>
*/
public static final Info HTTP11 = new HTTP11(new HttpClientConnectionFactory());
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context)
@ -34,4 +38,26 @@ public class HttpClientConnectionFactory implements ClientConnectionFactory
HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, context);
return customize(connection, context);
}
private static class HTTP11 extends Info
{
private static final List<String> protocols = List.of("http/1.1");
private HTTP11(ClientConnectionFactory factory)
{
super(factory);
}
@Override
public List<String> getProtocols(boolean secure)
{
return protocols;
}
@Override
public String toString()
{
return String.format("%s@%x%s", getClass().getSimpleName(), hashCode(), protocols);
}
}
}

View File

@ -59,8 +59,13 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-io</artifactId>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>http2-http-client-transport</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.fcgi</groupId>
<artifactId>fcgi-client</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

View File

@ -50,5 +50,5 @@ The Jetty HTTP/2 implementation consists of the following sub-projects (each pro
2. `http2-hpack`: Contains the HTTP/2 HPACK implementation for HTTP header compression.
3. `http2-server`: Provides the server-side implementation of HTTP/2.
4. `http2-client`: Provides the implementation of HTTP/2 client with a low level HTTP/2 API, dealing with HTTP/2 streams, frames, etc.
5. `http2-http-client-transport`: Provides the implementation of the HTTP/2 transport for `HttpClient` (see xref:http-client[]).
5. `http2-http-client-transport`: Provides the implementation of the HTTP/2 transport for `HttpClient` (see xref:client-http[this section]).
Applications can use the higher level API provided by `HttpClient` to send HTTP requests and receive HTTP responses, and the HTTP/2 transport will take care of converting them in HTTP/2 format (see also https://webtide.com/http2-support-for-httpclient/[this blog entry]).

View File

@ -34,7 +34,7 @@ include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=simpleBl
The method `HttpClient.GET(...)` performs a HTTP `GET` request to the given URI and returns a `ContentResponse` when the request/response conversation completes successfully.
The `ContentResponse` object contains the HTTP response information: status code, headers and possibly content.
The content length is limited by default to 2 MiB; for larger content see xref:http-client-response-content[].
The content length is limited by default to 2 MiB; for larger content see xref:client-http-content-response[].
If you want to customize the request, for example by issuing a `HEAD` request instead of a `GET`, and simulating a browser user agent, you can do it in this way:
@ -190,7 +190,7 @@ which allows applications to write request content when it is available to the `
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tags=outputStreamRequestContent]
----
[[http-client-response-content]]
[[client-http-content-response]]
===== Response Content Handling
Jetty's `HttpClient` allows applications to handle response content in different ways.

View File

@ -16,21 +16,25 @@
// ========================================================================
//
[[http-client-transport]]
=== Pluggable Transports
[[client-http-transport]]
=== HttpClient Pluggable Transports
Jetty's HTTP client can be configured to use different transports to carry the semantic of HTTP requests and responses.
Jetty's `HttpClient` can be configured to use different transports to carry the
semantic of HTTP requests and responses.
This means that the intention of a client to request resource `/index.html` using the `GET` method can be carried over the network in different formats.
This means that the intention of a client to request resource `/index.html`
using the `GET` method can be carried over the network in different formats.
A HTTP client transport is the component that is in charge of converting a high-level, semantic, HTTP requests such as "GET resource /index.html" into the specific format understood by the server (for example, HTTP/2), and to convert the server response from the specific format (HTTP/2) into high-level, semantic objects that can be used by applications.
A `HttpClient` transport is the component that is in charge of converting a
high-level, semantic, HTTP requests such as "`GET` resource ``/index.html``"
into the specific format understood by the server (for example, HTTP/2), and to
convert the server response from the specific format (HTTP/2) into high-level,
semantic objects that can be used by applications.
In this way, applications are not aware of the actual protocol being used.
This allows them to write their logic against a high-level API that hides the details of the specific protocol being used over the network.
The most common protocol format is HTTP/1.1, a textual protocol with lines
separated by `\r\n`:
The most common protocol format is HTTP/1.1, a text-based protocol with lines separated by `\r\n`:
[source, screen, subs="{sub-order}"]
[source,screen,subs="{sub-order}"]
----
GET /index.html HTTP/1.1\r\n
Host: domain.com\r\n
@ -40,7 +44,7 @@ Host: domain.com\r\n
However, the same request can be made using FastCGI, a binary protocol:
[source, screen, subs="{sub-order}"]
[source,screen,subs="{sub-order}"]
----
x01 x01 x00 x01 x00 x08 x00 x00
x00 x01 x01 x00 x00 x00 x00 x00
@ -52,64 +56,153 @@ x0C x0B D O C U M E
...
----
Similarly, HTTP/2 is a binary protocol that transports the same information in a yet different format.
Similarly, HTTP/2 is a binary protocol that transports the same information
in a yet different format.
A protocol may be _negotiated_ between client and server. A request for a
resource may be sent using one protocol (for example, HTTP/1.1), but the
response may arrive in a different protocol (for example, HTTP/2).
`HttpClient` supports 3 static transports, each speaking only one protocol:
link:#client-http-transport-http11[HTTP/1.1],
link:#client-http-transport-http2[HTTP/2] and
link:#client-http-transport-fcgi[FastCGI],
all of them with 2 variants: clear-text and TLS encrypted.
`HttpClient` also supports one
link:#client-http-transport-dynamic[dynamic transport],
that can speak different protocols and can select the right protocol by
negotiating it with the server or by explicit indication from applications.
Applications are typically not aware of the actual protocol being used.
This allows them to write their logic against a high-level API that hides the
details of the specific protocol being used over the network.
[[client-http-transport-http11]]
==== HTTP/1.1 Transport
HTTP/1.1 is the default transport.
[source, java, subs="{sub-order}"]
[source,java,indent=0]
----
// No transport specified, using default.
HttpClient client = new HttpClient();
client.start();
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=defaultTransport]
----
If you want to customize the HTTP/1.1 transport, you can explicitly configure `HttpClient` in this way:
If you want to customize the HTTP/1.1 transport, you can explicitly configure
it in this way:
[source, java, subs="{sub-order}"]
[source,java,indent=0]
----
int selectors = 1;
HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(selectors);
HttpClient client = new HttpClient(transport, null);
client.start();
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=http11Transport]
----
The example above allows you to customize the number of NIO selectors that `HttpClient` will be using.
[[client-http-transport-http2]]
==== HTTP/2 Transport
The HTTP/2 transport can be configured in this way:
[source, java, subs="{sub-order}"]
[source,java,indent=0]
----
HTTP2Client h2Client = new HTTP2Client();
h2Client.setSelectors(1);
HttpClientTransportOverHTTP2 transport = new HttpClientTransportOverHTTP2(h2Client);
HttpClient client = new HttpClient(transport, null);
client.start();
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=http2Transport]
----
`HTTP2Client` is the lower-level client that provides an API based on HTTP/2 concepts such as _sessions_, _streams_ and _frames_ that are specific to HTTP/2.
`HTTP2Client` is the lower-level client that provides an API based on HTTP/2
concepts such as _sessions_, _streams_ and _frames_ that are specific to HTTP/2.
See link:#client-http2[the HTTP/2 client section] for more information.
`HttpClientTransportOverHTTP2` uses `HTTP2Client` to format high-level semantic HTTP requests ("GET resource /index.html") into the HTTP/2 specific format.
`HttpClientTransportOverHTTP2` uses `HTTP2Client` to format high-level semantic
HTTP requests (like "GET resource /index.html") into the HTTP/2 specific format.
[[client-http-transport-fcgi]]
==== FastCGI Transport
The FastCGI transport can be configured in this way:
[source, java, subs="{sub-order}"]
[source,java,indent=0]
----
int selectors = 1;
String scriptRoot = "/var/www/wordpress";
HttpClientTransportOverFCGI transport = new HttpClientTransportOverFCGI(selectors, false, scriptRoot);
HttpClient client = new HttpClient(transport, null);
client.start();
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=fcgiTransport]
----
In order to make requests using the FastCGI transport, you need to have a FastCGI server such as https://en.wikipedia.org/wiki/PHP#PHPFPM[PHP-FPM] (see also http://php.net/manual/en/install.fpm.php).
In order to make requests using the FastCGI transport, you need to have a
FastCGI server such as https://en.wikipedia.org/wiki/PHP#PHPFPM[PHP-FPM]
(see also http://php.net/manual/en/install.fpm.php).
The FastCGI transport is primarily used by Jetty's link:#fastcgi[FastCGI support] to serve PHP pages (WordPress for example).
The FastCGI transport is primarily used by Jetty's link:#fastcgi[FastCGI support]
to serve PHP pages (WordPress for example).
[[client-http-transport-dynamic]]
==== Dynamic Transport
The static transports work well if you know in advance the protocol you want
to speak with the server, or if the server only supports one protocol (such
as FastCGI).
With the advent of HTTP/2, however, servers are now able to support multiple
protocols, at least both HTTP/1.1 and HTTP/2.
The HTTP/2 protocol is typically negotiated between client and server.
This negotiation can happen via ALPN, a TLS extension that allows the client
to tell the server the list of protocol that the client supports, so that the
server can pick one of the client supported protocols that also the server
supports; or via HTTP/1.1 upgrade by means of the `Upgrade` header.
Applications can configure the dynamic transport with one or more
_application_ protocols such as HTTP/1.1 or HTTP/2. The implementation will
take care of using TLS for HTTPS URIs, using ALPN, negotiating protocols,
upgrading from one protocol to another, etc.
By default, the dynamic transport only speaks HTTP/1.1:
[source,java,indent=0]
----
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=dynamicDefault]
----
The dynamic transport can be configured with just one protocol, making it
equivalent to the corresponding static transport:
[source,java,indent=0]
----
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=dynamicOneProtocol]
----
The dynamic transport, however, has been implemented to support multiple
transports, in particular both HTTP/1.1 and HTTP/2:
[source,java,indent=0]
----
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=dynamicH1H2]
----
IMPORTANT: The order in which the protocols are specified to
`HttpClientTransportDynamic` indicates what is the client preference.
If the protocol is negotiated via ALPN, it is the server that decides what is
the protocol to use for the communication, regardless of the client preference.
If the protocol is not negotiated, the client preference is honored.
Provided that the server supports both HTTP/1.1 and HTTP/2 clear-text, client
applications can explicitly hint the version they want to use:
[source,java,indent=0]
----
include::../../{doc_code}/embedded/client/http/HTTPClientDocs.java[tag=dynamicClearText]
----
In case of TLS encrypted communication using the HTTPS scheme, things are a
little more complicated.
If the client application explicitly specifies the HTTP version, then ALPN
is not used on the client. By specifying the HTTP version explicitly, the
client application has prior-knowledge of what HTTP version the server
supports, and therefore ALPN is not needed.
If the server does not support the HTTP version chosen by the client, then
the communication will fail.
If the client application does not explicitly specify the HTTP version,
then ALPN will be used on the client.
If the server also supports ALPN, then the protocol will be negotiated via
ALPN and the server will choose the protocol to use.
If the server does not support ALPN, the client will try to use the first
protocol configured in `HttpClientTransportDynamic`, and the communication
may succeed or fail depending on whether the server supports the protocol
chosen by the client.

View File

@ -22,7 +22,7 @@
This example shows the bare minimum required for deploying a servlet into Jetty.
Note that this is strictly a servlet, not a servlet in the context of a web application, that example comes later.
This is purely just a servlet deployed and mounted on a context and able to process requests.
This example is excellent for situations where you have a simple servlet that you need to unit test, just mount it on a context and issue requests using your favorite http client library (like our Jetty client found in xref:http-client[]).
This example is excellent for situations where you have a simple servlet that you need to unit test, just mount it on a context and issue requests using your favorite http client library (like our Jetty client found in xref:client-http[]).
[source, java, subs="{sub-order}"]
----

View File

@ -21,7 +21,7 @@
This example shows how to deploy a simple webapp with an embedded instance of Jetty.
This is useful when you want to manage the lifecycle of a server programmatically, either within a production application or as a simple way to deploying and debugging a full scale application deployment.
In many ways it is easier then traditional deployment since you control the classpath yourself, making this easy to wire up in a test case in Maven and issue requests using your favorite http client library (like our Jetty client found in xref:http-client[]).
In many ways it is easier then traditional deployment since you control the classpath yourself, making this easy to wire up in a test case in Maven and issue requests using your favorite http client library (like our Jetty client found in xref:client-http[]).
[source, java, subs="{sub-order}"]
----

View File

@ -41,6 +41,8 @@ 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.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.util.AsyncRequestContent;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.client.util.BufferingResponseListener;
@ -52,9 +54,16 @@ import org.eclipse.jetty.client.util.InputStreamResponseListener;
import org.eclipse.jetty.client.util.OutputStreamRequestContent;
import org.eclipse.jetty.client.util.PathRequestContent;
import org.eclipse.jetty.client.util.StringRequestContent;
import org.eclipse.jetty.fcgi.client.http.HttpClientTransportOverFCGI;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.ClientConnectionFactoryOverHTTP2;
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.HttpCookieStore;
@ -666,4 +675,129 @@ public class HTTPClientDocs
ContentResponse response = httpClient.newRequest(serverURI).send();
// end::proxyAuthentication[]
}
public void defaultTransport() throws Exception
{
// tag::defaultTransport[]
// No transport specified, using default.
HttpClient httpClient = new HttpClient();
httpClient.start();
// end::defaultTransport[]
}
public void http11Transport() throws Exception
{
// tag::http11Transport[]
// Configure HTTP/1.1 transport.
HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP();
transport.setHeaderCacheSize(16384);
HttpClient client = new HttpClient(transport);
client.start();
// end::http11Transport[]
}
public void http2Transport() throws Exception
{
// tag::http2Transport[]
// The HTTP2Client powers the HTTP/2 transport.
HTTP2Client h2Client = new HTTP2Client();
h2Client.setInitialSessionRecvWindow(64 * 1024 * 1024);
// Create and configure the HTTP/2 transport.
HttpClientTransportOverHTTP2 transport = new HttpClientTransportOverHTTP2(h2Client);
transport.setUseALPN(true);
HttpClient client = new HttpClient(transport);
client.start();
// end::http2Transport[]
}
public void fcgiTransport() throws Exception
{
// tag::fcgiTransport[]
String scriptRoot = "/var/www/wordpress";
HttpClientTransportOverFCGI transport = new HttpClientTransportOverFCGI(scriptRoot);
HttpClient client = new HttpClient(transport);
client.start();
// end::fcgiTransport[]
}
public void dynamicDefault() throws Exception
{
// tag::dynamicDefault[]
// Dynamic transport speaks HTTP/1.1 by default.
HttpClientTransportDynamic transport = new HttpClientTransportDynamic();
HttpClient client = new HttpClient(transport);
client.start();
// end::dynamicDefault[]
}
public void dynamicOneProtocol() throws Exception
{
// tag::dynamicOneProtocol[]
ClientConnector connector = new ClientConnector();
// Equivalent to HttpClientTransportOverHTTP.
HttpClientTransportDynamic http11Transport = new HttpClientTransportDynamic(connector, HttpClientConnectionFactory.HTTP11);
// Equivalent to HttpClientTransportOverHTTP2.
HTTP2Client http2Client = new HTTP2Client(connector);
HttpClientTransportDynamic http2Transport = new HttpClientTransportDynamic(connector, new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client));
// end::dynamicOneProtocol[]
}
public void dynamicH1H2() throws Exception
{
// tag::dynamicH1H2[]
ClientConnector connector = new ClientConnector();
ClientConnectionFactory.Info http1 = HttpClientConnectionFactory.HTTP11;
HTTP2Client http2Client = new HTTP2Client(connector);
ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
HttpClientTransportDynamic transport = new HttpClientTransportDynamic(connector, http1, http2);
HttpClient client = new HttpClient(transport);
client.start();
// end::dynamicH1H2[]
}
public void dynamicClearText() throws Exception
{
// tag::dynamicClearText[]
ClientConnector connector = new ClientConnector();
ClientConnectionFactory.Info http1 = HttpClientConnectionFactory.HTTP11;
HTTP2Client http2Client = new HTTP2Client(connector);
ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
HttpClientTransportDynamic transport = new HttpClientTransportDynamic(connector, http1, http2);
HttpClient client = new HttpClient(transport);
client.start();
// The server supports both HTTP/1.1 and HTTP/2 clear-text on port 8080.
// Make a clear-text request without explicit version.
// The first protocol specified to HttpClientTransportDynamic
// is picked, in this example will be HTTP/1.1.
ContentResponse http1Response = client.newRequest("host", 8080).send();
// Make a clear-text request with explicit version.
// Clear-text HTTP/2 is used for this request.
ContentResponse http2Response = client.newRequest("host", 8080)
// Specify the version explicitly.
.version(HttpVersion.HTTP_2)
.send();
// Make a clear-text upgrade request from HTTP/1.1 to HTTP/2.
// The request will start as HTTP/1.1, but the response will be HTTP/2.
ContentResponse upgradedResponse = client.newRequest("host", 8080)
.header(HttpHeader.UPGRADE, "h2c")
.header(HttpHeader.HTTP2_SETTINGS, "")
.header(HttpHeader.CONNECTION, "Upgrade, HTTP2-Settings")
.send();
// end::dynamicClearText[]
}
}

View File

@ -26,6 +26,8 @@ import java.util.Map;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.HTTP2ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnectionFactory;
@ -56,19 +58,25 @@ public class ClientConnectionFactoryOverHTTP2 extends ContainerLifeCycle impleme
return factory.newConnection(endPoint, context);
}
public static class H2 extends Info
/**
* <p>Representation of the {@code HTTP/2} application protocol used by {@link HttpClientTransportDynamic}.</p>
*
* @see HttpClientConnectionFactory#HTTP11
*/
public static class HTTP2 extends Info
{
public H2(HTTP2Client client)
{
super(List.of("h2"), new ClientConnectionFactoryOverHTTP2(client));
}
}
private static final List<String> protocols = List.of("h2", "h2c");
private static final List<String> h2c = List.of("h2c");
public static class H2C extends Info
{
public H2C(HTTP2Client client)
public HTTP2(HTTP2Client client)
{
super(List.of("h2c"), new ClientConnectionFactoryOverHTTP2(client));
super(new ClientConnectionFactoryOverHTTP2(client));
}
@Override
public List<String> getProtocols(boolean secure)
{
return secure ? protocols : h2c;
}
@Override
@ -119,5 +127,11 @@ public class ClientConnectionFactoryOverHTTP2 extends ContainerLifeCycle impleme
throw new UncheckedIOException(x);
}
}
@Override
public String toString()
{
return String.format("%s@%x%s", getClass().getSimpleName(), hashCode(), protocols);
}
}
}

View File

@ -66,26 +66,21 @@ public interface ClientConnectionFactory
}
/**
* <p>A holder for a list of protocol strings identifying a network protocol
* <p>A holder for a list of protocol strings identifying an application protocol
* (for example {@code ["h2", "h2-17", "h2-16"]}) and a {@link ClientConnectionFactory}
* that creates connections that speak that network protocol.</p>
*/
public static class Info extends ContainerLifeCycle
public abstract static class Info extends ContainerLifeCycle
{
private final List<String> protocols;
private final ClientConnectionFactory factory;
public Info(List<String> protocols, ClientConnectionFactory factory)
public Info(ClientConnectionFactory factory)
{
this.protocols = protocols;
this.factory = factory;
addBean(factory);
}
public List<String> getProtocols()
{
return protocols;
}
public abstract List<String> getProtocols(boolean secure);
public ClientConnectionFactory getClientConnectionFactory()
{
@ -98,20 +93,14 @@ public interface ClientConnectionFactory
* @param candidates the candidates to match against
* @return whether one of the protocols of this class is present in the candidates
*/
public boolean matches(List<String> candidates)
public boolean matches(List<String> candidates, boolean secure)
{
return protocols.stream().anyMatch(p -> candidates.stream().anyMatch(c -> c.equalsIgnoreCase(p)));
return getProtocols(secure).stream().anyMatch(p -> candidates.stream().anyMatch(c -> c.equalsIgnoreCase(p)));
}
public void upgrade(EndPoint endPoint, Map<String, Object> context)
{
throw new UnsupportedOperationException(this + " does not support upgrade to another protocol");
}
@Override
public String toString()
{
return String.format("%s@%x%s", getClass().getSimpleName(), hashCode(), protocols);
}
}
}

View File

@ -152,7 +152,7 @@ public class WebSocketOverHTTP2Test
@Test
public void testWebSocketOverDynamicHTTP2() throws Exception
{
testWebSocketOverDynamicTransport(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2C(new HTTP2Client(clientConnector)));
testWebSocketOverDynamicTransport(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
}
private void testWebSocketOverDynamicTransport(Function<ClientConnector, ClientConnectionFactory.Info> protocolFn) throws Exception
@ -184,7 +184,7 @@ public class WebSocketOverHTTP2Test
AbstractHTTP2ServerConnectionFactory h2c = connector.getBean(AbstractHTTP2ServerConnectionFactory.class);
h2c.setConnectProtocolEnabled(false);
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2C(new HTTP2Client(clientConnector)));
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/ws/echo");
@ -220,7 +220,7 @@ public class WebSocketOverHTTP2Test
}
});
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
// Connect and send immediately a message, so the message
// arrives to the server while the server is still upgrading.
@ -242,7 +242,7 @@ public class WebSocketOverHTTP2Test
public void testWebSocketConnectPortDoesNotExist() throws Exception
{
startServer();
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + (connector.getLocalPort() + 1) + "/ws/echo");
@ -259,7 +259,7 @@ public class WebSocketOverHTTP2Test
public void testWebSocketNotFound() throws Exception
{
startServer();
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/nothing");
@ -276,7 +276,7 @@ public class WebSocketOverHTTP2Test
public void testNotNegotiated() throws Exception
{
startServer();
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/ws/null");
@ -293,7 +293,7 @@ public class WebSocketOverHTTP2Test
public void testThrowFromCreator() throws Exception
{
startServer();
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
CountDownLatch latch = new CountDownLatch(1);
connector.addBean(new HttpChannel.Listener()
@ -327,7 +327,7 @@ public class WebSocketOverHTTP2Test
public void testServerConnectionClose() throws Exception
{
startServer();
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.H2(new HTTP2Client(clientConnector)));
startClient(clientConnector -> new ClientConnectionFactoryOverHTTP2.HTTP2(new HTTP2Client(clientConnector)));
EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/ws/connectionClose");

View File

@ -42,7 +42,7 @@ import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.client.util.BytesRequestContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
@ -244,7 +244,7 @@ public class HttpClientTransportDynamicTest
startServer(this::h1H2C, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, h2c);
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
// .version(HttpVersion.HTTP_2)
@ -273,14 +273,15 @@ public class HttpClientTransportDynamicTest
clientConnector.setSslContextFactory(newClientSslContextFactory());
HttpClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
HttpClientTransportDynamic transport = new HttpClientTransportDynamic(clientConnector, h1, h2c)
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
HttpClientTransportDynamic transport = new HttpClientTransportDynamic(clientConnector, h1, http2)
{
@Override
public Origin newOrigin(HttpRequest request)
{
// Use prior-knowledge, i.e. negotiate==false.
List<String> protocols = HttpVersion.HTTP_2 == request.getVersion() ? h2c.getProtocols() : h1.getProtocols();
boolean secure = HttpClient.isSchemeSecure(request.getScheme());
List<String> protocols = HttpVersion.HTTP_2 == request.getVersion() ? http2.getProtocols(secure) : h1.getProtocols(secure);
return new Origin(request.getScheme(), request.getHost(), request.getPort(), request.getTag(), new Origin.Protocol(protocols, false));
}
};
@ -320,8 +321,8 @@ public class HttpClientTransportDynamicTest
startServer(this::sslAlpnH1H2, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make a request, should be HTTP/1.1 because of the order of protocols on server.
ContentResponse h1cResponse = client.newRequest("localhost", connector.getLocalPort())
@ -355,8 +356,8 @@ public class HttpClientTransportDynamicTest
startServer(this::sslAlpnH1, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
startClient(clientConnector, h2, HttpClientConnectionFactory.HTTP11);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, http2, HttpClientConnectionFactory.HTTP11);
// The client prefers h2 over h1, and use of TLS and ALPN will allow the fallback to h1.
ContentResponse h1cResponse = client.newRequest("localhost", connector.getLocalPort())
@ -372,8 +373,8 @@ public class HttpClientTransportDynamicTest
startServer(this::h1, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// The client forces HTTP/2, but the server cannot speak it, so the request fails.
// There is no fallback to HTTP/1 because the protocol version is set explicitly.
@ -450,9 +451,8 @@ public class HttpClientTransportDynamicTest
server.start();
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
startClient(clientConnector, h2, HttpClientConnectionFactory.HTTP11, h2c);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make a clear-text request using HTTP/1.1.
ContentResponse h1cResponse = client.newRequest("localhost", clearConnector.getLocalPort())
@ -510,8 +510,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make an upgrade request from HTTP/1.1 to H2C.
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
@ -584,8 +584,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
int proxyPort = connector.getLocalPort();
// The proxy speaks both http/1.1 and h2c.
@ -622,8 +622,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make an upgrade request from HTTP/1.1 to H2C.
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
@ -653,8 +653,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make a POST upgrade request from HTTP/1.1 to H2C.
// We don't support upgrades with request content because
@ -668,7 +668,7 @@ public class HttpClientTransportDynamicTest
.header(HttpHeader.UPGRADE, "h2c")
.header(HttpHeader.HTTP2_SETTINGS, "")
.header(HttpHeader.CONNECTION, "Upgrade, HTTP2-Settings")
.content(new BytesContentProvider(bytes))
.body(new BytesRequestContent(bytes))
.timeout(5, TimeUnit.SECONDS)
.send(new BufferingResponseListener(bytes.length)
{
@ -693,8 +693,8 @@ public class HttpClientTransportDynamicTest
startServer(this::h1H2C, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// The upgrade request is missing the required HTTP2-Settings header.
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
@ -719,8 +719,8 @@ public class HttpClientTransportDynamicTest
});
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, h2c);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make an upgrade request from HTTP/1.1 to H2C.
CountDownLatch latch = new CountDownLatch(1);
@ -736,4 +736,23 @@ public class HttpClientTransportDynamicTest
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@Test
public void testClientWithALPNServerWithoutALPN() throws Exception
{
startServer(this::sslH1H2C, new EmptyServerHandler());
ClientConnector clientConnector = new ClientConnector();
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
startClient(clientConnector, HttpClientConnectionFactory.HTTP11, http2);
// Make a request without explicit version, so ALPN is used on the client.
// Since the server does not support ALPN, the first protocol is used.
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(HttpScheme.HTTPS.asString())
.timeout(5, TimeUnit.SECONDS)
.send();
assertEquals(HttpStatus.OK_200, response.getStatus());
}
}

View File

@ -180,9 +180,8 @@ public class ProxyWithDynamicTransportTest
{
ClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
return new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, h2c, h2));
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
return new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, http2));
}
});
context.addServlet(holder, "/*");
@ -200,9 +199,8 @@ public class ProxyWithDynamicTransportTest
clientConnector.setSslContextFactory(new SslContextFactory.Client(true));
http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactory.Info h1 = HttpClientConnectionFactory.HTTP11;
ClientConnectionFactory.Info h2c = new ClientConnectionFactoryOverHTTP2.H2C(http2Client);
ClientConnectionFactory.Info h2 = new ClientConnectionFactoryOverHTTP2.H2(http2Client);
client = new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, h2c, h2));
ClientConnectionFactory.Info http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
client = new HttpClient(new HttpClientTransportDynamic(clientConnector, h1, http2));
client.start();
}