Skip to content

Commit 51b3d6b

Browse files
Volkan Yazicijaikiran
Volkan Yazici
authored andcommitted
8352431: java/net/httpclient/EmptyAuthenticate.java uses "localhost"
Reviewed-by: dfuchs, jpai
1 parent 9a3f999 commit 51b3d6b

File tree

1 file changed

+105
-40
lines changed

1 file changed

+105
-40
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,55 +24,120 @@
2424
/*
2525
* @test
2626
* @bug 8263899
27-
* @summary HttpClient throws NPE in AuthenticationFilter when parsing www-authenticate head
28-
*
29-
* @run main/othervm EmptyAuthenticate
27+
* @summary Verifies that empty `WWW-Authenticate` header is correctly parsed
28+
* @library /test/jdk/java/net/httpclient/lib
29+
* /test/lib
30+
* @build jdk.httpclient.test.lib.common.HttpServerAdapters
31+
* jdk.test.lib.net.SimpleSSLContext
32+
* @run junit EmptyAuthenticate
3033
*/
31-
import com.sun.net.httpserver.HttpServer;
34+
35+
import jdk.httpclient.test.lib.common.HttpServerAdapters;
36+
import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler;
37+
import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestServer;
38+
import jdk.test.lib.net.SimpleSSLContext;
39+
import org.junit.jupiter.params.ParameterizedTest;
40+
import org.junit.jupiter.params.provider.Arguments;
41+
import org.junit.jupiter.params.provider.MethodSource;
42+
43+
import javax.net.ssl.SSLContext;
3244
import java.io.IOException;
33-
import java.io.OutputStream;
34-
import java.net.InetSocketAddress;
3545
import java.net.URI;
36-
import java.net.URISyntaxException;
3746
import java.net.http.HttpClient;
47+
import java.net.http.HttpClient.Version;
48+
import java.net.http.HttpHeaders;
3849
import java.net.http.HttpRequest;
3950
import java.net.http.HttpResponse;
51+
import java.nio.charset.StandardCharsets;
52+
import java.util.stream.Stream;
4053

41-
public class EmptyAuthenticate {
42-
43-
public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {
44-
int port = 0;
45-
46-
//start server:
47-
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
48-
port = server.getAddress().getPort();
49-
server.createContext("/", exchange -> {
50-
String response = "test body";
51-
//this empty header will make the HttpClient throw NPE
52-
exchange.getResponseHeaders().add("www-authenticate", "");
53-
exchange.sendResponseHeaders(401, response.length());
54-
OutputStream os = exchange.getResponseBody();
55-
os.write(response.getBytes());
56-
os.close();
57-
});
58-
server.start();
54+
import static java.net.http.HttpClient.Builder.NO_PROXY;
55+
import static org.junit.jupiter.api.Assertions.assertEquals;
5956

60-
HttpResponse<String> response = null;
61-
//run client:
57+
class EmptyAuthenticate {
58+
59+
private static final SSLContext SSL_CONTEXT = createSslContext();
60+
61+
private static final String WWW_AUTH_HEADER_NAME = "WWW-Authenticate";
62+
63+
private static SSLContext createSslContext() {
6264
try {
63-
HttpClient httpClient = HttpClient.newHttpClient();
64-
HttpRequest request = HttpRequest.newBuilder(new URI("http://localhost:" + port + "/")).GET().build();
65-
//this line will throw NPE (wrapped by IOException) when parsing empty www-authenticate response header in AuthenticationFilter:
66-
response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
67-
boolean ok = !response.headers().firstValue("WWW-Authenticate").isEmpty();
68-
if (!ok) {
69-
throw new RuntimeException("WWW-Authenicate missing");
70-
}
71-
} catch (IOException e) {
72-
e.printStackTrace();
73-
throw new RuntimeException("Test failed");
65+
return new SimpleSSLContext().get();
66+
} catch (IOException exception) {
67+
throw new RuntimeException(exception);
68+
}
69+
}
70+
71+
@ParameterizedTest
72+
@MethodSource("args")
73+
void test(Version version, boolean secure) throws Exception {
74+
String handlerPath = "/%s/%s/".formatted(EmptyAuthenticate.class.getSimpleName(), version);
75+
String uriPath = handlerPath + (secure ? 's' : 'c');
76+
HttpTestServer server = createServer(version, secure, handlerPath);
77+
try (HttpClient client = createClient(version, secure)) {
78+
HttpRequest request = createRequest(server, secure, uriPath);
79+
HttpResponse<Void> response = client.send(request, HttpResponse.BodyHandlers.discarding());
80+
HttpHeaders responseHeaders = response.headers();
81+
assertEquals(
82+
"",
83+
responseHeaders.firstValue(WWW_AUTH_HEADER_NAME).orElse(null),
84+
() -> "was expecting empty `%s` header in: %s".formatted(
85+
WWW_AUTH_HEADER_NAME, responseHeaders.map()));
7486
} finally {
75-
server.stop(0);
87+
server.stop();
7688
}
7789
}
90+
91+
static Stream<Arguments> args() {
92+
return Stream
93+
.of(Version.HTTP_1_1, Version.HTTP_2)
94+
.flatMap(version -> Stream
95+
.of(true, false)
96+
.map(secure -> Arguments.of(version, secure)));
97+
}
98+
99+
private static HttpTestServer createServer(Version version, boolean secure, String uriPath)
100+
throws IOException {
101+
HttpTestServer server = secure
102+
? HttpTestServer.create(version, SSL_CONTEXT)
103+
: HttpTestServer.create(version);
104+
HttpTestHandler handler = new ServerHandlerRespondingWithEmptyWwwAuthHeader();
105+
server.addHandler(handler, uriPath);
106+
server.start();
107+
return server;
108+
}
109+
110+
private static final class ServerHandlerRespondingWithEmptyWwwAuthHeader implements HttpTestHandler {
111+
112+
private int responseIndex = 0;
113+
114+
@Override
115+
public synchronized void handle(HttpServerAdapters.HttpTestExchange exchange) throws IOException {
116+
try (exchange) {
117+
exchange.getResponseHeaders().addHeader(WWW_AUTH_HEADER_NAME, "");
118+
byte[] responseBodyBytes = "test body %d"
119+
.formatted(responseIndex)
120+
.getBytes(StandardCharsets.US_ASCII);
121+
exchange.sendResponseHeaders(401, responseBodyBytes.length);
122+
exchange.getResponseBody().write(responseBodyBytes);
123+
} finally {
124+
responseIndex++;
125+
}
126+
}
127+
128+
}
129+
130+
private static HttpClient createClient(Version version, boolean secure) {
131+
HttpClient.Builder clientBuilder = HttpClient.newBuilder().version(version).proxy(NO_PROXY);
132+
if (secure) {
133+
clientBuilder.sslContext(SSL_CONTEXT);
134+
}
135+
return clientBuilder.build();
136+
}
137+
138+
private static HttpRequest createRequest(HttpTestServer server, boolean secure, String uriPath) {
139+
URI uri = URI.create("%s://%s%s".formatted(secure ? "https" : "http", server.serverAuthority(), uriPath));
140+
return HttpRequest.newBuilder(uri).version(server.getVersion()).GET().build();
141+
}
142+
78143
}

0 commit comments

Comments
 (0)