Issue #408 (Http client does not work on https with proxy)
Fixed by not considering authority-form targets to be URIs, so that the request is correctly copied after a 407.
This commit is contained in:
parent
e6c2c81bea
commit
4039f00bda
|
@ -26,6 +26,7 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Authentication;
|
import org.eclipse.jetty.client.api.Authentication;
|
||||||
|
import org.eclipse.jetty.client.api.Connection;
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
@ -171,8 +172,13 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
||||||
copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION);
|
copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION);
|
||||||
copyIfAbsent(request, newRequest, HttpHeader.PROXY_AUTHORIZATION);
|
copyIfAbsent(request, newRequest, HttpHeader.PROXY_AUTHORIZATION);
|
||||||
|
|
||||||
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult))
|
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult));
|
||||||
.send(null);
|
|
||||||
|
Connection connection = (Connection)request.getAttributes().get(HttpRequest.CONNECTION_ATTRIBUTE);
|
||||||
|
if (connection != null)
|
||||||
|
connection.send(newRequest, null);
|
||||||
|
else
|
||||||
|
newRequest.send(null);
|
||||||
}
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,6 @@ import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
@ -105,7 +104,7 @@ public abstract class HttpConnection implements Connection
|
||||||
|
|
||||||
URI uri = request.getURI();
|
URI uri = request.getURI();
|
||||||
|
|
||||||
if (proxy != null && !HttpMethod.CONNECT.is(method) && uri != null)
|
if (proxy != null && uri != null)
|
||||||
{
|
{
|
||||||
path = uri.toString();
|
path = uri.toString();
|
||||||
request.path(path);
|
request.path(path);
|
||||||
|
|
|
@ -146,6 +146,13 @@ public class HttpProxy extends ProxyConfiguration.Proxy
|
||||||
.idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS)
|
.idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS)
|
||||||
.timeout(connectTimeout, TimeUnit.MILLISECONDS);
|
.timeout(connectTimeout, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
// In case the proxy replies with a 407, we want
|
||||||
|
// to use the same connection for resending the
|
||||||
|
// request (this time with the Proxy-Authorization
|
||||||
|
// header), so we save it as an attribute to be
|
||||||
|
// used to send the next request.
|
||||||
|
connect.attribute(HttpRequest.CONNECTION_ATTRIBUTE, connection);
|
||||||
|
|
||||||
connection.send(connect, result ->
|
connection.send(connect, result ->
|
||||||
{
|
{
|
||||||
if (result.isFailed())
|
if (result.isFailed())
|
||||||
|
|
|
@ -59,6 +59,7 @@ import org.eclipse.jetty.util.Fields;
|
||||||
public class HttpRequest implements Request
|
public class HttpRequest implements Request
|
||||||
{
|
{
|
||||||
private static final URI NULL_URI = URI.create("null:0");
|
private static final URI NULL_URI = URI.create("null:0");
|
||||||
|
static final String CONNECTION_ATTRIBUTE = HttpRequest.class.getName() + ".connection";
|
||||||
|
|
||||||
private final HttpFields headers = new HttpFields();
|
private final HttpFields headers = new HttpFields();
|
||||||
private final Fields params = new Fields(true);
|
private final Fields params = new Fields(true);
|
||||||
|
@ -171,8 +172,6 @@ public class HttpRequest implements Request
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
String rawPath = uri.getRawPath();
|
String rawPath = uri.getRawPath();
|
||||||
if (uri.isOpaque())
|
|
||||||
rawPath = path;
|
|
||||||
if (rawPath == null)
|
if (rawPath == null)
|
||||||
rawPath = "";
|
rawPath = "";
|
||||||
this.path = rawPath;
|
this.path = rawPath;
|
||||||
|
@ -789,7 +788,7 @@ public class HttpRequest implements Request
|
||||||
URI result = newURI(path);
|
URI result = newURI(path);
|
||||||
if (result == null)
|
if (result == null)
|
||||||
return NULL_URI;
|
return NULL_URI;
|
||||||
if (!result.isAbsolute() && !result.isOpaque())
|
if (!result.isAbsolute())
|
||||||
result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
|
result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -801,7 +800,8 @@ public class HttpRequest implements Request
|
||||||
// Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!).
|
// Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!).
|
||||||
if ("*".equals(uri))
|
if ("*".equals(uri))
|
||||||
return null;
|
return null;
|
||||||
return new URI(uri);
|
URI result = new URI(uri);
|
||||||
|
return result.isOpaque() ? null : result;
|
||||||
}
|
}
|
||||||
catch (URISyntaxException x)
|
catch (URISyntaxException x)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.proxy;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -38,6 +39,7 @@ import org.eclipse.jetty.client.Origin;
|
||||||
import org.eclipse.jetty.client.api.Connection;
|
import org.eclipse.jetty.client.api.Connection;
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Destination;
|
import org.eclipse.jetty.client.api.Destination;
|
||||||
|
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||||
import org.eclipse.jetty.client.util.FutureResponseListener;
|
import org.eclipse.jetty.client.util.FutureResponseListener;
|
||||||
import org.eclipse.jetty.client.util.StringContentProvider;
|
import org.eclipse.jetty.client.util.StringContentProvider;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
@ -449,6 +451,54 @@ public class ForwardProxyTLSServerTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProxyAuthentication() throws Exception
|
||||||
|
{
|
||||||
|
startTLSServer(new ServerHandler());
|
||||||
|
String proxyRealm = "ProxyRealm";
|
||||||
|
startProxy(new ConnectHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||||
|
{
|
||||||
|
String proxyAuth = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
|
||||||
|
if (proxyAuth == null)
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
|
||||||
|
response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + proxyRealm + "\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.handle(target, baseRequest, request, response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
HttpClient httpClient = new HttpClient(newSslContextFactory());
|
||||||
|
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
|
||||||
|
URI proxyURI = URI.create("https://localhost:" + proxyConnector.getLocalPort());
|
||||||
|
httpClient.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword"));
|
||||||
|
httpClient.start();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String body = "BODY";
|
||||||
|
ContentResponse response = httpClient.newRequest("localhost", serverConnector.getLocalPort())
|
||||||
|
.scheme(HttpScheme.HTTPS.asString())
|
||||||
|
.method(HttpMethod.GET)
|
||||||
|
.path("/echo")
|
||||||
|
.param("body", body)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
|
String content = response.getContentAsString();
|
||||||
|
Assert.assertEquals(body, content);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
httpClient.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("External Proxy Server no longer stable enough for testing")
|
@Ignore("External Proxy Server no longer stable enough for testing")
|
||||||
public void testExternalProxy() throws Exception
|
public void testExternalProxy() throws Exception
|
||||||
|
|
Loading…
Reference in New Issue