diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java index d0a43da40c11..ad863f1b626f 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java @@ -141,6 +141,17 @@ public URI getURI() return URI.create(getOrigin().asString()); } + /** + *

Returns whether tunneling is required to reach the given server {@link Origin}.

+ *

Returns {@link true} when the server is secure, or when the server does not speak + * the same protocol as this proxy. + * An example of the latter case is sending an HTTP/2 clear-text request to the server + * via a proxy that only speaks HTTP/1.1, or an HTTP/1.1 request to a server that + * only speaks HTTP/2.

+ * + * @param serverOrigin the server {@link Origin} + * @return true if tunneling is required to reach the server + */ public boolean requiresTunnel(Origin serverOrigin) { if (HttpScheme.isSecure(serverOrigin.getScheme())) diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java index c89b983a8ea2..c90c5faf639d 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java @@ -15,12 +15,13 @@ import java.net.URI; -import org.eclipse.jetty.client.transport.HttpDestination; +import org.eclipse.jetty.client.internal.TunnelRequest; +import org.eclipse.jetty.client.transport.HttpRequest; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; /** - *

A protocol handler that handles the 401 response code + *

A protocol handler that handles the {@code 407} response code * in association with the {@code Proxy-Authenticate} header.

* * @see WWWAuthenticationProtocolHandler @@ -49,7 +50,14 @@ public String getName() @Override public boolean accept(Request request, Response response) { - return response.getStatus() == HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407; + return response.getStatus() == HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407 && isSentToProxy(request); + } + + private boolean isSentToProxy(Request request) + { + if (request instanceof TunnelRequest) + return true; + return ((HttpRequest)request).getHttpProxy() != null; } @Override @@ -67,8 +75,7 @@ protected HttpHeader getAuthorizationHeader() @Override protected URI getAuthenticationURI(Request request) { - HttpDestination destination = (HttpDestination)getHttpClient().resolveDestination(request); - ProxyConfiguration.Proxy proxy = destination.getProxy(); + HttpProxy proxy = ((HttpRequest)request).getHttpProxy(); return proxy != null ? proxy.getURI() : request.getURI(); } diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java index 5bd17976ddd6..2062b96cbebf 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java @@ -15,6 +15,7 @@ import java.net.URI; +import org.eclipse.jetty.client.internal.TunnelRequest; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; @@ -48,7 +49,7 @@ public String getName() @Override public boolean accept(Request request, Response response) { - return response.getStatus() == HttpStatus.UNAUTHORIZED_401; + return response.getStatus() == HttpStatus.UNAUTHORIZED_401 && !(request instanceof TunnelRequest); } @Override diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpConnection.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpConnection.java index e18dc4f4b32a..785886ccb496 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpConnection.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpConnection.java @@ -160,20 +160,24 @@ protected void normalizeRequest(HttpRequest request) if (proxy instanceof HttpProxy httpProxy) { boolean tunnelled = httpProxy.requiresTunnel(destination.getOrigin()); - - // RFC 9112, section 3.2.2: when making a request to a proxy other than CONNECT, - // the client must send the target URI in absolute-form as the request target. - // In practice, this is only valid for HTTP/1.1 requests that are not tunnelled. - if (http1 && !tunnelled) + if (!tunnelled) { - URI uri = request.getURI(); - if (uri != null) - request.path(uri.toString()); + // RFC 9112, section 3.2.2: when making a request to a proxy other than CONNECT, + // the client must send the target URI in absolute-form as the request target. + // In practice, this is only valid for HTTP/1.1 requests that are not tunnelled. + if (http1) + { + URI uri = request.getURI(); + if (uri != null) + request.path(uri.toString()); + } + + request.httpProxy(httpProxy); + + // Send the proxy credentials only when not tunnelled, + // otherwise proxy credentials are leaked to the server. + applyProxyAuthentication = true; } - - // Do not send proxy authentication headers when tunnelled, - // otherwise proxy credentials arrive to the server. - applyProxyAuthentication = !tunnelled; } // If we are HTTP 1.1, add the Host header. diff --git a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpRequest.java b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpRequest.java index 99de6226ec26..83204c0bf9ab 100644 --- a/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpRequest.java +++ b/jetty-core/jetty-client/src/main/java/org/eclipse/jetty/client/transport/HttpRequest.java @@ -44,6 +44,7 @@ import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.client.Destination; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpProxy; import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.PathRequestContent; import org.eclipse.jetty.client.Request; @@ -96,6 +97,7 @@ public class HttpRequest implements Request private Supplier trailers; private Object tag; private boolean normalized; + private HttpProxy httpProxy; public HttpRequest(HttpClient client, HttpConversation conversation, URI uri) { @@ -824,6 +826,16 @@ boolean normalized() return result; } + public HttpProxy getHttpProxy() + { + return httpProxy; + } + + void httpProxy(HttpProxy httpProxy) + { + this.httpProxy = httpProxy; + } + private String buildQuery() { StringBuilder result = new StringBuilder();