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:
Simone Bordet 2016-03-10 10:08:50 +01:00
parent e6c2c81bea
commit 4039f00bda
5 changed files with 70 additions and 8 deletions

View File

@ -26,6 +26,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.Request;
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.PROXY_AUTHORIZATION);
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult))
.send(null);
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult));
Connection connection = (Connection)request.getAttributes().get(HttpRequest.CONNECTION_ATTRIBUTE);
if (connection != null)
connection.send(newRequest, null);
else
newRequest.send(null);
}
catch (Throwable x)
{

View File

@ -35,7 +35,6 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -105,7 +104,7 @@ public abstract class HttpConnection implements Connection
URI uri = request.getURI();
if (proxy != null && !HttpMethod.CONNECT.is(method) && uri != null)
if (proxy != null && uri != null)
{
path = uri.toString();
request.path(path);

View File

@ -146,6 +146,13 @@ public class HttpProxy extends ProxyConfiguration.Proxy
.idleTimeout(2 * 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 ->
{
if (result.isFailed())

View File

@ -59,6 +59,7 @@ import org.eclipse.jetty.util.Fields;
public class HttpRequest implements Request
{
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 Fields params = new Fields(true);
@ -171,8 +172,6 @@ public class HttpRequest implements Request
else
{
String rawPath = uri.getRawPath();
if (uri.isOpaque())
rawPath = path;
if (rawPath == null)
rawPath = "";
this.path = rawPath;
@ -789,7 +788,7 @@ public class HttpRequest implements Request
URI result = newURI(path);
if (result == null)
return NULL_URI;
if (!result.isAbsolute() && !result.isOpaque())
if (!result.isAbsolute())
result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
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 "*" (!).
if ("*".equals(uri))
return null;
return new URI(uri);
URI result = new URI(uri);
return result.isOpaque() ? null : result;
}
catch (URISyntaxException x)
{

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.proxy;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.URI;
import java.net.URLEncoder;
import java.util.concurrent.CountDownLatch;
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.ContentResponse;
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.StringContentProvider;
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
@Ignore("External Proxy Server no longer stable enough for testing")
public void testExternalProxy() throws Exception