--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/whitebox/java.net.http/jdk/internal/net/http/AuthenticationFilterTest.java Mon Feb 12 17:32:52 2018 +0000
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.net.http;
+
+import jdk.internal.net.http.common.HttpHeadersImpl;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.testng.annotations.AfterClass;
+
+import java.lang.ref.Reference;
+import java.net.Authenticator;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URL;
+import java.net.http.HttpClient;
+import java.net.http.HttpHeaders;
+import java.net.http.HttpResponse;
+import java.security.AccessController;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.net.http.HttpClient.Version;
+
+import static java.lang.String.format;
+import static java.lang.System.out;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.util.stream.Collectors.joining;
+import static java.net.http.HttpClient.Version.HTTP_1_1;
+import static java.net.http.HttpClient.Version.HTTP_2;
+import static java.net.http.HttpClient.Builder.NO_PROXY;
+import static org.testng.Assert.*;
+
+public class AuthenticationFilterTest {
+
+ @DataProvider(name = "uris")
+ public Object[][] responses() {
+ return new Object[][] {
+ { "http://foo.com", HTTP_1_1, null },
+ { "http://foo.com", HTTP_2, null },
+ { "http://foo.com#blah", HTTP_1_1, null },
+ { "http://foo.com#blah", HTTP_2, null },
+ { "http://foo.com/x/y/z", HTTP_1_1, null },
+ { "http://foo.com/x/y/z", HTTP_2, null },
+ { "http://foo.com/x/y/z#blah", HTTP_1_1, null },
+ { "http://foo.com/x/y/z#blah", HTTP_2, null },
+ { "http://foo.com:80", HTTP_1_1, null },
+ { "http://foo.com:80", HTTP_2, null },
+ { "http://foo.com:80#blah", HTTP_1_1, null },
+ { "http://foo.com:80#blah", HTTP_2, null },
+ { "http://foo.com", HTTP_1_1, "127.0.0.1:8080" },
+ { "http://foo.com", HTTP_2, "127.0.0.1:8080" },
+ { "http://foo.com#blah", HTTP_1_1, "127.0.0.1:8080" },
+ { "http://foo.com#blah", HTTP_2, "127.0.0.1:8080" },
+ { "http://foo.com:8080", HTTP_1_1, "127.0.0.1:8080" },
+ { "http://foo.com:8080", HTTP_2, "127.0.0.1:8080" },
+ { "http://foo.com:8080#blah", HTTP_1_1, "127.0.0.1:8080" },
+ { "http://foo.com:8080#blah", HTTP_2, "127.0.0.1:8080" },
+ { "https://foo.com", HTTP_1_1, null },
+ { "https://foo.com", HTTP_2, null },
+ { "https://foo.com#blah", HTTP_1_1, null },
+ { "https://foo.com#blah", HTTP_2, null },
+ { "https://foo.com:443", HTTP_1_1, null },
+ { "https://foo.com:443", HTTP_2, null },
+ { "https://foo.com:443#blah", HTTP_1_1, null },
+ { "https://foo.com:443#blah", HTTP_2, null },
+ { "https://foo.com", HTTP_1_1, "127.0.0.1:8080" },
+ { "https://foo.com", HTTP_2, "127.0.0.1:8080" },
+ { "https://foo.com#blah", HTTP_1_1, "127.0.0.1:8080" },
+ { "https://foo.com#blah", HTTP_2, "127.0.0.1:8080" },
+ { "https://foo.com:8080", HTTP_1_1, "127.0.0.1:8080" },
+ { "https://foo.com:8080", HTTP_2, "127.0.0.1:8080" },
+ { "https://foo.com:8080#blah", HTTP_1_1, "127.0.0.1:8080" },
+ { "https://foo.com:8080#blah", HTTP_2, "127.0.0.1:8080" },
+ { "http://foo.com:80/x/y/z", HTTP_1_1, null },
+ { "http://foo.com:80/x/y/z", HTTP_2, null },
+ { "http://foo.com:80/x/y/z#blah", HTTP_1_1, null },
+ { "http://foo.com:80/x/y/z#blah", HTTP_2, null },
+ { "http://foo.com/x/y/z", HTTP_1_1, "127.0.0.1:8080" },
+ { "http://foo.com/x/y/z", HTTP_2, "127.0.0.1:8080" },
+ { "http://foo.com/x/y/z#blah", HTTP_1_1, "127.0.0.1:8080" },
+ { "http://foo.com/x/y/z#blah", HTTP_2, "127.0.0.1:8080" },
+ { "http://foo.com:8080/x/y/z", HTTP_1_1, "127.0.0.1:8080" },
+ { "http://foo.com:8080/x/y/z", HTTP_2, "127.0.0.1:8080" },
+ { "http://foo.com:8080/x/y/z#blah", HTTP_1_1, "127.0.0.1:8080" },
+ { "http://foo.com:8080/x/y/z#blah", HTTP_2, "127.0.0.1:8080" },
+ { "https://foo.com/x/y/z", HTTP_1_1, null },
+ { "https://foo.com/x/y/z", HTTP_2, null },
+ { "https://foo.com/x/y/z#blah", HTTP_1_1, null },
+ { "https://foo.com/x/y/z#blah", HTTP_2, null },
+ { "https://foo.com:443/x/y/z", HTTP_1_1, null },
+ { "https://foo.com:443/x/y/z", HTTP_2, null },
+ { "https://foo.com:443/x/y/z#blah", HTTP_1_1, null },
+ { "https://foo.com:443/x/y/z#blah", HTTP_2, null },
+ { "https://foo.com/x/y/z", HTTP_1_1, "127.0.0.1:8080" },
+ { "https://foo.com/x/y/z", HTTP_2, "127.0.0.1:8080" },
+ { "https://foo.com/x/y/z#blah", HTTP_1_1, "127.0.0.1:8080" },
+ { "https://foo.com/x/y/z#blah", HTTP_2, "127.0.0.1:8080" },
+ { "https://foo.com:8080/x/y/z", HTTP_1_1, "127.0.0.1:8080" },
+ { "https://foo.com:8080/x/y/z", HTTP_2, "127.0.0.1:8080" },
+ { "https://foo.com:8080/x/y/z#blah", HTTP_1_1, "127.0.0.1:8080" },
+ { "https://foo.com:8080/x/y/z#blah", HTTP_2, "127.0.0.1:8080" },
+ };
+ }
+
+ static final ConcurrentMap<String,Throwable> FAILED = new ConcurrentHashMap<>();
+
+ static boolean isNullOrEmpty(String s) {
+ return s == null || s.isEmpty();
+ }
+
+ @Test(dataProvider = "uris")
+ public void testAuthentication(String uri, Version v, String proxy) throws Exception {
+ String test = format("testAuthentication: {\"%s\", %s, \"%s\"}", uri, v, proxy);
+ try {
+ doTestAuthentication(uri, v, proxy);
+ } catch(Exception | Error x) {
+ FAILED.putIfAbsent(test, x);
+ throw x;
+ }
+ }
+
+ @AfterClass
+ public void printDiagnostic() {
+ if (FAILED.isEmpty()) {
+ out.println("All tests passed");
+ return;
+ }
+ // make sure failures don't disappear in the overflow
+ out.println("Failed tests: ");
+ FAILED.keySet().forEach(s ->
+ out.println("\t " + s.substring(s.indexOf(':')+1) + ","));
+ out.println();
+ FAILED.entrySet().forEach(e -> {
+ System.err.println("\n" + e.getKey()
+ + " FAILED: " + e.getValue());
+ e.getValue().printStackTrace();
+ });
+ }
+
+ private void doTestAuthentication(String uri, Version v, String proxy) throws Exception {
+ int colon = proxy == null ? -1 : proxy.lastIndexOf(":");
+ ProxySelector ps = proxy == null ? NO_PROXY
+ : ProxySelector.of(InetSocketAddress.createUnresolved(
+ proxy.substring(0, colon),
+ Integer.parseInt(proxy.substring(colon+1))));
+ int unauthorized = proxy == null ? 401 : 407;
+
+ TestAuthenticator authenticator = new TestAuthenticator();
+
+ // Creates a HttpClientImpl
+ HttpClientBuilderImpl clientBuilder = new HttpClientBuilderImpl()
+ .authenticator(authenticator).proxy(ps);
+ HttpClientFacade facade = HttpClientImpl.create(clientBuilder);
+ HttpClientImpl client = facade.impl;
+ AuthenticationFilter filter = new AuthenticationFilter();
+
+ assertEquals(authenticator.COUNTER.get(), 0);
+
+ // Creates the first HttpRequestImpl, and call filter.request() with
+ // it. The expectation is that the filter will not add any credentials,
+ // because the cache is empty and we don't know which auth schemes the
+ // server supports yet.
+ URI reqURI = URI.create(uri);
+ HttpRequestBuilderImpl reqBuilder =
+ new HttpRequestBuilderImpl(reqURI);
+ HttpRequestImpl origReq = new HttpRequestImpl(reqBuilder);
+ HttpRequestImpl req = new HttpRequestImpl(origReq, ps, AccessController.getContext());
+ MultiExchange<?> multi = new MultiExchange<Void>(origReq, req, client,
+ HttpResponse.BodyHandler.replace(null),
+ null, AccessController.getContext());
+ Exchange<?> exchange = new Exchange<>(req, multi);
+ out.println("\nSimulating unauthenticated request to " + uri);
+ filter.request(req, multi);
+ assertFalse(req.getSystemHeaders().firstValue(authorization(true)).isPresent());
+ assertFalse(req.getSystemHeaders().firstValue(authorization(false)).isPresent());
+ assertEquals(authenticator.COUNTER.get(), 0);
+
+ // Creates the Response to the first request, and call filter.response
+ // with it. That response has a 401 or 407 status code.
+ // The expectation is that the filter will return a new request containing
+ // credentials, and will also cache the credentials in the multi exchange.
+ // The credentials shouldn't be put in the cache until the 200 response
+ // for that request arrives.
+ HttpHeadersImpl headers = new HttpHeadersImpl();
+ headers.addHeader(authenticate(proxy!=null),
+ "Basic realm=\"earth\"");
+ Response response = new Response(req, exchange, headers, unauthorized, v);
+ out.println("Simulating " + unauthorized
+ + " response from " + uri);
+ HttpRequestImpl next = filter.response(response);
+
+ out.println("Checking filter's response to "
+ + unauthorized + " from " + uri);
+ assertTrue(next != null, "next should not be null");
+ String[] up = check(reqURI, next.getSystemHeaders(), proxy);
+ assertEquals(authenticator.COUNTER.get(), 1);
+
+ // Now simulate a new successful exchange to get the credentials in the cache
+ // We first call filter.request with the request that was previously
+ // returned by the filter, then create a new Response with a 200 status
+ // code, and feed that to the filter with filter.response.
+ // At this point, the credentials will be added to the cache.
+ out.println("Simulating next request with credentials to " + uri);
+ exchange = new Exchange<>(next, multi);
+ filter.request(next, multi);
+ out.println("Checking credentials in request header after filter for " + uri);
+ check(reqURI, next.getSystemHeaders(), proxy);
+ check(next.uri(), next.getSystemHeaders(), proxy);
+ out.println("Simulating successful response 200 from " + uri);
+ response = new Response(next, exchange, new HttpHeadersImpl(), 200, v);
+ next = filter.response(response);
+ assertTrue(next == null, "next should be null");
+ assertEquals(authenticator.COUNTER.get(), 1);
+
+ // Now verify that the cache is used for the next request to the same server.
+ // We're going to create a request to the same server by appending "/bar" to
+ // the original request path. Then we're going to feed that to filter.request
+ // The expectation is that filter.request will add the credentials to the
+ // request system headers, because it should find them in the cache.
+ int fragmentIndex = uri.indexOf('#');
+ String subpath = "/bar";
+ String prefix = uri;
+ String fragment = "";
+ if (fragmentIndex > -1) {
+ prefix = uri.substring(0, fragmentIndex);
+ fragment = uri.substring(fragmentIndex);
+ }
+ URI reqURI2 = URI.create(prefix + subpath + fragment);
+ out.println("Simulating new request to " + reqURI2);
+ HttpRequestBuilderImpl reqBuilder2 =
+ new HttpRequestBuilderImpl(reqURI2);
+ HttpRequestImpl origReq2 = new HttpRequestImpl(reqBuilder2);
+ HttpRequestImpl req2 = new HttpRequestImpl(origReq2, ps, AccessController.getContext());
+ MultiExchange<?> multi2 = new MultiExchange<Void>(origReq2, req2, client,
+ HttpResponse.BodyHandler.replace(null),
+ null, AccessController.getContext());
+ filter.request(req2, multi2);
+ out.println("Check that filter has added credentials from cache for " + reqURI2
+ + " with proxy " + req2.proxy());
+ String[] up2 = check(reqURI, req2.getSystemHeaders(), proxy);
+ assertTrue(Arrays.deepEquals(up, up2), format("%s:%s != %s:%s", up2[0], up2[1], up[0], up[1]));
+ assertEquals(authenticator.COUNTER.get(), 1);
+
+ // Now verify that the cache is not used if we send a request to a different server.
+ // We're going to append ".bar" to the original request host name, and feed that
+ // to filter.request.
+ // There are actually two cases: if we were using a proxy, then the new request
+ // should contain proxy credentials. If we were not using a proxy, then it should
+ // not contain any credentials at all.
+ URI reqURI3;
+ if (isNullOrEmpty(reqURI.getPath())
+ && isNullOrEmpty(reqURI.getFragment())
+ && reqURI.getPort() == -1) {
+ reqURI3 = URI.create(uri + ".bar");
+ } else {
+ reqURI3 = new URI(reqURI.getScheme(), reqURI.getUserInfo(),
+ reqURI.getHost() + ".bar", reqURI.getPort(),
+ reqURI.getPath(), reqURI.getQuery(),
+ reqURI.getFragment());
+ }
+ out.println("Simulating new request to " + reqURI3);
+ HttpRequestBuilderImpl reqBuilder3 =
+ new HttpRequestBuilderImpl(reqURI3);
+ HttpRequestImpl origReq3 = new HttpRequestImpl(reqBuilder3);
+ HttpRequestImpl req3 = new HttpRequestImpl(origReq3, ps, AccessController.getContext());
+ MultiExchange<?> multi3 = new MultiExchange<Void>(origReq3, req3, client,
+ HttpResponse.BodyHandler.replace(null),
+ null, AccessController.getContext());
+ filter.request(req3, multi3);
+ if (proxy == null) {
+ out.println("Check that filter has not added proxy credentials from cache for " + reqURI3);
+ assert !req3.getSystemHeaders().firstValue(authorization(true)).isPresent()
+ : format("Unexpected proxy credentials found: %s",
+ java.util.stream.Stream.of(getAuthorization(req3.getSystemHeaders(), true))
+ .collect(joining(":")));
+ assertFalse(req3.getSystemHeaders().firstValue(authorization(true)).isPresent());
+ } else {
+ out.println("Check that filter has added proxy credentials from cache for " + reqURI3);
+ String[] up3 = check(reqURI, req3.getSystemHeaders(), proxy);
+ assertTrue(Arrays.deepEquals(up, up3), format("%s:%s != %s:%s", up3[0], up3[1], up[0], up[1]));
+ }
+ out.println("Check that filter has not added server credentials from cache for " + reqURI3);
+ assert !req3.getSystemHeaders().firstValue(authorization(false)).isPresent()
+ : format("Unexpected server credentials found: %s",
+ java.util.stream.Stream.of(getAuthorization(req3.getSystemHeaders(), false))
+ .collect(joining(":")));
+ assertFalse(req3.getSystemHeaders().firstValue(authorization(false)).isPresent());
+ assertEquals(authenticator.COUNTER.get(), 1);
+
+ // Now we will verify that credentials for proxies are not used for servers and
+ // conversely.
+ // If we were using a proxy, we're now going to send a request to the proxy host,
+ // without using a proxy, and verify that filter.request neither add proxy credential
+ // or server credential to that host.
+ // I we were not using a proxy, we're going to send a request to the original
+ // server, using a proxy whose address matches the original server.
+ // We expect that the cache will add server credentials, but not proxy credentials.
+ int port = reqURI.getPort();
+ port = port == -1 ? defaultPort(reqURI.getScheme()) : port;
+ ProxySelector fakeProxy = proxy == null
+ ? ProxySelector.of(InetSocketAddress.createUnresolved(
+ reqURI.getHost(), port))
+ : NO_PROXY;
+ URI reqURI4 = proxy == null ? reqURI : new URI("http", null, req.proxy().getHostName(),
+ req.proxy().getPort(), "/", null, null);
+ HttpRequestBuilderImpl reqBuilder4 = new HttpRequestBuilderImpl(reqURI4);
+ HttpRequestImpl origReq4 = new HttpRequestImpl(reqBuilder4);
+ HttpRequestImpl req4 = new HttpRequestImpl(origReq4, fakeProxy,
+ AccessController.getContext());
+ MultiExchange<?> multi4 = new MultiExchange<Void>(origReq4, req4, client,
+ HttpResponse.BodyHandler.replace(null), null,
+ AccessController.getContext());
+ out.println("Simulating new request to " + reqURI4 + " with a proxy " + req4.proxy());
+ assertTrue((req4.proxy() == null) == (proxy != null),
+ "(req4.proxy() == null) == (proxy != null) should be true");
+ filter.request(req4, multi4);
+ out.println("Check that filter has not added proxy credentials from cache for "
+ + reqURI4 + " (proxy: " + req4.proxy() + ")");
+ assert !req4.getSystemHeaders().firstValue(authorization(true)).isPresent()
+ : format("Unexpected proxy credentials found: %s",
+ java.util.stream.Stream.of(getAuthorization(req4.getSystemHeaders(), true))
+ .collect(joining(":")));
+ assertFalse(req4.getSystemHeaders().firstValue(authorization(true)).isPresent());
+ if (proxy != null) {
+ out.println("Check that filter has not added server credentials from cache for "
+ + reqURI4 + " (proxy: " + req4.proxy() + ")");
+ assert !req4.getSystemHeaders().firstValue(authorization(false)).isPresent()
+ : format("Unexpected server credentials found: %s",
+ java.util.stream.Stream.of(getAuthorization(req4.getSystemHeaders(), false))
+ .collect(joining(":")));
+ assertFalse(req4.getSystemHeaders().firstValue(authorization(false)).isPresent());
+ } else {
+ out.println("Check that filter has added server credentials from cache for "
+ + reqURI4 + " (proxy: " + req4.proxy() + ")");
+ String[] up4 = check(reqURI, req4.getSystemHeaders(), proxy);
+ assertTrue(Arrays.deepEquals(up, up4), format("%s:%s != %s:%s", up4[0], up4[1], up[0], up[1]));
+ }
+ assertEquals(authenticator.COUNTER.get(), 1);
+
+ if (proxy != null) {
+ // Now if we were using a proxy, we're going to send the same request than
+ // the original request, but without a proxy, and verify that this time
+ // the cache does not add any server or proxy credential. It should not
+ // add server credential because it should not have them (we only used
+ // proxy authentication so far) and it should not add proxy credentials
+ // because the request has no proxy.
+ HttpRequestBuilderImpl reqBuilder5 = new HttpRequestBuilderImpl(reqURI);
+ HttpRequestImpl origReq5 = new HttpRequestImpl(reqBuilder5);
+ HttpRequestImpl req5 = new HttpRequestImpl(origReq5, NO_PROXY,
+ AccessController.getContext());
+ MultiExchange<?> multi5 = new MultiExchange<Void>(origReq5, req5, client,
+ HttpResponse.BodyHandler.replace(null), null,
+ AccessController.getContext());
+ out.println("Simulating new request to " + reqURI + " with a proxy " + req5.proxy());
+ assertTrue(req5.proxy() == null, "req5.proxy() should be null");
+ Exchange<?> exchange5 = new Exchange<>(req5, multi5);
+ filter.request(req5, multi5);
+ out.println("Check that filter has not added server credentials from cache for "
+ + reqURI + " (proxy: " + req5.proxy() + ")");
+ assert !req5.getSystemHeaders().firstValue(authorization(false)).isPresent()
+ : format("Unexpected server credentials found: %s",
+ java.util.stream.Stream.of(getAuthorization(req5.getSystemHeaders(), false))
+ .collect(joining(":")));
+ assertFalse(req5.getSystemHeaders().firstValue(authorization(false)).isPresent());
+ out.println("Check that filter has not added proxy credentials from cache for "
+ + reqURI + " (proxy: " + req5.proxy() + ")");
+ assert !req5.getSystemHeaders().firstValue(authorization(true)).isPresent()
+ : format("Unexpected proxy credentials found: %s",
+ java.util.stream.Stream.of(getAuthorization(req5.getSystemHeaders(), true))
+ .collect(joining(":")));
+ assertFalse(req5.getSystemHeaders().firstValue(authorization(true)).isPresent());
+ assertEquals(authenticator.COUNTER.get(), 1);
+
+ // Now simulate a 401 response from the server
+ HttpHeadersImpl headers5 = new HttpHeadersImpl();
+ headers5.addHeader(authenticate(false),
+ "Basic realm=\"earth\"");
+ unauthorized = 401;
+ Response response5 = new Response(req5, exchange5, headers5, unauthorized, v);
+ out.println("Simulating " + unauthorized
+ + " response from " + uri);
+ HttpRequestImpl next5 = filter.response(response5);
+ assertEquals(authenticator.COUNTER.get(), 2);
+
+ out.println("Checking filter's response to "
+ + unauthorized + " from " + uri);
+ assertTrue(next5 != null, "next5 should not be null");
+ String[] up5 = check(reqURI, next5.getSystemHeaders(), null);
+
+ // now simulate a 200 response from the server
+ exchange5 = new Exchange<>(next5, multi5);
+ filter.request(next5, multi5);
+ response5 = new Response(next5, exchange5, new HttpHeadersImpl(), 200, v);
+ filter.response(response5);
+ assertEquals(authenticator.COUNTER.get(), 2);
+
+ // now send the request again, with proxy this time, and it should have both
+ // server auth and proxy auth
+ HttpRequestBuilderImpl reqBuilder6 = new HttpRequestBuilderImpl(reqURI);
+ HttpRequestImpl origReq6 = new HttpRequestImpl(reqBuilder6);
+ HttpRequestImpl req6 = new HttpRequestImpl(origReq6, ps,
+ AccessController.getContext());
+ MultiExchange<?> multi6 = new MultiExchange<Void>(origReq6, req6, client,
+ HttpResponse.BodyHandler.replace(null), null,
+ AccessController.getContext());
+ out.println("Simulating new request to " + reqURI + " with a proxy " + req6.proxy());
+ assertTrue(req6.proxy() != null, "req6.proxy() should not be null");
+ Exchange<?> exchange6 = new Exchange<>(req6, multi6);
+ filter.request(req6, multi6);
+ out.println("Check that filter has added server credentials from cache for "
+ + reqURI + " (proxy: " + req6.proxy() + ")");
+ check(reqURI, req6.getSystemHeaders(), null);
+ out.println("Check that filter has added proxy credentials from cache for "
+ + reqURI + " (proxy: " + req6.proxy() + ")");
+ String[] up6 = check(reqURI, req6.getSystemHeaders(), proxy);
+ assertTrue(Arrays.deepEquals(up, up6), format("%s:%s != %s:%s", up6[0], up6[1], up[0], up[1]));
+ assertEquals(authenticator.COUNTER.get(), 2);
+ }
+
+ if (proxy == null && uri.contains("x/y/z")) {
+ URI reqURI7 = URI.create(prefix + "/../../w/z" + fragment);
+ assertTrue(reqURI7.getPath().contains("../../"));
+ HttpRequestBuilderImpl reqBuilder7 = new HttpRequestBuilderImpl(reqURI7);
+ HttpRequestImpl origReq7 = new HttpRequestImpl(reqBuilder7);
+ HttpRequestImpl req7 = new HttpRequestImpl(origReq7, ps,
+ AccessController.getContext());
+ MultiExchange<?> multi7 = new MultiExchange<Void>(origReq7, req7, client,
+ HttpResponse.BodyHandler.replace(null), null,
+ AccessController.getContext());
+ out.println("Simulating new request to " + reqURI7 + " with a proxy " + req7.proxy());
+ assertTrue(req7.proxy() == null, "req7.proxy() should be null");
+ Exchange<?> exchange7 = new Exchange<>(req7, multi7);
+ filter.request(req7, multi7);
+ out.println("Check that filter has not added server credentials from cache for "
+ + reqURI7 + " (proxy: " + req7.proxy() + ") [resolved uri: "
+ + reqURI7.resolve(".") + " should not match " + reqURI.resolve(".") + "]");
+ assert !req7.getSystemHeaders().firstValue(authorization(false)).isPresent()
+ : format("Unexpected server credentials found: %s",
+ java.util.stream.Stream.of(getAuthorization(req7.getSystemHeaders(), false))
+ .collect(joining(":")));
+ assertFalse(req7.getSystemHeaders().firstValue(authorization(false)).isPresent());
+ out.println("Check that filter has not added proxy credentials from cache for "
+ + reqURI7 + " (proxy: " + req7.proxy() + ")");
+ assert !req7.getSystemHeaders().firstValue(authorization(true)).isPresent()
+ : format("Unexpected proxy credentials found: %s",
+ java.util.stream.Stream.of(getAuthorization(req7.getSystemHeaders(), true))
+ .collect(joining(":")));
+ assertFalse(req7.getSystemHeaders().firstValue(authorization(true)).isPresent());
+ assertEquals(authenticator.COUNTER.get(), 1);
+
+ }
+
+ Reference.reachabilityFence(facade);
+ }
+
+ static int defaultPort(String protocol) {
+ if ("http".equalsIgnoreCase(protocol)) return 80;
+ if ("https".equalsIgnoreCase(protocol)) return 443;
+ return -1;
+ }
+
+ static String authenticate(boolean proxy) {
+ return proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
+ }
+ static String authorization(boolean proxy) {
+ return proxy ? "Proxy-Authorization" : "Authorization";
+ }
+
+ static String[] getAuthorization(HttpHeaders headers, boolean proxy) {
+ String auth = headers.firstValue(authorization(proxy)).get().substring(6);
+ String pw = new String(Base64.getDecoder().decode(auth), US_ASCII);
+ String[] up = pw.split(":");
+ up[1] = new String(Base64.getDecoder().decode(up[1]), US_ASCII);
+ return up;
+ }
+
+ static Authenticator.RequestorType requestorType(boolean proxy) {
+ return proxy ? Authenticator.RequestorType.PROXY
+ : Authenticator.RequestorType.SERVER;
+ }
+
+ static String[] check(URI reqURI, HttpHeaders headers, String proxy) throws Exception {
+ out.println("Next request headers: " + headers.map());
+ String[] up = getAuthorization(headers, proxy != null);
+ String u = up[0];
+ String p = up[1];
+ out.println("user:password: " + u + ":" + p);
+ String protocol = proxy != null ? "http" : reqURI.getScheme();
+ String expectedUser = "u." + protocol;
+ assertEquals(u, expectedUser);
+ String host = proxy == null ? reqURI.getHost() :
+ proxy.substring(0, proxy.lastIndexOf(':'));
+ int port = proxy == null ? reqURI.getPort()
+ : Integer.parseInt(proxy.substring(proxy.lastIndexOf(':')+1));
+ String expectedPw = concat(requestorType(proxy!=null),
+ "basic", protocol, host,
+ port, "earth", reqURI.toURL());
+ assertEquals(p, expectedPw);
+ return new String[] {u, p};
+ }
+
+ static String concat(Authenticator.RequestorType reqType,
+ String authScheme,
+ String requestingProtocol,
+ String requestingHost,
+ int requestingPort,
+ String realm,
+ URL requestingURL) {
+ return new StringBuilder()
+ .append(reqType).append(":")
+ .append(authScheme).append(":")
+ .append(String.valueOf(realm))
+ .append("[")
+ .append(requestingProtocol).append(':')
+ .append(requestingHost).append(':')
+ .append(requestingPort).append("]")
+ .append("/").append(String.valueOf(requestingURL))
+ .toString();
+ }
+
+ static class TestAuthenticator extends Authenticator {
+ final AtomicLong COUNTER = new AtomicLong();
+ @Override
+ public PasswordAuthentication getPasswordAuthentication() {
+ COUNTER.incrementAndGet();
+ return new PasswordAuthentication("u."+getRequestingProtocol(),
+ Base64.getEncoder().encodeToString(concat().getBytes(US_ASCII))
+ .toCharArray());
+ }
+
+ String concat() {
+ return AuthenticationFilterTest.concat(
+ getRequestorType(),
+ getRequestingScheme(),
+ getRequestingProtocol(),
+ getRequestingHost(),
+ getRequestingPort(),
+ getRequestingPrompt(),
+ getRequestingURL());
+ }
+
+ }
+}