diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index c9ce6f258ed..e6e71096bd6 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -562,7 +562,13 @@ public class HttpClient extends ContainerLifeCycle return new Origin(scheme, host, port, tag); } - protected HttpDestination resolveDestination(Origin origin) + /** + *

Returns, creating it if absent, the destination with the given origin.

+ * + * @param origin the origin that identifies the destination + * @return the destination for the given origin + */ + public HttpDestination resolveDestination(Origin origin) { return destinations.computeIfAbsent(origin, o -> { diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java index 6930ca19b6b..8ab4f21e1f7 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java @@ -120,7 +120,13 @@ public abstract class HttpConnection implements Connection, Attachable if (version.getVersion() <= 11) { if (!headers.containsKey(HttpHeader.HOST.asString())) - headers.put(getHttpDestination().getHostField()); + { + URI uri = request.getURI(); + if (uri != null) + headers.put(HttpHeader.HOST, uri.getAuthority()); + else + headers.put(getHttpDestination().getHostField()); + } } // Add content headers diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java index fd8ae0bb52b..c8866e6dc00 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java @@ -253,15 +253,13 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest abort(x); } + public void send(Request request, Response.CompleteListener listener) + { + ((HttpRequest)request).sendAsync(this, listener); + } + protected void send(HttpRequest request, List listeners) { - if (!getScheme().equalsIgnoreCase(request.getScheme())) - throw new IllegalArgumentException("Invalid request scheme " + request.getScheme() + " for destination " + this); - if (!getHost().equalsIgnoreCase(request.getHost())) - throw new IllegalArgumentException("Invalid request host " + request.getHost() + " for destination " + this); - int port = request.getPort(); - if (port >= 0 && getPort() != port) - throw new IllegalArgumentException("Invalid request port " + port + " for destination " + this); send(new HttpExchange(this, request, listeners)); } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java index 53750ad0f2a..2a373e288b0 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java @@ -40,6 +40,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.LongConsumer; import java.util.function.Supplier; @@ -722,7 +723,7 @@ public class HttpRequest implements Request public ContentResponse send() throws InterruptedException, TimeoutException, ExecutionException { FutureResponseListener listener = new FutureResponseListener(this); - send(this, listener); + send(listener); try { @@ -761,15 +762,20 @@ public class HttpRequest implements Request @Override public void send(Response.CompleteListener listener) { - send(this, listener); + sendAsync(client::send, listener); } - private void send(HttpRequest request, Response.CompleteListener listener) + void sendAsync(HttpDestination destination, Response.CompleteListener listener) + { + sendAsync(destination::send, listener); + } + + private void sendAsync(BiConsumer> sender, Response.CompleteListener listener) { if (listener != null) responseListeners.add(listener); sent(); - client.send(request, responseListeners); + sender.accept(this, responseListeners); } void sent() diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java index ccc977159cc..7e58db34ce7 100644 --- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java +++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java @@ -36,6 +36,8 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.client.HttpDestination; +import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Response; @@ -44,7 +46,9 @@ import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.InputStreamResponseListener; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http2.FlowControlStrategy; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -733,6 +737,46 @@ public class HttpClientTest extends AbstractTest assertEquals(1, scenario.client.getDestinations().size()); } + @ParameterizedTest + @ArgumentsSource(TransportProvider.class) + public void testRequestWithDifferentDestination(Transport transport) throws Exception + { + init(transport); + + String requestScheme = HttpScheme.HTTPS.is(scenario.getScheme()) ? "http" : "https"; + String requestHost = "otherHost.com"; + int requestPort = 8888; + scenario.start(new EmptyServerHandler() + { + @Override + protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) + { + HttpURI uri = jettyRequest.getHttpURI(); + assertEquals(requestHost, uri.getHost()); + assertEquals(requestPort, uri.getPort()); + if (scenario.transport == Transport.H2C || scenario.transport == Transport.H2) + assertEquals(requestScheme, request.getScheme()); + } + }); + + Origin origin = new Origin(scenario.getScheme(), "localhost", scenario.getNetworkConnectorLocalPortInt().get()); + HttpDestination destination = scenario.client.resolveDestination(origin); + + org.eclipse.jetty.client.api.Request request = scenario.client.newRequest(requestHost, requestPort) + .scheme(requestScheme) + .path("/path"); + + CountDownLatch resultLatch = new CountDownLatch(1); + destination.send(request, result -> + { + assertTrue(result.isSucceeded()); + assertEquals(HttpStatus.OK_200, result.getResponse().getStatus()); + resultLatch.countDown(); + }); + + assertTrue(resultLatch.await(5, TimeUnit.SECONDS)); + } + private void sleep(long time) throws IOException { try