undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2020, 2024, 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.
*/
/*
* @test
* @bug 8251496
* @summary Tests for methods in Authenticator
* @run junit AuthenticatorTest
*/
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.HttpPrincipal;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class AuthenticatorTest {
@Test
public void testFailure() {
var failureResult = new Authenticator.Failure(666);
assertEquals(666, failureResult.getResponseCode());
}
@Test
public void testRetry() {
var retryResult = new Authenticator.Retry(333);
assertEquals(333, retryResult.getResponseCode());
}
@Test
public void testSuccess() {
var principal = new HttpPrincipal("test", "123");
var successResult = new Authenticator.Success(principal);
assertEquals(principal, successResult.getPrincipal());
assertEquals("test", principal.getUsername());
assertEquals("123", principal.getRealm());
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2024, 2025, 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.
*/
/*
* @test
* @bug 8144100
* @summary checking token sent by client should be done in case-insensitive manner
* @run main BasicAuthToken
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Base64;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.BasicAuthenticator;
import com.sun.net.httpserver.HttpServer;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
public class BasicAuthToken {
private static final String CRLF = "\r\n";
private static final String someContext = "/test";
public static void main(String[] args) throws Exception {
HttpServer server = server();
try {
client(server.getAddress().getPort());
} finally {
server.stop(0);
}
}
static HttpServer server() throws Exception {
String realm = "someRealm";
ServerAuthenticator authenticator = new ServerAuthenticator(realm);
HttpServer server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
server.createContext(someContext, exchange -> {
if (authenticator.authenticate(exchange) instanceof Authenticator.Failure) {
exchange.sendResponseHeaders(401, RSPBODY_EMPTY);
exchange.close();
return;
}
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
exchange.close();
}).setAuthenticator(authenticator);
server.start();
return server;
}
static void client(int port) throws Exception {
try (Socket socket = new Socket(InetAddress.getLoopbackAddress(), port)) {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String credentials = "username:password";
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());
writer.write("GET " + someContext + " HTTP/1.1" + CRLF);
writer.write("Host: localhost:" + port + CRLF);
writer.write("User-Agent: Java/" + System.getProperty("java.version") + CRLF);
writer.write("Authorization: BAsIc " + encodedCredentials + CRLF);
writer.write(CRLF);
writer.flush();
System.err.println("Server response");
String statusLine = reader.readLine();
System.err.println(statusLine);
if (!statusLine.startsWith("HTTP/1.1 200")) {
throw new RuntimeException("unexpected status line: " + statusLine);
}
if (!ServerAuthenticator.wasChecked()) {
throw new RuntimeException("Authenticator wasn't invoked");
}
}
}
static class ServerAuthenticator extends BasicAuthenticator {
private static volatile boolean invoked = false;
ServerAuthenticator(String realm) {
super(realm);
}
public static boolean wasChecked() {
return invoked;
}
@Override
public boolean checkCredentials(String username, String password) {
String validUsername = "username", validPassword = "password";
invoked = true;
return username.equals(validUsername) && password.equals(validPassword);
}
}
}

View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @bug 8275534
* @summary Check that ISO-8859-1 encoded realm strings are transported correctly
* with HttpURLConnection and HttpClient
* @modules jdk.httpserver
* @library /test/lib
* @run junit/othervm BasicAuthenticatorRealm
*/
import com.sun.net.httpserver.BasicAuthenticator;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpHandlers;
import com.sun.net.httpserver.HttpServer;
import java.net.Authenticator;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import jdk.test.lib.net.URIBuilder;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/**
* The second test @Order(2) must run after the first test because it
* sets a VM wide authenticator and the first test depends on no authenticator
* being set.
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BasicAuthenticatorRealm {
static final String REALM = "U\u00ffU@realm"; // non-ASCII char
static final String EXPECTED_AUTH_HEADER_VALUE = "Basic realm=\"U\u00ffU@realm\", charset=\"UTF-8\"";
static final InetAddress LOOPBACK_ADDR = InetAddress.getLoopbackAddress();
@Test
@Order(1)
public void testURLConnection() throws Exception {
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 0);
var handler = HttpHandlers.of(200, Headers.of(), "");
var context = server.createContext("/test", handler);
var auth = new ServerAuthenticator(REALM);
context.setAuthenticator(auth);
try {
server.start();
var url = uri(server).toURL();
var connection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
assertEquals(401, connection.getResponseCode());
assertEquals(EXPECTED_AUTH_HEADER_VALUE, connection.getHeaderField("WWW-Authenticate"));
} finally {
server.stop(0);
}
}
@Test
@Order(2)
public void testURLConnectionAuthenticated() throws Exception {
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 0);
var handler = HttpHandlers.of(200, Headers.of(), "foo");
var context = server.createContext("/test", handler);
var auth = new ServerAuthenticator(REALM);
context.setAuthenticator(auth);
Authenticator.setDefault(new ClientAuthenticator());
try {
server.start();
var url = uri(server).toURL();
var connection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
assertEquals(200, connection.getResponseCode());
Assertions.assertArrayEquals("foo".getBytes(UTF_8), connection.getInputStream().readAllBytes());
} finally {
server.stop(0);
}
}
@Test
@Order(3)
public void testHttpClient() throws Exception {
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 0);
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server)).build();
var handler = HttpHandlers.of(200, Headers.of(), "foo");
var context = server.createContext("/test", handler);
var authenticator = new ServerAuthenticator(REALM);
context.setAuthenticator(authenticator);
try {
server.start();
var response = client.send(request, BodyHandlers.ofString(UTF_8));
assertEquals(401, response.statusCode());
assertEquals(EXPECTED_AUTH_HEADER_VALUE, response.headers().firstValue("WWW-Authenticate").orElseThrow());
} finally {
server.stop(0);
}
}
@Test
@Order(4)
public void testHttpClientAuthenticated() throws Exception {
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 0);
var request = HttpRequest.newBuilder(uri(server)).build();
var handler = HttpHandlers.of(200, Headers.of(), "foo");
var context = server.createContext("/test", handler);
var auth = new ServerAuthenticator(REALM);
var client = HttpClient.newBuilder()
.proxy(NO_PROXY)
.authenticator(new ClientAuthenticator())
.build();
context.setAuthenticator(auth);
try {
server.start();
var response = client.send(request, BodyHandlers.ofString(UTF_8));
assertEquals(200, response.statusCode());
assertEquals("foo", response.body());
} finally {
server.stop(0);
}
}
static class ServerAuthenticator extends BasicAuthenticator {
ServerAuthenticator(String realm) {
super(realm);
}
@Override
public boolean checkCredentials(String username, String password) {
if (!getRealm().equals(realm)) {
return false;
}
return true;
}
}
static class ClientAuthenticator extends java.net.Authenticator {
@Override
public PasswordAuthentication getPasswordAuthentication() {
if (!getRequestingPrompt().equals(REALM)) {
throw new RuntimeException("realm does not match");
}
return new PasswordAuthentication("username", "password".toCharArray());
}
}
public static URI uri(HttpServer server) {
return URIBuilder.newBuilder()
.scheme("http")
.host(server.getAddress().getAddress())
.port(server.getAddress().getPort())
.path("/test/")
.buildUnchecked();
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2026, 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.
*/
/*
* @test
* @bug 8373677
* @summary Tests for verifying that a non-SSL server can detect
* when a client attempts to use SSL.
* @library /test/lib
* @run junit/othervm ${test.main.class}
*/
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLException;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.junit.jupiter.api.Assertions.*;
public class ClearTextServerSSL {
static final InetAddress LOOPBACK_ADDR = InetAddress.getLoopbackAddress();
static final boolean ENABLE_LOGGING = true;
static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
static final String CTXT_PATH = "/ClearTextServerSSL";
@BeforeAll
public static void setup() {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
logger.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
logger.addHandler(ch);
}
}
@Test
public void test() throws Exception {
var sslContext = SimpleSSLContext.findSSLContext();
var handler = new TestHandler();
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 0);
server.createContext(path(""), handler);
server.start();
try (var client = HttpClient.newBuilder()
.sslContext(sslContext)
.proxy(NO_PROXY)
.build()) {
var request = HttpRequest.newBuilder()
.uri(uri("http", server, path("/clear")))
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
var sslRequest = HttpRequest.newBuilder()
.uri(uri("https", server, path("/ssl")))
.build();
Assertions.assertThrows(SSLException.class, () -> {
client.send(sslRequest, HttpResponse.BodyHandlers.ofString());
});
try (var socket = new Socket()) {
socket.connect(server.getAddress());
byte[] badRequest = {
22, 'B', 'A', 'D', ' ',
'/', ' ' ,
'H', 'T', 'T', 'P', '/', '1', '.', '1' };
socket.getOutputStream().write(badRequest);
socket.getOutputStream().flush();
var reader = new InputStreamReader(socket.getInputStream());
var line = reader.readAllLines();
Assertions.assertEquals("HTTP/1.1 400 Bad Request", line.get(0));
System.out.println("Got expected response:");
line.stream().map(l -> "\t" + l).forEach(System.out::println);
}
} finally {
server.stop(0);
}
}
// --- infra ---
static String path(String path) {
assert CTXT_PATH.startsWith("/");
assert !CTXT_PATH.endsWith("/");
if (path.startsWith("/")) {
return CTXT_PATH + path;
} else {
return CTXT_PATH + "/" + path;
}
}
static URI uri(String scheme, HttpServer server, String path) throws URISyntaxException {
return URIBuilder.newBuilder()
.scheme(scheme)
.loopback()
.port(server.getAddress().getPort())
.path(path)
.build();
}
/**
* A test handler that reads any request bytes and sends
* an empty 200 response
*/
static class TestHandler implements HttpHandler {
@java.lang.Override
public void handle(HttpExchange exchange) throws IOException {
try (var reqBody = exchange.getRequestBody()) {
reqBody.readAllBytes();
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
} catch (Throwable t) {
t.printStackTrace();
exchange.sendResponseHeaders(500, RSPBODY_EMPTY);
}
}
}
}

View file

@ -0,0 +1,251 @@
/*
* Copyright (c) 2026, 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.
*/
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.http.HttpClient;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
/*
* @test id=default
* @bug 8272758
* @summary Verifies path prefix matching using defaults
* @build EchoHandler
* @run junit ${test.main.class}
*/
/*
* @test id=withProperty
* @bug 8272758
* @summary Verifies path prefix matching by providing a system property
* @build EchoHandler
* @run junit/othervm
* -Dsun.net.httpserver.pathMatcher=pathPrefix
* ${test.main.class}
*/
/*
* @test id=withInvalidProperty
* @bug 8272758
* @summary Verifies path prefix matching by providing a system property
* containing an invalid value, and observing it fall back to the
* default
* @build EchoHandler
* @run junit/othervm
* -Dsun.net.httpserver.pathMatcher=noSuchMatcher
* ${test.main.class}
*/
public class ContextPathMatcherPathPrefixTest {
protected static final HttpClient CLIENT =
HttpClient.newBuilder().proxy(NO_PROXY).build();
@AfterAll
static void stopClient() {
CLIENT.shutdownNow();
}
@Test
void testContextPathOfEmptyString() {
var iae = assertThrows(IllegalArgumentException.class, () -> new Infra(""));
assertEquals("Illegal value for path or protocol", iae.getMessage());
}
@Test
void testContextPathAtRoot() throws Exception {
// Repeating all cases with both known (GET) and unknown (DOH) request methods to stress both paths
try (var infra = new Infra("/")) {
// 200
infra.expect(200, "GET /foo");
infra.expect(200, "GET /foo/");
infra.expect(200, "GET /foo/bar");
infra.expect(200, "GET /foobar");
infra.expect(200, "DOH /foo");
infra.expect(200, "DOH /foo/");
infra.expect(200, "DOH /foo/bar");
infra.expect(200, "DOH /foobar");
// 404
infra.expect(404, "GET foo");
infra.expect(404, "GET *");
infra.expect(404, "GET ");
infra.expect(404, "DOH foo");
infra.expect(404, "DOH *");
infra.expect(404, "DOH ");
// 400
infra.expect(400, "GET");
infra.expect(400, "DOH");
}
}
@Test
void testContextPathAtSubDir() throws Exception {
// Repeating all cases with both known (GET) and unknown (DOH) request methods to stress both paths
try (var infra = new Infra("/foo")) {
// 200
infra.expect(200, "GET /foo");
infra.expect(200, "GET /foo/");
infra.expect(200, "GET /foo/bar");
infra.expect(200, "DOH /foo");
infra.expect(200, "DOH /foo/");
infra.expect(200, "DOH /foo/bar");
// 404
infra.expect(404, "GET /foobar"); // Differs from string prefix matching!
infra.expect(404, "GET foo");
infra.expect(404, "GET *");
infra.expect(404, "GET ");
infra.expect(404, "DOH /foobar"); // Differs from string prefix matching!
infra.expect(404, "DOH foo");
infra.expect(404, "DOH *");
infra.expect(404, "DOH ");
// 400
infra.expect(400, "GET");
infra.expect(400, "DOH");
}
}
@Test
void testContextPathAtSubDirWithTrailingSlash() throws Exception {
// Repeating all cases with both known (GET) and unknown (DOH) request methods to stress both paths
try (var infra = new Infra("/foo/")) {
// 200
infra.expect(200, "GET /foo/");
infra.expect(200, "GET /foo/bar");
infra.expect(200, "DOH /foo/");
infra.expect(200, "DOH /foo/bar");
// 404
infra.expect(404, "GET /foo");
infra.expect(404, "GET /foobar");
infra.expect(404, "GET foo");
infra.expect(404, "GET *");
infra.expect(404, "GET ");
infra.expect(404, "DOH /foo");
infra.expect(404, "DOH /foobar");
infra.expect(404, "DOH foo");
infra.expect(404, "DOH *");
infra.expect(404, "DOH ");
// 400
infra.expect(400, "GET");
infra.expect(400, "DOH");
}
}
protected static final class Infra implements AutoCloseable {
/** Charset used for network and file I/O. */
private static final Charset CHARSET = StandardCharsets.US_ASCII;
/** Socket address the server will bind to. */
private static final InetSocketAddress LO_SA_0 =
new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
private static final HttpHandler HANDLER = new EchoHandler();
private final HttpServer server;
private final String contextPath;
protected Infra(String contextPath) throws IOException {
this.server = HttpServer.create(LO_SA_0, 10);
server.createContext(contextPath, HANDLER);
server.start();
this.contextPath = contextPath;
}
protected void expect(int statusCode, String requestLinePrefix) {
try {
expect0(statusCode, requestLinePrefix);
} catch (Throwable exception) {
var extendedMessage = exception.getMessage() + " " + Map.of(
"contextPath", contextPath,
"requestLinePrefix", requestLinePrefix);
var extendedException = new RuntimeException(extendedMessage);
extendedException.setStackTrace(exception.getStackTrace());
throw extendedException;
}
}
private void expect0(int statusCode, String requestLinePrefix) throws IOException {
// Connect to the server
try (Socket socket = new Socket()) {
socket.connect(server.getAddress());
// Obtain the I/O streams
try (OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
BufferedReader inputReader = new BufferedReader(new InputStreamReader(inputStream, CHARSET))) {
// Write the request
byte[] requestBytes = String
// `Connection: close` is required for `BufferedReader::readLine` to work.
.format("%s HTTP/1.1\r\nConnection: close\r\n\r\n", requestLinePrefix)
.getBytes(CHARSET);
outputStream.write(requestBytes);
outputStream.flush();
// Read the response status code
String statusLine = inputReader.readLine();
assertNotNull(statusLine, "Unexpected EOF while reading status line");
Matcher statusLineMatcher = Pattern.compile("^HTTP/1\\.1 (\\d+) .+$").matcher(statusLine);
assertTrue(statusLineMatcher.matches(), "Couldn't match status line: \"" + statusLine + "\"");
assertEquals(statusCode, Integer.parseInt(statusLineMatcher.group(1)));
}
}
}
@Override
public void close() {
server.stop(0);
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2026, 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.
*/
import org.junit.jupiter.api.Test;
/*
* @test
* @bug 8272758
* @summary Verifies string prefix matching configured using a system property
* @build ContextPathMatcherPathPrefixTest
* EchoHandler
* @run junit/othervm
* -Dsun.net.httpserver.pathMatcher=stringPrefix
* ${test.main.class}
*/
class ContextPathMatcherStringPrefixTest extends ContextPathMatcherPathPrefixTest {
@Test
@Override
void testContextPathAtSubDir() throws Exception {
// Repeating all cases with both known (GET) and unknown (DOH) request methods to stress both paths
try (var infra = new Infra("/foo")) {
// 200
infra.expect(200, "GET /foo");
infra.expect(200, "GET /foo/");
infra.expect(200, "GET /foo/bar");
infra.expect(200, "GET /foobar"); // Differs from path prefix matching!
infra.expect(200, "DOH /foo");
infra.expect(200, "DOH /foo/");
infra.expect(200, "DOH /foo/bar");
infra.expect(200, "DOH /foobar"); // Differs from path prefix matching!
// 404
infra.expect(404, "GET /");
infra.expect(404, "GET foo");
infra.expect(404, "GET *");
infra.expect(404, "GET ");
infra.expect(404, "DOH /");
infra.expect(404, "DOH foo");
infra.expect(404, "DOH *");
infra.expect(404, "DOH ");
// 400
infra.expect(400, "GET");
infra.expect(400, "DOH");
}
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020, 2025, 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.
*/
/*
* @test
* @bug 8251496
* @summary summary
* @run junit/othervm CreateHttpServerTest
*/
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class CreateHttpServerTest {
@Test
public void TestCreate() throws IOException {
var server = HttpServer.create();
assertTrue(server instanceof HttpServer);
}
@Test
public void TestCreateWithParameters() throws IOException {
var addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
var server = HttpServer.create(addr, 123);
assertTrue(server instanceof HttpServer);
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2020, 2025, 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.
*/
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdk.test.lib.net.URIBuilder;
import static java.net.http.HttpResponse.BodyHandlers.ofString;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @test
* @bug 8245307
* @summary Test for DateFormatter in ExchangeImpl
* @modules java.net.http
* @library /test/lib
* @build DateFormatterTest
* @run junit/othervm DateFormatterTest
*/
public class DateFormatterTest {
private static HttpServer server;
static URI httpURI;
static final Integer ITERATIONS = 10;
static String format;
static Pattern pattern;
@BeforeAll
public static void setUp() throws IOException, URISyntaxException {
String days = "(Mon|Tue|Wed|Thu|Fri|Sat|Sun)(,)";
String dayNo = "(\\s\\d\\d\\s)";
String month = "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
String hour = "(\\s\\d\\d\\d\\d\\s\\d\\d)(:)(\\d\\d)(:)(\\d\\d\\s)";
String zone = "(GMT)";
format = days + dayNo + month + hour + zone;
pattern = Pattern.compile(format);
server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 10);
server.createContext("/server", new DateFormatHandler());
server.setExecutor(Executors.newCachedThreadPool());
httpURI = URIBuilder.newBuilder()
.host(server.getAddress().getAddress())
.port(server.getAddress().getPort())
.scheme("http")
.path("/server")
.build();
server.start();
}
@AfterAll
public static void cleanUp() {
server.stop(0);
}
@Test
public void testDateFormat() throws Exception {
HttpClient client = HttpClient.newBuilder()
.build();
HttpRequest request = HttpRequest.newBuilder(httpURI)
.GET()
.build();
for (int i = 0; i < ITERATIONS; i++) {
HttpResponse<String> response = client.send(request, ofString());
String date = response.headers().firstValue("Date").orElse("null");
if (date.equals("null"))
fail("Date not present");
Matcher matcher = pattern.matcher(date);
assertTrue(matcher.matches());
}
}
public static class DateFormatHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
try (InputStream is = exchange.getRequestBody();
OutputStream os = exchange.getResponseBody()) {
byte[] bytes = is.readAllBytes();
exchange.sendResponseHeaders(200, bytes.length);
os.write(bytes);
}
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2005, 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.
*/
import com.sun.net.httpserver.*;
import javax.net.ssl.*;
public class DummyVerifier implements HostnameVerifier {
public boolean verify (String s, SSLSession s1) {
return true;
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
/**
* Implements a basic static EchoHandler for an HTTP server
*/
public class EchoHandler implements HttpHandler {
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
String fixedrequest = map.getFirst ("XFixed");
// return the number of bytes received (no echo)
String summary = map.getFirst ("XSummary");
OutputStream os = t.getResponseBody();
byte[] in;
in = is.readAllBytes();
if (summary != null) {
in = Integer.toString(in.length).getBytes(StandardCharsets.UTF_8);
}
if (fixedrequest != null) {
t.sendResponseHeaders(200, in.length == 0 ? RSPBODY_EMPTY : in.length);
} else {
t.sendResponseHeaders(200, RSPBODY_CHUNKED);
}
os.write(in);
close(t, os);
close(t, is);
}
protected void close(OutputStream os) throws IOException {
os.close();
}
protected void close(InputStream is) throws IOException {
is.close();
}
protected void close(HttpExchange t, OutputStream os) throws IOException {
close(os);
}
protected void close(HttpExchange t, InputStream is) throws IOException {
close(is);
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2023, 2025, 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.
*/
/*
* @test
* @bug 8288109
* @summary Tests for HttpExchange set/getAttribute
* @library /test/lib
* @run junit/othervm ExchangeAttributeTest
* @run junit/othervm -Djdk.httpserver.attributes=context ExchangeAttributeTest
* @run junit/othervm -Djdk.httpserver.attributes=random-string ExchangeAttributeTest
* @run junit/othervm -Djdk.httpserver.attributes ExchangeAttributeTest
*/
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.junit.jupiter.api.Assertions.*;
public class ExchangeAttributeTest {
static final InetAddress LOOPBACK_ADDR = InetAddress.getLoopbackAddress();
static final boolean ENABLE_LOGGING = true;
static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
@BeforeAll
public static void setup() {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
logger.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
logger.addHandler(ch);
}
}
@Test
public void testExchangeAttributes() throws Exception {
var handler = new AttribHandler();
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10);
server.createContext("/", handler).getAttributes().put("attr", "context-val");
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
} finally {
server.stop(0);
}
}
// --- infra ---
static URI uri(HttpServer server, String path) throws URISyntaxException {
return URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path(path)
.build();
}
/**
* A test handler that discards the request and sends a response
*/
static class AttribHandler implements HttpHandler {
@java.lang.Override
public void handle(HttpExchange exchange) throws IOException {
try {
if ("context".equals(System.getProperty("jdk.httpserver.attributes"))) {
exchange.setAttribute("attr", "val");
assertEquals("val", exchange.getAttribute("attr"));
assertEquals("val", exchange.getHttpContext().getAttributes().get("attr"));
} else {
assertNull(exchange.getAttribute("attr"));
assertEquals("context-val", exchange.getHttpContext().getAttributes().get("attr"));
exchange.setAttribute("attr", "val");
assertEquals("val", exchange.getAttribute("attr"));
assertEquals("context-val", exchange.getHttpContext().getAttributes().get("attr"));
}
exchange.setAttribute("attr", null);
assertNull(exchange.getAttribute("attr"));
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
} catch (Throwable t) {
t.printStackTrace();
exchange.sendResponseHeaders(500, RSPBODY_EMPTY);
}
}
}
}

View file

@ -0,0 +1,306 @@
/*
* Copyright (c) 2026, 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.
*/
/**
* @test
* @bug 8377302
* @summary HttpServer.stop() blocks indefinitely if handler throws
* @modules jdk.httpserver java.logging
* @library /test/lib
* @run main/othervm ${test.main.class}
*/
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import jdk.test.lib.Utils;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
public class FailAndStopTest implements HttpHandler {
// Keep that logger in a static field to make sure it doesn't
// get GC'ed and recreated before the HttpServer is initialized.
private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
private static final String BODY = "OK";
static enum TestCases {
FAILNOW("failNow", TestCases::shouldAlwaysFail),
ASSERTNOW("assertNow", TestCases::shouldAlwaysFail),
RESPANDFAIL("failAfterResponseStatus", TestCases::shouldFailExceptForHead),
RESPANDASSERT("assertAfterResponseStatus", TestCases::shouldFailExceptForHead),
CLOSEAFTERRESP("closeExchangeAfterResponseStatus", TestCases::shouldFailExceptForHead),
BODYANDFAIL("failAfterBody", TestCases::shouldFailExceptForHeadOrHttps),
BODYANDASSERT("assertAfterBody", TestCases::shouldFailExceptForHeadOrHttps),
CLOSEBEFOREOS("closeExchangeBeforeOS", TestCases::shouldNeverFail),
CLOSEANDRETURN("closeAndReturn", TestCases::shouldNeverFail),
CLOSEANDFAIL("closeAndFail", TestCases::shouldNeverFail),
CLOSEANDASSERT("closeAndAssert", TestCases::shouldNeverFail);
private final String query;
private BiPredicate<String,String> shouldFail;
TestCases(String query, BiPredicate<String,String> shouldFail) {
this.query = query;
this.shouldFail = shouldFail;
}
boolean shouldFail(String scheme, String method) {
// in case of HEAD method the client should not
// fail if we throw after sending response headers
return shouldFail.test(scheme, method);
}
private static boolean shouldAlwaysFail(String scheme, String method) {
return true;
}
private static boolean shouldNeverFail(String scheme, String method) {
return false;
}
private static boolean shouldFailExceptForHead(String scheme, String method) {
return !"HEAD".equals(method);
}
private static boolean shouldFailExceptForHeadOrHttps(String scheme, String method) {
// When using https, the buffered response bytes may be sent
// when the connection is closed, in which case the full body
// will be correctly received, and the client connection
// will not fail. With plain http, the bytes are not sent and
// the client fails with premature end of file.
return !"HEAD".equals(method) && !"https".equalsIgnoreCase(scheme);
}
}
@Override
public void handle(HttpExchange ex) throws IOException {
String query = ex.getRequestURI().getRawQuery();
TestCases step = TestCases.FAILNOW;
if (query == null || query.equals(step.query)) {
System.out.println("Server: " + step);
throw new NullPointerException("Got you!");
}
step = TestCases.ASSERTNOW;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
throw new AssertionError("Got you!");
}
byte[] body = BODY.getBytes(StandardCharsets.UTF_8);
ex.sendResponseHeaders(200, body.length);
step = TestCases.RESPANDFAIL;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
throw new NullPointerException("Got you!");
}
step = TestCases.RESPANDASSERT;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
throw new AssertionError("Got you!");
}
step = TestCases.CLOSEAFTERRESP;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
ex.close();
return;
}
if (!"HEAD".equals(ex.getRequestMethod())) {
ex.getResponseBody().write(body);
}
step = TestCases.BODYANDFAIL;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
throw new NullPointerException("Got you!");
}
step = TestCases.BODYANDASSERT;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
throw new AssertionError("Got you!");
}
step = TestCases.CLOSEBEFOREOS;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
ex.close();
return;
}
System.out.println("Server: closing response body");
ex.getResponseBody().close();
step = TestCases.CLOSEANDRETURN;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
ex.close();
return;
}
step = TestCases.CLOSEANDFAIL;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
throw new NullPointerException("Got you!");
}
step = TestCases.CLOSEANDASSERT;
if (query.equals(step.query)) {
System.out.println("Server: " + step);
throw new AssertionError("Got you!");
}
}
private static void enableHttpServerLogging() {
// set HttpServer's logger to ALL
LOGGER.setLevel(Level.ALL);
// get the root logger, get its first handler (by default
// it's a ConsoleHandler), and set its level to ALL (by
// default its level is INFO).
Logger.getLogger("").getHandlers()[0].setLevel(Level.ALL);
}
public static void main(String[] args) throws Exception {
enableHttpServerLogging();
// test with GET
for (var test : TestCases.values()) {
test(test, Optional.empty(), "http");
test(test, Optional.empty(), "https");
}
// test with HEAD
for (var test : TestCases.values()) {
test(test, Optional.of("HEAD"), "http");
test(test, Optional.of("HEAD"), "https");
}
}
private static SSLContext initSSLContext(boolean secure) {
SSLContext context = secure ? SimpleSSLContext.findSSLContext() : null;
if (secure) {
SSLContext.setDefault(context);
}
return context;
}
private static HttpServer createHttpServer(SSLContext context) throws IOException {
var address = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
if (context != null) {
var server = HttpsServer.create(address, 0);
server.setHttpsConfigurator(new HttpsConfigurator(context));
return server;
} else {
return HttpServer.create(address, 0);
}
}
private static HttpURLConnection createConnection(URL url, boolean secure)
throws IOException {
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
if (secure) {
((HttpsURLConnection)urlc).setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
return urlc;
}
private static void test(TestCases test, Optional<String> method, String scheme)
throws Exception {
boolean secure = "https".equalsIgnoreCase(scheme);
SSLContext context = initSSLContext(secure);
HttpServer server = createHttpServer(context);
System.out.println("Test: " + method.orElse("GET") + " " + test.query);
System.out.println("Server listening at: " + server.getAddress());
try {
server.createContext("/FailAndStopTest/", new FailAndStopTest());
server.start();
URL url = URIBuilder.newBuilder()
.scheme(scheme)
.loopback()
.port(server.getAddress().getPort())
.path("/FailAndStopTest/")
.query(test.query)
.toURLUnchecked();
System.out.println("Connecting to: " + url);
HttpURLConnection urlc = createConnection(url, secure);
if (method.isPresent()) urlc.setRequestMethod(method.get());
try {
System.out.println("Client: Response code received: " + urlc.getResponseCode());
InputStream is = urlc.getInputStream();
String body = new String(is.readAllBytes(), StandardCharsets.UTF_8);
is.close();
System.out.printf("Client: read body: \"%s\"%n", body);
if (test.shouldFail(scheme, urlc.getRequestMethod())) {
throw new AssertionError(test.query + ": test did not fail");
}
if (!method.orElse("GET").equals("HEAD")) {
if (!BODY.equals(body)) {
throw new AssertionError(
String.format("\"%s\" != \"%s\"", body, BODY));
}
} else if (!body.isEmpty()) {
throw new AssertionError("Body is not empty: " + body);
}
} catch (IOException so) {
if (test.shouldFail(scheme, urlc.getRequestMethod())) {
// expected
System.out.println(test.query + ": Got expected exception: " + so);
} else if (!test.shouldFail("http", urlc.getRequestMethod())) {
// When using https, the buffered response bytes may be sent
// when the connection is closed, in which case the full body
// will be correctly received, and the client connection
// will not fail. With plain http, the bytes are not sent and
// the client fails with premature end of file.
// So only fail here if the test should not fail with plain
// http - we want to accept possible exception for https...
throw new AssertionError(
String.format("%s: test failed with %s", test.query, so), so);
} else {
System.out.printf("%s: WARNING: unexpected exception: %s%n", test.query, so);
// should only happen in those two cases:
assert secure && !"HEAD".equals(method) &&
(test == TestCases.BODYANDFAIL || test == TestCases.BODYANDASSERT);
}
}
} finally {
// if not fixed will cause the test to fail in jtreg timeout
server.stop((int)Utils.adjustTimeout(5000));
System.out.println("Server stopped as expected");
}
}
}

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
import java.io.*;
import java.net.*;
import com.sun.net.httpserver.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
/**
* Implements a basic static content HTTP file server handler
* which understands text/html, text/plain content types
*
* Must be given an abs pathname to the document root.
* Directory listings together with text + html files
* can be served.
*
*/
public class FileServerHandler implements HttpHandler {
String docroot;
public FileServerHandler (String docroot) {
this.docroot = docroot;
}
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
URI uri = t.getRequestURI();
String path = uri.getPath();
int x = 0;
while (is.read () != -1) x++;
is.close();
File f = new File (docroot, path);
if (!f.exists()) {
notfound (t, path);
return;
}
String fixedrequest = map.getFirst ("XFixed");
String method = t.getRequestMethod();
if (method.equals ("HEAD")) {
rmap.set ("Content-Length", Long.toString (f.length()));
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
} else if (!method.equals("GET")) {
t.sendResponseHeaders (405, RSPBODY_EMPTY);
t.close();
return;
}
if (path.endsWith (".html") || path.endsWith (".htm")) {
rmap.set ("Content-Type", "text/html");
} else {
rmap.set ("Content-Type", "text/plain");
}
if (f.isDirectory()) {
if (!path.endsWith ("/")) {
moved (t);
return;
}
rmap.set ("Content-Type", "text/html");
t.sendResponseHeaders (200, RSPBODY_CHUNKED);
String[] list = f.list();
try (final OutputStream os = t.getResponseBody();
final PrintStream p = new PrintStream (os)) {
p.println("<h2>Directory listing for: " + path + "</h2>");
p.println("<ul>");
for (int i = 0; i < list.length; i++) {
p.println("<li><a href=\"" + list[i] + "\">" + list[i] + "</a></li>");
}
p.println("</ul><p><hr>");
p.flush();
}
} else {
int clen;
if (fixedrequest != null) {
clen = (int) f.length();
} else {
clen = 0;
}
t.sendResponseHeaders (200, clen);
int count = 0;
try (final OutputStream os = t.getResponseBody();
final FileInputStream fis = new FileInputStream (f)) {
byte[] buf = new byte [16 * 1024];
int len;
while ((len=fis.read (buf)) != -1) {
os.write (buf, 0, len);
count += len;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
void moved (HttpExchange t) throws IOException {
Headers req = t.getRequestHeaders();
Headers map = t.getResponseHeaders();
URI uri = t.getRequestURI();
String host = req.getFirst ("Host");
String location = "http://"+host+uri.getPath() + "/";
map.set ("Content-Type", "text/html");
map.set ("Location", location);
t.sendResponseHeaders (301, RSPBODY_EMPTY);
t.close();
}
void notfound (HttpExchange t, String p) throws IOException {
t.getResponseHeaders().set ("Content-Type", "text/html");
t.sendResponseHeaders (404, RSPBODY_CHUNKED);
OutputStream os = t.getResponseBody();
String s = "<h2>File not found</h2>";
s = s + p + "<p>";
os.write (s.getBytes());
os.close();
t.close();
}
}

View file

@ -0,0 +1,439 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @bug 8267262
* @summary Tests for Filter static factory methods
* @run junit/othervm FilterTest
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class FilterTest {
static final Class<NullPointerException> NPE = NullPointerException.class;
static final Class<IOException> IOE = IOException.class;
static final InetAddress LOOPBACK_ADDR = InetAddress.getLoopbackAddress();
static final boolean ENABLE_LOGGING = true;
static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
@BeforeAll
public static void setup() {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
logger.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
logger.addHandler(ch);
}
}
@Test
public void testNull() {
assertThrows(NPE, () -> Filter.beforeHandler(null, e -> e.getResponseHeaders().set("X-Foo", "Bar")));
assertThrows(NPE, () -> Filter.beforeHandler("Some description", null));
assertThrows(NPE, () -> Filter.afterHandler("Some description", null));
assertThrows(NPE, () -> Filter.afterHandler(null, HttpExchange::getResponseCode));
assertThrows(NPE, () -> Filter.adaptRequest("Some description", null));
assertThrows(NPE, () -> Filter.adaptRequest(null, r -> r.with("Foo", List.of("Bar"))));
}
@Test
public void testDescription() {
var desc = "Some description";
var beforeFilter = Filter.beforeHandler(desc, HttpExchange::getRequestBody);
assertEquals(beforeFilter.description(), desc);
var afterFilter = Filter.afterHandler(desc, HttpExchange::getResponseCode);
assertEquals(afterFilter.description(), desc);
var adaptFilter = Filter.adaptRequest(desc, r -> r.with("Foo", List.of("Bar")));
assertEquals(adaptFilter.description(), desc);
}
public static Object[][] throwingFilters() {
return new Object[][] {
{Filter.beforeHandler("before RE", e -> { throw new RuntimeException(); }), IOE},
{Filter.beforeHandler("before AE", e -> { throw new AssertionError(); }), IOE},
{Filter.afterHandler( "after RE", e -> { throw new RuntimeException(); }), null},
{Filter.afterHandler( "after AE", e -> { throw new AssertionError(); }), null},
};
}
@ParameterizedTest
@MethodSource("throwingFilters")
public void testException(Filter filter, Class<Exception> exception)
throws Exception
{
var handler = new EchoHandler();
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10);
server.createContext("/", handler).getFilters().add(filter);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
if (exception != null) {
assertThrows(exception, () -> client.send(request, HttpResponse.BodyHandlers.ofString()));
} else {
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("hello world", response.body());
}
} finally {
server.stop(0);
}
}
@Test
public void testBeforeHandler() throws Exception {
var handler = new EchoHandler();
var filter = Filter.beforeHandler("Add x-foo response header",
e -> e.getResponseHeaders().set("x-foo", "bar"));
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10);
server.createContext("/", handler).getFilters().add(filter);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(3, response.headers().map().size());
assertEquals("bar", response.headers().firstValue("x-foo").orElseThrow());
} finally {
server.stop(0);
}
}
@Test
public void testBeforeHandlerRepeated() throws Exception {
var handler = new EchoHandler();
var filter1 = Filter.beforeHandler("Add x-foo response header",
e -> e.getResponseHeaders().set("x-foo", "bar"));
var filter2 = Filter.beforeHandler("Update x-foo response header",
e -> e.getResponseHeaders().set("x-foo", "barbar"));
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 10);
var context = server.createContext("/", handler);
context.getFilters().add(filter1);
context.getFilters().add(filter2);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(3, response.headers().map().size());
assertEquals("barbar", response.headers().firstValue("x-foo").orElseThrow());
} finally {
server.stop(0);
}
}
@Test
public void testBeforeHandlerSendResponse() throws Exception {
var handler = new NoResponseHandler();
var filter = Filter.beforeHandler("Add x-foo response header and send response",
e -> {
try (InputStream is = e.getRequestBody();
OutputStream os = e.getResponseBody()) {
is.readAllBytes();
e.getResponseHeaders().set("x-foo", "bar");
var resp = "hello world".getBytes(StandardCharsets.UTF_8);
e.sendResponseHeaders(200, resp.length);
os.write(resp);
} catch (IOException ioe) {
ioe.printStackTrace(System.out);
throw new UncheckedIOException(ioe);
}
});
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 10);
server.createContext("/", handler).getFilters().add(filter);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(3, response.headers().map().size());
assertEquals("bar", response.headers().firstValue("x-foo").orElseThrow());
} finally {
server.stop(0);
}
}
@Test
public void testAfterHandler() throws Exception {
var handler = new EchoHandler();
var respCode = new CompletableFuture<Integer>();
var filter = Filter.afterHandler("Log response code",
e -> respCode.complete(e.getResponseCode()));
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 10);
server.createContext("/", handler).getFilters().add(filter);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals((int)respCode.get(), response.statusCode());
} finally {
server.stop(0);
}
}
@Test
public void testAfterHandlerRepeated() throws Exception {
var handler = new EchoHandler();
var attr = new CompletableFuture<String>();
final var value = "some value";
var filter1 = Filter.afterHandler("Set attribute",
e -> e.setAttribute("test-attr", value));
var filter2 = Filter.afterHandler("Read attribute",
e -> attr.complete((String) e.getAttribute("test-attr")));
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 10);
var context = server.createContext("/", handler);
context.getFilters().add(filter2);
context.getFilters().add(filter1);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(value, attr.get());
} finally {
server.stop(0);
}
}
@Test
public void testAfterHandlerSendResponse() throws Exception {
var handler = new NoResponseHandler();
var respCode = new CompletableFuture<Integer>();
var filter = Filter.afterHandler("Log response code and send response",
e -> {
try (InputStream is = e.getRequestBody();
OutputStream os = e.getResponseBody()) {
is.readAllBytes();
var resp = "hello world".getBytes(StandardCharsets.UTF_8);
e.sendResponseHeaders(200, resp.length);
os.write(resp);
respCode.complete(e.getResponseCode());
} catch (IOException ioe) {
ioe.printStackTrace(System.out);
throw new UncheckedIOException(ioe);
}
});
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 10);
server.createContext("/", handler).getFilters().add(filter);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals((int)respCode.get(), response.statusCode());
} finally {
server.stop(0);
}
}
@Test
public void testBeforeAndAfterHandler() throws Exception {
var handler = new EchoHandler();
var respCode = new CompletableFuture<Integer>();
var beforeFilter = Filter.beforeHandler("Add x-foo response header",
e -> e.getResponseHeaders().set("x-foo", "bar"));
var afterFilter = Filter.afterHandler("Log response code",
e -> respCode.complete(e.getResponseCode()));
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR, 0), 10);
var context = server.createContext("/", handler);
context.getFilters().add(beforeFilter);
context.getFilters().add(afterFilter);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(3, response.headers().map().size());
assertEquals("bar", response.headers().firstValue("x-foo").orElseThrow());
assertEquals((int)respCode.get(), response.statusCode());
} finally {
server.stop(0);
}
}
@Test
public void testInspectRequest() throws Exception {
var handler = new EchoHandler();
var inspectedURI = new AtomicReference<URI>();
var filter = Filter.adaptRequest("Inspect request URI",
r -> {inspectedURI.set(r.getRequestURI()); return r;});
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10);
server.createContext("/", handler).getFilters().add(filter);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "foo/bar")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(URI.create("/foo/bar"), inspectedURI.get());
} finally {
server.stop(0);
}
}
private static HttpExchange originalExchange;
/**
* Confirms that adaptRequest changes only the expected request state and
* all other exchange state remains unchanged.
*/
@Test
public void testAdaptRequest() throws Exception {
var handler = new CompareStateAndEchoHandler();
var captureFilter = Filter.beforeHandler("capture exchange", e -> {
e.setAttribute("foo", "bar");
originalExchange = e;
});
var adaptFilter = Filter.adaptRequest("Add x-foo request header", r -> {
// Confirm request state is unchanged
assertEquals(originalExchange.getRequestHeaders(), r.getRequestHeaders());
assertEquals(originalExchange.getRequestURI(), r.getRequestURI());
assertEquals(originalExchange.getRequestMethod(), r.getRequestMethod());
return r.with("x-foo", List.of("bar"));
});
var server = HttpServer.create(new InetSocketAddress(LOOPBACK_ADDR,0), 10);
var context = server.createContext("/", handler);
context.getFilters().add(captureFilter);
context.getFilters().add(adaptFilter);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("bar", response.body());
} finally {
server.stop(0);
}
}
// --- infra ---
static URI uri(HttpServer server, String path) {
return URI.create("http://localhost:%s/%s".formatted(server.getAddress().getPort(), path));
}
/**
* A test handler that discards the request and sends a response
*/
static class EchoHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
try (InputStream is = exchange.getRequestBody();
OutputStream os = exchange.getResponseBody()) {
is.readAllBytes();
var resp = "hello world".getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, resp.length);
os.write(resp);
}
}
}
/**
* A handler that compares the adapted exchange with the original exchange,
* before discarding the request and returning the test request header value.
*/
static class CompareStateAndEchoHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
assertEquals(originalExchange.getLocalAddress(), exchange.getLocalAddress());
assertEquals(originalExchange.getRemoteAddress(), exchange.getRemoteAddress());
assertEquals(originalExchange.getProtocol(), exchange.getProtocol());
assertEquals(originalExchange.getPrincipal(), exchange.getPrincipal());
assertEquals(originalExchange.getHttpContext(), exchange.getHttpContext());
assertEquals(originalExchange.getRequestMethod(), exchange.getRequestMethod());
assertEquals(originalExchange.getRequestURI(), exchange.getRequestURI());
assertEquals(originalExchange.getRequestBody(), exchange.getRequestBody());
assertEquals(originalExchange.getResponseHeaders(), exchange.getResponseHeaders());
assertEquals(originalExchange.getResponseCode(), exchange.getResponseCode());
assertEquals(originalExchange.getResponseBody(), exchange.getResponseBody());
assertEquals(originalExchange.getAttribute("foo"), exchange.getAttribute("foo"));
assertFalse(exchange.getRequestHeaders().equals(originalExchange.getRequestHeaders()));
exchange.setAttribute("foo", "barbar");
assertEquals(originalExchange.getAttribute("foo"), exchange.getAttribute("foo"));
try (InputStream is = exchange.getRequestBody();
OutputStream os = exchange.getResponseBody()) {
is.readAllBytes();
var resp = exchange.getRequestHeaders().get("x-foo")
.get(0)
.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, resp.length);
os.write(resp);
}
}
}
/**
* A test handler that does nothing
*/
static class NoResponseHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException { }
}
}

View file

@ -0,0 +1,536 @@
/*
* Copyright (c) 2020, 2025, 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.
*/
/*
* @test
* @bug 8251496 8268960
* @summary Tests for methods in Headers class
* @modules jdk.httpserver/com.sun.net.httpserver:+open
* jdk.httpserver/sun.net.httpserver:+open
* @library /test/lib
* @build jdk.test.lib.net.URIBuilder
* @run junit/othervm HeadersTest
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import sun.net.httpserver.UnmodifiableHeaders;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class HeadersTest {
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
static final Class<IOException> IOE = IOException.class;
static final Class<NullPointerException> NPE = NullPointerException.class;
@Test
public void testDefaultConstructor() {
var headers = new Headers();
assertTrue(headers.isEmpty());
}
@Test
public void testNull() {
final Headers h = new Headers();
h.put("Foo", List.of("Bar"));
final var mapNullKey = new HashMap<String, List<String>>();
mapNullKey.put(null, List.of("Bar"));
final var mapNullList = new HashMap<String, List<String>>();
mapNullList.put("Foo", null);
final var listWithNull = new LinkedList<String>();
listWithNull.add(null);
final var mapNullInList = new HashMap<String, List<String>>();
mapNullInList.put("Foo", listWithNull);
assertThrows(NPE, () -> h.add(null, "Bar"));
assertThrows(NPE, () -> h.add("Foo", null));
assertThrows(NPE, () -> h.compute(null, (k, v) -> List.of("Bar")));
assertThrows(NPE, () -> h.compute("Foo", (k, v) -> listWithNull));
assertThrows(NPE, () -> h.computeIfAbsent(null, (k) -> List.of("Bar")));
assertThrows(NPE, () -> h.computeIfAbsent("Foo-foo", (k) -> listWithNull));
assertThrows(NPE, () -> h.computeIfPresent(null, (k, v) -> List.of("Bar")));
assertThrows(NPE, () -> h.computeIfPresent("Foo", (k, v) -> listWithNull));
assertThrows(NPE, () -> h.containsKey(null));
assertThrows(NPE, () -> h.containsValue(null));
assertThrows(NPE, () -> h.get(null));
assertThrows(NPE, () -> h.getFirst(null));
assertThrows(NPE, () -> h.getOrDefault(null, List.of("Bar")));
assertThrows(NPE, () -> h.merge(null, List.of("Bar"), (k, v) -> List.of("Bar")));
assertThrows(NPE, () -> h.merge("Foo-foo", null, (k, v) -> List.of("Bar")));
assertThrows(NPE, () -> h.merge("Foo-foo", listWithNull, (k, v) -> List.of("Bar")));
assertThrows(NPE, () -> h.merge("Foo", List.of("Bar"), (k, v) -> listWithNull));
assertThrows(NPE, () -> h.put(null, List.of("Bar")));
assertThrows(NPE, () -> h.put("Foo", null));
assertThrows(NPE, () -> h.put("Foo", listWithNull));
assertThrows(NPE, () -> h.putAll(mapNullKey));
assertThrows(NPE, () -> h.putAll(mapNullList));
assertThrows(NPE, () -> h.putAll(mapNullInList));
assertThrows(NPE, () -> h.putIfAbsent(null, List.of("Bar")));
assertThrows(NPE, () -> h.putIfAbsent("Foo-foo", null));
assertThrows(NPE, () -> h.putIfAbsent("Foo-foo", listWithNull));
assertThrows(NPE, () -> h.remove(null));
assertThrows(NPE, () -> h.remove(null, List.of("Bar")));
assertThrows(NPE, () -> h.replace(null, List.of("Bar")));
assertThrows(NPE, () -> h.replace("Foo", null));
assertThrows(NPE, () -> h.replace("Foo", listWithNull));
assertThrows(NPE, () -> h.replace(null, List.of("Bar"), List.of("Bar")));
assertThrows(NPE, () -> h.replace("Foo", List.of("Bar"), null));
assertThrows(NPE, () -> h.replace("Foo", List.of("Bar"), listWithNull));
assertThrows(NPE, () -> h.replaceAll((k, v) -> listWithNull));
assertThrows(NPE, () -> h.replaceAll((k, v) -> null));
assertThrows(NPE, () -> h.set(null, "Bar"));
assertThrows(NPE, () -> h.set("Foo", null));
}
public static Object[][] responseHeaders() {
final var listWithNull = new LinkedList<String>();
listWithNull.add(null);
return new Object[][] {
{null, List.of("Bar")},
{"Foo", null},
{"Foo", listWithNull}
};
}
/**
* Confirms HttpExchange::sendResponseHeaders throws NPE if response headers
* contain a null key or value.
*/
@ParameterizedTest
@MethodSource("responseHeaders")
public void testNullResponseHeaders(String headerKey, List<String> headerVal)
throws Exception {
var handler = new Handler(headerKey, headerVal);
var server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
server.createContext("/", handler);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
assertThrows(IOE, () -> client.send(request, HttpResponse.BodyHandlers.ofString()));
assertEquals(NPE, throwable.get().getClass());
assertTrue(Arrays.stream(throwable.get().getStackTrace())
.anyMatch(e -> e.getClassName().equals("sun.net.httpserver.HttpExchangeImpl")
|| e.getMethodName().equals("sendResponseHeaders")));
} finally {
server.stop(0);
}
}
private static CompletableFuture<Throwable> throwable = new CompletableFuture<>();
private record Handler(String headerKey, List<String> headerVal) implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
try (InputStream is = exchange.getRequestBody();
OutputStream os = exchange.getResponseBody()) {
is.readAllBytes();
var resp = "hello world".getBytes(StandardCharsets.UTF_8);
putHeaders(exchange.getResponseHeaders(), headerKey, headerVal);
try {
exchange.sendResponseHeaders(200, resp.length);
} catch (Throwable t) { // expect NPE
throwable.complete(t);
throw t;
}
os.write(resp);
}
}
}
private static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.buildUnchecked();
}
/**
* Sets headers reflectively to be able to set a null key or value.
*/
private static void putHeaders(Headers headers,
String headerKey,
List<String> headerVal) {
try {
final var map = new HashMap<String, List<String>>();
map.put(headerKey, headerVal);
var mapField = Headers.class.getDeclaredField("map");
mapField.setAccessible(true);
mapField.set(headers, map);
} catch (Exception e) {
throw new RuntimeException("Could not set headers reflectively", e);
}
}
public static Object[][] headerPairs() {
final var h1 = new Headers();
final var h2 = new Headers();
final var h3 = new Headers();
final var h4 = new Headers();
final var h5 = new Headers();
h1.put("Accept-Encoding", List.of("gzip, deflate"));
h2.put("accept-encoding", List.of("gzip, deflate"));
h3.put("AccePT-ENCoding", List.of("gzip, deflate"));
h4.put("ACCept-EncodING", List.of("gzip, deflate"));
h5.put("ACCEPT-ENCODING", List.of("gzip, deflate"));
final var headers = List.of(h1, h2, h3, h4, h5);
return headers.stream() // cartesian product of headers
.flatMap(header1 -> headers.stream().map(header2 -> new Headers[] { header1, header2 }))
.toArray(Object[][]::new);
}
@ParameterizedTest
@MethodSource("headerPairs")
public void testEqualsAndHashCode(Headers h1, Headers h2) {
assertTrue(h1.equals(h2), "Headers differ");
assertEquals(h2.hashCode(), h1.hashCode(), "hashCode differ for "
+ List.of(h1, h2));
}
@Test
public void testEqualsMap() {
final var h = new Headers();
final var m = new HashMap<String, List<String>>();
assertTrue(h.equals(m));
assertTrue(m.equals(h));
assertFalse(h.equals(null), "null cannot be equal to Headers");
}
@Test
public void testToString() {
final var h = new Headers();
h.put("Accept-Encoding", List.of("gzip, deflate"));
assertTrue(h.toString().equals("Headers { {Accept-encoding=[gzip, deflate]} }"));
}
@Test
public void testPutAll() {
final var h0 = new Headers();
final var map = new HashMap<String, List<String>>();
map.put("a", null);
assertThrows(NPE, () -> h0.putAll(map));
final var list = new ArrayList<String>();
list.add(null);
assertThrows(NPE, () -> h0.putAll(Map.of("a", list)));
assertThrows(IAE, () -> h0.putAll(Map.of("a", List.of("\r"))));
assertThrows(IAE, () -> h0.putAll(Map.of("a", List.of("\n"))));
assertThrows(IAE, () -> h0.putAll(Map.of("a", List.of("a\r"))));
assertThrows(IAE, () -> h0.putAll(Map.of("a", List.of("a\n"))));
assertThrows(IAE, () -> h0.putAll(Map.of("\r", List.of("a"))));
assertThrows(IAE, () -> h0.putAll(Map.of("\n", List.of("a"))));
assertThrows(IAE, () -> h0.putAll(Map.of("a\r", List.of("a"))));
assertThrows(IAE, () -> h0.putAll(Map.of("a\n", List.of("a"))));
final var h1 = new Headers();
h1.put("a", List.of("1"));
h1.put("b", List.of("2"));
final var h2 = new Headers();
h2.putAll(Map.of("a", List.of("1"), "b", List.of("2")));
assertTrue(h1.equals(h2));
}
@Test
public void testReplaceAll() {
final var h1 = new Headers();
h1.put("a", List.of("1"));
h1.put("b", List.of("2"));
final var list = new ArrayList<String>();
list.add(null);
assertThrows(NPE, () -> h1.replaceAll((k, v) -> list));
assertThrows(IAE, () -> h1.replaceAll((k, v) -> List.of("\n")));
h1.replaceAll((k, v) -> {
String s = h1.get(k).get(0);
return List.of(s+s);
});
final var h2 = new Headers();
h2.put("a", List.of("11"));
h2.put("b", List.of("22"));
assertTrue(h1.equals(h2));
}
@Test
public void test1ArgConstructorNull() {
assertThrows(NPE, () -> new Headers(null));
{
final var m = new HashMap<String, List<String>>();
m.put(null, List.of("Bar"));
assertThrows(NPE, () -> new Headers(m));
}
{
final var m = new HashMap<String, List<String>>();
m.put("Foo", null);
assertThrows(NPE, () -> new Headers(m));
}
{
final var m = new HashMap<String, List<String>>();
final var list = new LinkedList<String>();
list.add(null);
m.put("Foo", list);
assertThrows(NPE, () -> new Headers(m));
}
}
@Test
public void test1ArgConstructor() {
{
var h = new Headers(new Headers());
assertTrue(h.isEmpty());
}
{
var h = new Headers(Map.of("Foo", List.of("Bar")));
assertEquals(List.of("Bar"), h.get("Foo"));
assertEquals(1, h.size());
}
{
var h1 = new Headers(new UnmodifiableHeaders(new Headers()));
assertTrue(h1.isEmpty());
h1.put("Foo", List.of("Bar")); // modifiable
assertEquals(List.of("Bar"), h1.get("Foo"));
assertEquals(1, h1.size());
var h2 = new Headers(h1);
assertEquals(List.of("Bar"), h2.get("Foo"));
assertEquals(1, h2.size());
assertEquals(h2, h1);
h1.set("Foo", "Barbar");
assertNotEquals(h2, h1);
}
}
@Test
public void testMutableHeaders() {
{
var h = new Headers();
h.add("Foo", "Bar");
h.add("Foo", "Bazz");
h.get("Foo").remove(0);
h.remove("Foo");
h.clear();
}
{
var h = new Headers(Map.of("Foo", List.of("Bar")));
h.get("Foo").add("Bazz");
h.get("Foo").remove(0);
h.remove("Foo");
h.clear();
}
}
@Test
public void testOfNull() {
assertThrows(NPE, () -> Headers.of((String[])null));
assertThrows(NPE, () -> Headers.of(null, "Bar"));
assertThrows(NPE, () -> Headers.of("Foo", null));
assertThrows(NPE, () -> Headers.of((Map<String, List<String>>) null));
{
final var m = new HashMap<String, List<String>>();
m.put(null, List.of("Bar"));
assertThrows(NPE, () -> Headers.of(m));
}
{
final var m = new HashMap<String, List<String>>();
m.put("Foo", null);
assertThrows(NPE, () -> Headers.of(m));
}
{
final var m = new HashMap<String, List<String>>();
final var list = new LinkedList<String>();
list.add(null);
m.put("Foo", list);
assertThrows(NPE, () -> Headers.of(m));
}
}
@Test
public void testOf() {
final var h = Headers.of("a", "1", "b", "2");
assertEquals(2, h.size());
List.of("a", "b").forEach(n -> assertTrue(h.containsKey(n)));
List.of("1", "2").forEach(v -> assertTrue(h.containsValue(List.of(v))));
}
@Test
public void testOfEmpty() {
for (var h : List.of(Headers.of(), Headers.of(new String[] { }))) {
assertEquals(0, h.size());
assertTrue(h.isEmpty());
}
}
@Test
public void testOfNumberOfElements() {
assertThrows(IAE, () -> Headers.of("a"));
assertThrows(IAE, () -> Headers.of("a", "1", "b"));
}
@Test
public void testOfMultipleValues() {
final var h = Headers.of("a", "1", "b", "1", "b", "2", "b", "3");
assertEquals(2, h.size());
List.of("a", "b").forEach(n -> assertTrue(h.containsKey(n)));
List.of(List.of("1"), List.of("1", "2", "3")).forEach(v -> assertTrue(h.containsValue(v)));
}
@Test
public void testNormalizeOnNull() {
assertThrows(NullPointerException.class, () -> normalize(null));
}
public static Object[][] illegalKeys() {
var illegalChars = List.of('\r', '\n');
var illegalStrings = Stream
// Insert an illegal char at every possible position of following strings
.of("Ab", "ab", "_a", "2a")
.flatMap(s -> IntStream
.range(0, s.length() + 1)
.boxed()
.flatMap(i -> illegalChars
.stream()
.map(c -> s.substring(0, i) + c + s.substring(i))));
return Stream
.concat(illegalChars.stream().map(c -> "" + c), illegalStrings)
.map(s -> new Object[]{s})
.toArray(Object[][]::new);
}
@ParameterizedTest
@MethodSource("illegalKeys")
public void testNormalizeOnIllegalKeys(String illegalKey) {
assertThrows(IllegalArgumentException.class, () -> normalize(illegalKey));
}
public static Object[][] normalizedKeys() {
return new Object[][]{
// Empty string
{""},
// Non-alpha prefix
{"_"},
{"0"},
{"_xy-@"},
{"0xy-@"},
// Upper-case prefix
{"A"},
{"B"},
{"Ayz-@"},
{"Byz-@"},
};
}
@ParameterizedTest
@MethodSource("normalizedKeys")
public void testNormalizeOnNormalizedKeys(String normalizedKey) {
// Verify that the fast-path is taken
assertSame(normalize(normalizedKey), normalizedKey);
}
public static Object[][] notNormalizedKeys() {
return new Object[][]{
{"a"},
{"b"},
{"axy-@"},
{"bxy-@"},
};
}
@ParameterizedTest
@MethodSource("notNormalizedKeys")
public void testNormalizeOnNotNormalizedKeys(String notNormalizedKey) {
var normalizedKey = normalize(notNormalizedKey);
// Verify that the fast-path is *not* taken
assertNotSame(normalizedKey, notNormalizedKey);
// Verify the result
var expectedNormalizedKey = normalizedKey.substring(0, 1).toUpperCase() + normalizedKey.substring(1);
assertEquals(expectedNormalizedKey, normalizedKey);
}
private static String normalize(String key) {
return Headers.of(key, "foo").keySet().iterator().next();
}
// Immutability tests in UnmodifiableHeadersTest.java
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2022, 2025, 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.
*/
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.HttpHeaderParser;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
/*
* @test
* @bug 8289291
* @summary Verifies that the HttpServer doesn't add the "max" parameter to the Keep-Alive header
* that it sets in the response
* @library /test/lib
* @run main Http10KeepAliveMaxParamTest
*/
public class Http10KeepAliveMaxParamTest {
/**
* Sends a HTTP/1.0 request with "Connection: keep-alive" header to the
* com.sun.net.httpserver.HttpServer and then verifies that if the server responds back
* with a "Keep-Alive" header in the response, then the header value doesn't have the
* "max" parameter.
*/
public static void main(final String[] args) throws Exception {
final var bindAddr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
final int backlog = 0;
final HttpServer server = HttpServer.create(bindAddr, backlog);
server.createContext("/", (exchange) -> {
System.out.println("Sending response for request " + exchange.getRequestURI());
exchange.sendResponseHeaders(200, RSPBODY_CHUNKED);
exchange.getResponseBody().close();
});
server.start();
System.out.println("Server started at address " + server.getAddress());
try {
try (final Socket sock = new Socket(bindAddr.getAddress(), server.getAddress().getPort())) {
// send a HTTP/1.0 request
final String request = "GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n";
final OutputStream os = sock.getOutputStream();
os.write(request.getBytes(StandardCharsets.UTF_8));
os.flush();
System.out.println("Sent request to server:");
System.out.println(request);
// read the response headers
final HttpHeaderParser headerParser = new HttpHeaderParser(sock.getInputStream());
// verify that the response contains 200 status code.
// method is oddly named, but it returns status line of response
final String statusLine = headerParser.getRequestDetails();
System.out.println("Received status line " + statusLine);
if (statusLine == null || !statusLine.contains("200")) {
throw new AssertionError("Unexpected response from server," +
" status line = " + statusLine);
}
System.out.println("Server responded with headers: " + headerParser.getHeaderMap());
// spec doesn't mandate the presence of the Keep-Alive header. We skip this test
// if the server doesn't send one
final List<String> keepAliveHeader = headerParser.getHeaderValue("keep-alive");
if (keepAliveHeader == null || keepAliveHeader.isEmpty()) {
// skip the test
System.out.println("Test SKIPPED since the server didn't return a keep-alive" +
" header in response");
return;
}
// we expect only one keep-alive header and there shouldn't be any "max" parameter
// in that value
final String val = keepAliveHeader.get(0);
if (val.toLowerCase(Locale.ROOT).contains("max")) {
throw new AssertionError("Server wasn't supposed to send " +
"\"max\" parameter in keep-alive response header. " +
"Actual header value = " + val);
}
}
} finally {
server.stop(0);
}
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @bug 8269692
* @summary HttpContext::createContext should throw IllegalArgumentException
* if context already exists
* @run junit/othervm HttpContextTest
*/
import java.io.IOException;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
public class HttpContextTest {
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
@Test
public void test() throws IOException {
final var server = HttpServer.create(null, 0);
final var path = "/foo/";
assertThrows(IAE, () -> server.removeContext(path));
HttpContext context = server.createContext(path);
assertEquals(path, context.getPath());
assertThrows(IAE, () -> server.createContext(path));
assertThrows(IAE, () -> server.createContext(path, new Handler()));
context.setHandler(new Handler());
assertThrows(IAE, () -> server.createContext(path));
assertThrows(IAE, () -> server.createContext(path, new Handler()));
server.removeContext(context);
assertThrows(IAE, () -> server.removeContext(path));
context = server.createContext(path, new Handler());
assertEquals(path, context.getPath());
assertThrows(IAE, () -> server.createContext(path));
assertThrows(IAE, () -> server.createContext(path, new Handler()));
server.removeContext(path);
assertThrows(IAE, () -> server.removeContext(path));
}
/**
* Confirms that it is possible to create a subcontext, a context whose path
* shares the prefix of an existing context.
*/
@Test
public void testSubcontext() throws IOException {
final var server = HttpServer.create(null, 0);
server.createContext("/foo/bar/");
server.createContext("/foo/");
server.createContext("/foo");
server.createContext("/foo/bar");
}
/**
* A no-op handler
*/
static class Handler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException { }
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020, 2025, 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.
*/
/*
* @test
* @bug 8251496
* @summary Tests for methods in HttpPrincipal
* @run junit/othervm HttpPrincipalTest
*/
import com.sun.net.httpserver.HttpPrincipal;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class HttpPrincipalTest {
@Test
public void testGetters() {
var principal = new HttpPrincipal("test", "123");
assertEquals("test", principal.getUsername());
assertEquals("123", principal.getRealm());
assertEquals("123:test", principal.getName());
assertEquals(principal.getName(), principal.toString());
assertEquals(principal.hashCode(), ("test"+"123").hashCode());
}
}

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @bug 8270286
* @summary Test for HttpServerProvider::loadProviderFromProperty
* @run junit/othervm
* -Dcom.sun.net.httpserver.HttpServerProvider=HttpServerProviderTest$ProviderP
* HttpServerProviderTest
* @run junit/othervm
* -Dcom.sun.net.httpserver.HttpServerProvider=HttpServerProviderTest$ProviderPNPC
* HttpServerProviderTest
* @run junit/othervm
* -Dcom.sun.net.httpserver.HttpServerProvider=HttpServerProviderTest$ProviderNP
* HttpServerProviderTest
* @run junit/othervm
* -Dcom.sun.net.httpserver.HttpServerProvider=HttpServerProviderTest$ProviderT
* HttpServerProviderTest
* @run junit/othervm
* -Dcom.sun.net.httpserver.HttpServerProvider=DoesNotExist
* HttpServerProviderTest
*/
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.util.ServiceConfigurationError;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsServer;
import com.sun.net.httpserver.spi.HttpServerProvider;
import org.junit.jupiter.api.Assertions;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
public class HttpServerProviderTest {
public final static String PROPERTY_KEY = "com.sun.net.httpserver.HttpServerProvider";
@Test
public void test() throws Exception {
var provider = System.getProperty(PROPERTY_KEY);
switch (provider) {
case "HttpServerProviderTest$ProviderP" -> testPublic();
case "HttpServerProviderTest$ProviderPNPC" -> testPublicNonPublicConstructor();
case "HttpServerProviderTest$ProviderNP" -> testNonPublic();
case "HttpServerProviderTest$ProviderT" -> testThrowingConstructor();
default -> testBadData();
}
}
private void testPublic() throws Exception {
var n = ProviderP.class.getName();
assertEquals(n, System.getProperty(PROPERTY_KEY));
var p = HttpServerProvider.provider();
assertNull(p.createHttpServer(null, 0));
assertNull(p.createHttpsServer(null, 0));
}
private void testPublicNonPublicConstructor() {
var n = ProviderPNPC.class.getName();
assertEquals(n, System.getProperty(PROPERTY_KEY));
var e = Assertions.assertThrows(ServiceConfigurationError.class, HttpServerProvider::provider);
assertEquals(ServiceConfigurationError.class, e.getClass());
assertEquals(IllegalAccessException.class, e.getCause().getClass());
}
private void testNonPublic() {
var n = ProviderNP.class.getName();
assertEquals(n, System.getProperty(PROPERTY_KEY));
var e = Assertions.assertThrows(ServiceConfigurationError.class, HttpServerProvider::provider);
assertEquals(ServiceConfigurationError.class, e.getClass());
assertEquals(IllegalAccessException.class, e.getCause().getClass());
}
private void testThrowingConstructor() {
var cn = ProviderT.class.getName();
assertEquals(cn, System.getProperty(PROPERTY_KEY));
var e = Assertions.assertThrows(ServiceConfigurationError.class, HttpServerProvider::provider);
assertEquals(ServiceConfigurationError.class, e.getClass());
assertEquals(InvocationTargetException.class, e.getCause().getClass());
assertEquals("throwing constructor", e.getCause().getCause().getMessage());
}
private void testBadData() {
var cn = "DoesNotExist";
assertEquals(cn, System.getProperty(PROPERTY_KEY));
var e = Assertions.assertThrows(ServiceConfigurationError.class, HttpServerProvider::provider);
assertEquals(ServiceConfigurationError.class, e.getClass());
assertEquals(ClassNotFoundException.class, e.getCause().getClass());
}
/**
* Test provider that is public (P)
*/
public static class ProviderP extends HttpServerProvider {
public ProviderP() { super(); }
@Override
public HttpServer createHttpServer(InetSocketAddress addr, int backlog) { return null; }
@Override
public HttpsServer createHttpsServer(InetSocketAddress addr, int backlog) { return null; }
}
/**
* Test provider that is public with a non-public constructor (PNPC)
*/
public static class ProviderPNPC extends HttpServerProvider {
/*package-private*/ ProviderPNPC() { super(); }
@Override
public HttpServer createHttpServer(InetSocketAddress addr, int backlog) { return null; }
@Override
public HttpsServer createHttpsServer(InetSocketAddress addr, int backlog) { return null; }
}
/**
* Test provider that is not public (NP)
*/
/*package-private*/ static class ProviderNP extends HttpServerProvider {
/*package-private*/ ProviderNP() { super(); }
@Override
public HttpServer createHttpServer(InetSocketAddress addr, int backlog) { return null; }
@Override
public HttpsServer createHttpsServer(InetSocketAddress addr, int backlog) { return null; }
}
/**
* Test provider with a constructor that throws
*/
public static class ProviderT extends HttpServerProvider {
public ProviderT() { throw new AssertionError("throwing constructor"); }
@Override
public HttpServer createHttpServer(InetSocketAddress addr, int backlog) { return null; }
@Override
public HttpsServer createHttpsServer(InetSocketAddress addr, int backlog) { return null; }
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2019, 2025, 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.
*/
/**
* @test
* @bug 8233185
* @summary HttpServer.stop() blocks indefinitely when called on dispatch thread
* @modules java.base/sun.net.www
* @library /test/lib
* @run main/othervm HttpServerTest
*/
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
public class HttpServerTest implements HttpHandler {
private static final int HTTP_STATUS_CODE_OK = 200;
private static CountDownLatch serverStopped = new CountDownLatch(1);
private final HttpServer server;
public HttpServerTest(HttpServer server) {
this.server = server;
}
@Override
public void handle(HttpExchange ex) throws IOException {
sendHttpStatusCode(HTTP_STATUS_CODE_OK, ex);
System.out.println("Stopping server ...");
server.stop(0);
serverStopped.countDown();
}
private void sendHttpStatusCode(int httpCode, HttpExchange ex) {
try {
ex.sendResponseHeaders(httpCode, RSPBODY_CHUNKED);
ex.close();
} catch (IOException e) {
throw new RuntimeException("Server couldn't send response: " + e, e);
}
}
public static void main(String[] args) throws IOException, InterruptedException {
HttpServer server = HttpServer.create(
new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
try {
server.createContext("/context", new HttpServerTest(server));
server.start();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/context")
.toURLUnchecked();
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
System.out.println("Client: Response code received: " + urlc.getResponseCode());
InputStream is = urlc.getInputStream();
is.readAllBytes();
is.close();
} finally {
serverStopped.await();
System.out.println("Server stopped as expected");
}
}
}

View file

@ -0,0 +1,399 @@
/*
* Copyright (c) 2024, 2025, 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.
*/
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.security.KeyStore;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManagerFactory;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;
import jdk.internal.util.OperatingSystem;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.net.http.HttpClient.Version.HTTP_1_1;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/*
* @test
* @bug 8326381
* @summary verifies that the setNeedClientAuth() and setWantClientAuth()
* methods on HttpsParameters class work as expected
* @modules java.base/jdk.internal.util
* @library /test/lib
* @build jdk.test.lib.net.SimpleSSLContext jdk.test.lib.net.URIBuilder
* @run junit HttpsParametersClientAuthTest
*/
public class HttpsParametersClientAuthTest {
private static final boolean IS_WINDOWS = OperatingSystem.isWindows();
private static final AtomicInteger TID = new AtomicInteger();
private static final ThreadFactory SRV_THREAD_FACTORY = (r) -> {
final Thread t = new Thread(r);
t.setDaemon(true);
t.setName("server-thread-" + TID.incrementAndGet());
return t;
};
private static final SSLContext serverSSLCtx = SimpleSSLContext.findSSLContext();
/**
* verifies default values of {@link HttpsParameters#setNeedClientAuth(boolean)}
* and {@link HttpsParameters#setWantClientAuth(boolean)} methods
*/
@Test
public void testDefaultClientAuth() throws Exception {
// test default values
HttpsParameters defaultParams = new Params();
assertFalse(defaultParams.getNeedClientAuth(),
"needClientAuth was expected to be false but wasn't");
assertFalse(defaultParams.getWantClientAuth(),
"wantClientAuth was expected to be false but wasn't");
}
/**
* sets {@link HttpsParameters#setNeedClientAuth(boolean)} and verifies
* that subsequent calls to {@link HttpsParameters#getNeedClientAuth()} returns
* the set value and {@link HttpsParameters#getWantClientAuth()} returns false
*/
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testNeedClientAuth(final boolean initialWantClientAuth) throws Exception {
HttpsParameters needClientAuthParams = new Params();
// first set wantClientAuth to an initial value to verify that it later gets reset
needClientAuthParams.setWantClientAuth(initialWantClientAuth);
// needClientAuth = true and thus wantClientAuth = false
needClientAuthParams.setNeedClientAuth(true);
assertTrue(needClientAuthParams.getNeedClientAuth(),
"needClientAuth was expected to be true but wasn't");
assertFalse(needClientAuthParams.getWantClientAuth(),
"wantClientAuth was expected to be false but wasn't");
// now set needClientAuth = false and verify that both needClientAuth and wantClientAuth
// are now false
needClientAuthParams.setNeedClientAuth(false);
assertFalse(needClientAuthParams.getNeedClientAuth(),
"needClientAuth was expected to be false but wasn't");
assertFalse(needClientAuthParams.getWantClientAuth(),
"wantClientAuth was expected to be false but wasn't");
}
/**
* sets {@link HttpsParameters#setWantClientAuth(boolean)} and verifies
* that subsequent calls to {@link HttpsParameters#getWantClientAuth()} returns
* the set value and {@link HttpsParameters#getNeedClientAuth()} returns false
*/
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testWantClientAuth(final boolean initialNeedClientAuth) throws Exception {
HttpsParameters wantClientAuthParams = new Params();
// first set needClientAuth to an initial value to verify that it later gets reset
wantClientAuthParams.setNeedClientAuth(initialNeedClientAuth);
// wantClientAuth = true and thus needClientAuth = false
wantClientAuthParams.setWantClientAuth(true);
assertTrue(wantClientAuthParams.getWantClientAuth(),
"wantClientAuth was expected to be true but wasn't");
assertFalse(wantClientAuthParams.getNeedClientAuth(),
"needClientAuth was expected to be false but wasn't");
// now set wantClientAuth = false and verify that both wantClientAuth and needClientAuth
// are now false
wantClientAuthParams.setWantClientAuth(false);
assertFalse(wantClientAuthParams.getWantClientAuth(),
"wantClientAuth was expected to be false but wasn't");
assertFalse(wantClientAuthParams.getNeedClientAuth(),
"needClientAuth was expected to be false but wasn't");
}
/**
* Starts a {@link HttpsServer} by
* {@linkplain HttpsParameters#setNeedClientAuth(boolean) setting needClientAuth} as
* {@code true}. The client is then configured either to present the client certificates
* during the TLS handshake or configured not to present them. In the case where the
* client presents the client certificates, the HTTP request issued by the client is
* expected to pass. In the other case where the client doesn't present the certificates,
* the test verifies that the HTTP request fails due to a connection error
* (caused by TLS handshake failure)
*
* @param presentClientCerts true if the client should present certificates
* during TLS handshake, false otherwise
*/
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testServerNeedClientAuth(final boolean presentClientCerts) throws Exception {
// SSLContext which contains both the key and the trust material and will be used
// by the server
final HttpsConfigurator configurator = new HttpsConfigurator(serverSSLCtx) {
@Override
public void configure(final HttpsParameters params) {
// we intentionally don't call params.setSSLParameters()
// and instead call params.setNeedClientAuth()
params.setNeedClientAuth(true); // "require" the client to present certs
}
};
try (final ExecutorService executor = Executors.newCachedThreadPool(SRV_THREAD_FACTORY)) {
final HttpsServer server = createHttpsServer(executor, configurator);
server.start();
System.out.println("started server at " + server.getAddress());
try {
final HttpClient.Builder builder = createClientBuilder();
// if the client is expected to present client certificates, then
// we construct a SSLContext which has both key and trust material.
// otherwise we construct a SSLContext that only has trust material
// and thus won't present certificates during TLS handshake
final SSLContext clientSSLCtx = presentClientCerts
? serverSSLCtx : onlyTrustStoreContext();
// construct the client using the SSLContext
try (final HttpClient client = builder.sslContext(clientSSLCtx)
.build()) {
// issue a request
final URI reqURI = URIBuilder.newBuilder()
.scheme("https")
.host(server.getAddress().getAddress())
.port(server.getAddress().getPort())
.path("/")
.build();
System.out.println("issuing request to " + reqURI);
final HttpResponse<Void> resp;
try {
resp = client.send(HttpRequest.newBuilder(reqURI).build(),
BodyHandlers.discarding());
if (!presentClientCerts) {
// request was expected to fail since the server was configured to force
// the client to present the client cert, but the client didn't
// present any
fail("request was expected to fail, but didn't");
}
assertEquals(200, resp.statusCode(), "unexpected response code");
// verify the client did present the certs
assertTrue(resp.sslSession().isPresent(), "missing SSLSession on response");
assertNotNull(resp.sslSession().get().getLocalCertificates(),
"client was expected to present certs to the server, but didn't");
} catch (IOException ioe) {
if (presentClientCerts) {
// wasn't expected to fail, just let the exception propagate
throw ioe;
}
// verify it failed due to right reason
Throwable cause = ioe.getCause();
while (cause != null) {
// either of SocketException or SSLHandshakeException are OK.
// additionally on Windows we accept even IOException
// (caused by WSAECONNABORTED)
if (cause instanceof SocketException se) {
final String msg = se.getMessage();
assertTrue(msg != null && msg.contains("Connection reset"),
"unexpected message in SocketException: " + msg);
System.out.println("received the expected exception: " + se);
break;
} else if (cause instanceof SSLHandshakeException she) {
final String msg = she.getMessage();
assertTrue(msg != null && msg.contains("certificate_required"),
"unexpected message in SSLHandshakeException: " + msg);
System.out.println("received the expected exception: " + she);
break;
} else if (IS_WINDOWS && cause instanceof IOException winIOE) {
// on Windows we sometimes receive this exception, which is
// acceptable
System.out.println("(windows) received the expected exception: "
+ winIOE);
break;
}
cause = cause.getCause();
}
if (cause == null) {
// didn't find expected exception, rethrow original exception
throw ioe;
}
}
}
} finally {
System.out.println("Stopping server at " + server.getAddress());
server.stop(0 /* delay */);
}
}
}
/**
* Starts a {@link HttpsServer} by
* {@linkplain HttpsParameters#setWantClientAuth(boolean) setting wantClientAuth} as
* {@code true}. The client is then configured to either present the client certificates
* during the TLS handshake or configured not to present them. In both these cases the
* HTTP request issued by the client is expected to pass.
*
* @param presentClientCerts true if the client should present certificates
* during TLS handshake, false otherwise
*/
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testServerWantClientAuth(final boolean presentClientCerts) throws Exception {
// SSLContext which contains both the key and the trust material and will be used
// by the server
final HttpsConfigurator configurator = new HttpsConfigurator(serverSSLCtx) {
@Override
public void configure(final HttpsParameters params) {
// we intentionally don't call params.setSSLParameters()
// and instead call params.setWantClientAuth()
params.setWantClientAuth(true); // "request" the client to present certs
}
};
try (final ExecutorService executor = Executors.newCachedThreadPool(SRV_THREAD_FACTORY)) {
final HttpsServer server = createHttpsServer(executor, configurator);
server.start();
System.out.println("started server at " + server.getAddress());
try {
final HttpClient.Builder builder = createClientBuilder();
// if the client is expected to present client certificates, then
// we construct a SSLContext which has both key and trust material.
// otherwise we construct a SSLContext that only has trust material
// and thus won't present certificates during TLS handshake
final SSLContext clientSSLCtx = presentClientCerts
? serverSSLCtx : onlyTrustStoreContext();
// construct the client using the SSLContext
try (final HttpClient client = builder.sslContext(clientSSLCtx)
.build()) {
// issue a request
final URI reqURI = URIBuilder.newBuilder()
.scheme("https")
.host(server.getAddress().getAddress())
.port(server.getAddress().getPort())
.path("/")
.build();
System.out.println("issuing request to " + reqURI);
final HttpResponse<Void> resp = client.send(
HttpRequest.newBuilder(reqURI).build(), BodyHandlers.discarding());
assertEquals(200, resp.statusCode(), "unexpected response code");
if (presentClientCerts) {
// verify the client did present the certs
assertTrue(resp.sslSession().isPresent(), "missing SSLSession on response");
assertNotNull(resp.sslSession().get().getLocalCertificates(),
"client was expected to present certs to the server, but didn't");
}
}
} finally {
System.out.println("Stopping server at " + server.getAddress());
server.stop(0 /* delay */);
}
}
}
private static HttpsServer createHttpsServer(final Executor executor,
final HttpsConfigurator configurator)
throws IOException {
final InetAddress loopback = InetAddress.getLoopbackAddress();
final HttpsServer server = HttpsServer.create(
new InetSocketAddress(loopback, 0), 0 /* backlog */);
server.setExecutor(executor);
server.setHttpsConfigurator(configurator);
server.createContext("/", new AllOKHandler());
return server;
}
private static HttpClient.Builder createClientBuilder() {
return HttpClient.newBuilder().version(HTTP_1_1)
.proxy(NO_PROXY);
}
/**
* Creates and returns a {@link SSLContext} which only has trust material
* and doesn't have any test specific keys.
*/
private static SSLContext onlyTrustStoreContext() throws Exception {
final KeyStore keyStore = loadTestKeyStore();
final TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(keyStore);
final SSLContext ctx = SSLContext.getInstance("TLS");
// initialize with only trust managers
ctx.init(null, tmf.getTrustManagers(), null);
return ctx;
}
private static KeyStore loadTestKeyStore() throws Exception {
final String testKeys = System.getProperty("test.src")
+ "/"
+ "../../../../../../test/lib/jdk/test/lib/net/testkeys";
try (final FileInputStream fis = new FileInputStream(testKeys)) {
final char[] passphrase = "passphrase".toCharArray();
final KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(fis, passphrase);
return ks;
}
}
// no-op implementations of the abstract methods of HttpsParameters
private static final class Params extends HttpsParameters {
@Override
public HttpsConfigurator getHttpsConfigurator() {
// no-op
return null;
}
@Override
public InetSocketAddress getClientAddress() {
// no-op
return null;
}
@Override
public void setSSLParameters(SSLParameters params) {
// no-op
}
}
// A HttpHandler which just returns 200 response code
private static final class AllOKHandler implements HttpHandler {
@Override
public void handle(final HttpExchange exchange) throws IOException {
System.out.println("responding to request: " + exchange.getRequestURI());
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
}
}
}

View file

@ -0,0 +1,234 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/**
* @test
* @bug 8266761
* @summary HttpServer can fail with an assertion error when a handler doesn't fully
* read the request body and sends back a reply with no content, or when
* the client closes its outputstream while the server tries to drains
* its content.
* @run junit/othervm InputNotRead
* @run junit/othervm -Djava.net.preferIPv6Addresses=true InputNotRead
*/
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import static java.nio.charset.StandardCharsets.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import org.junit.jupiter.api.Test;
public class InputNotRead {
private static final int msgCode = 200;
private static final String someContext = "/context";
static class ServerThreadFactory implements ThreadFactory {
static final AtomicLong tokens = new AtomicLong();
@Override
public Thread newThread(Runnable r) {
var thread = new Thread(r, "Server-" + tokens.incrementAndGet());
thread.setDaemon(true);
return thread;
}
}
static {
Logger.getLogger("").setLevel(Level.ALL);
Logger.getLogger("").getHandlers()[0].setLevel(Level.ALL);
}
@Test
public void testSendResponse() throws Exception {
System.out.println("testSendResponse()");
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer server = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
ExecutorService executor = Executors.newCachedThreadPool(new ServerThreadFactory());
server.setExecutor(executor);
try {
server.createContext(someContext, new HttpHandler() {
@Override
public void handle(HttpExchange msg) throws IOException {
System.err.println("Handling request: " + msg.getRequestURI());
byte[] reply = new byte[0];
try {
msg.getRequestBody().read();
try {
msg.sendResponseHeaders(msgCode, reply.length == 0
? RSPBODY_EMPTY : reply.length);
} catch(IOException ioe) {
ioe.printStackTrace();
}
} finally {
// don't close the exchange and don't close any stream
// to trigger the assertion.
System.err.println("Request handled: " + msg.getRequestURI());
}
}
});
server.start();
System.out.println("Server started at port "
+ server.getAddress().getPort());
runRawSocketHttpClient(loopback, server.getAddress().getPort(), -1);
} finally {
System.out.println("shutting server down");
executor.shutdown();
server.stop(0);
}
System.out.println("Server finished.");
}
@Test
public void testCloseOutputStream() throws Exception {
System.out.println("testCloseOutputStream()");
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer server = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
ExecutorService executor = Executors.newCachedThreadPool(new ServerThreadFactory());
server.setExecutor(executor);
try {
server.createContext(someContext, new HttpHandler() {
@Override
public void handle(HttpExchange msg) throws IOException {
System.err.println("Handling request: " + msg.getRequestURI());
byte[] reply = "Here is my reply!".getBytes(UTF_8);
try {
BufferedReader r = new BufferedReader(new InputStreamReader(msg.getRequestBody()));
r.read();
try {
msg.sendResponseHeaders(msgCode, reply.length == 0
? RSPBODY_EMPTY : reply.length);
msg.getResponseBody().write(reply);
msg.getResponseBody().close();
Thread.sleep(50);
} catch(IOException | InterruptedException ie) {
ie.printStackTrace();
}
} finally {
System.err.println("Request handled: " + msg.getRequestURI());
}
}
});
server.start();
System.out.println("Server started at port "
+ server.getAddress().getPort());
runRawSocketHttpClient(loopback, server.getAddress().getPort(), 64 * 1024 + 16);
} finally {
System.out.println("shutting server down");
executor.shutdown();
server.stop(0);
}
System.out.println("Server finished.");
}
static void runRawSocketHttpClient(InetAddress address, int port, int contentLength)
throws Exception
{
Socket socket = null;
PrintWriter writer = null;
BufferedReader reader = null;
final String CRLF = "\r\n";
try {
socket = new Socket(address, port);
writer = new PrintWriter(new OutputStreamWriter(
socket.getOutputStream()));
System.out.println("Client connected by socket: " + socket);
String body = "I will send all the data.";
if (contentLength <= 0)
contentLength = body.getBytes(UTF_8).length;
writer.print("GET " + someContext + "/ HTTP/1.1" + CRLF);
writer.print("User-Agent: Java/"
+ System.getProperty("java.version")
+ CRLF);
writer.print("Host: " + address.getHostName() + CRLF);
writer.print("Accept: */*" + CRLF);
writer.print("Content-Length: " + contentLength + CRLF);
writer.print("Connection: keep-alive" + CRLF);
writer.print(CRLF); // Important, else the server will expect that
// there's more into the request.
writer.flush();
System.out.println("Client wrote request to socket: " + socket);
writer.print(body);
writer.flush();
reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
System.out.println("Client start reading from server:" );
String line = reader.readLine();
for (; line != null; line = reader.readLine()) {
if (line.isEmpty()) {
break;
}
System.out.println("\"" + line + "\"");
}
System.out.println("Client finished reading from server" );
} finally {
// give time to the server to try & drain its input stream
Thread.sleep(500);
// closes the client outputstream while the server is draining
// it
if (writer != null) {
writer.close();
}
// give time to the server to trigger its assertion
// error before closing the connection
Thread.sleep(500);
if (reader != null)
try {
reader.close();
} catch (IOException logOrIgnore) {
logOrIgnore.printStackTrace();
}
if (socket != null) {
try {
socket.close();
} catch (IOException logOrIgnore) {
logOrIgnore.printStackTrace();
}
}
}
System.out.println("Client finished." );
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2005, 2024, 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.
*/
import java.util.*;
import java.text.*;
import java.io.*;
import com.sun.net.httpserver.*;
class LogFilter extends Filter {
PrintStream ps;
DateFormat df;
LogFilter (File file) throws IOException {
ps = new PrintStream (new FileOutputStream (file));
df = DateFormat.getDateTimeInstance();
}
/**
* The filter's implementation, which is invoked by the serve r
*/
public void doFilter (HttpExchange t, Filter.Chain chain) throws IOException
{
chain.doFilter (t);
HttpContext context = t.getHttpContext();
Headers rmap = t.getRequestHeaders();
String s = df.format (new Date());
s = s +" " + t.getRequestMethod() + " " + t.getRequestURI() + " ";
s = s +" " + t.getResponseCode () +" " + t.getRemoteAddress();
ps.println (s);
}
public void init (HttpContext ctx) {}
public String description () {
return "Request logger";
}
public void destroy (HttpContext c){}
}

View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2015, 2025, 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.
*/
/**
* @test
* @bug 8068795
* @summary HttpServer missing tailing space for some response codes
* @run main MissingTrailingSpace
* @run main/othervm -Djava.net.preferIPv6Addresses=true MissingTrailingSpace
* @author lev.priima@oracle.com
*/
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
public class MissingTrailingSpace {
private static final int noMsgCode = 207;
private static final String someContext = "/context";
public static void main(String[] args) throws Exception {
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer server = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
try {
server.setExecutor(Executors.newFixedThreadPool(1));
server.createContext(someContext, new HttpHandler() {
@Override
public void handle(HttpExchange msg) {
try {
try {
msg.sendResponseHeaders(noMsgCode, RSPBODY_EMPTY);
} catch(IOException ioe) {
ioe.printStackTrace();
}
} finally {
msg.close();
}
}
});
server.start();
System.out.println("Server started at port "
+ server.getAddress().getPort());
runRawSocketHttpClient(loopback, server.getAddress().getPort());
} finally {
((ExecutorService)server.getExecutor()).shutdown();
server.stop(0);
}
System.out.println("Server finished.");
}
static void runRawSocketHttpClient(InetAddress address, int port)
throws Exception
{
Socket socket = null;
PrintWriter writer = null;
BufferedReader reader = null;
final String CRLF = "\r\n";
try {
socket = new Socket(address, port);
writer = new PrintWriter(new OutputStreamWriter(
socket.getOutputStream()));
System.out.println("Client connected by socket: " + socket);
writer.print("GET " + someContext + "/ HTTP/1.1" + CRLF);
writer.print("User-Agent: Java/"
+ System.getProperty("java.version")
+ CRLF);
writer.print("Host: " + address.getHostName() + CRLF);
writer.print("Accept: */*" + CRLF);
writer.print("Connection: keep-alive" + CRLF);
writer.print(CRLF); // Important, else the server will expect that
// there's more into the request.
writer.flush();
System.out.println("Client wrote rquest to socket: " + socket);
reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
System.out.println("Client start reading from server:" );
String line = reader.readLine();
if ( !line.endsWith(" ") ) {
throw new RuntimeException("respond to unknown code "
+ noMsgCode
+ " doesn't return space at the end of the first header.\n"
+ "Should be: " + "\"" + line + " \""
+ ", but returns: " + "\"" + line + "\".");
}
for (; line != null; line = reader.readLine()) {
if (line.isEmpty()) {
break;
}
System.out.println("\"" + line + "\"");
}
System.out.println("Client finished reading from server" );
} finally {
if (reader != null)
try {
reader.close();
} catch (IOException logOrIgnore) {
logOrIgnore.printStackTrace();
}
if (writer != null) {
writer.close();
}
if (socket != null) {
try {
socket.close();
} catch (IOException logOrIgnore) {
logOrIgnore.printStackTrace();
}
}
}
System.out.println("Client finished." );
}
}

View file

@ -0,0 +1,215 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/**
* @test
* @bug 8278312
* @library /test/lib /test/jdk/java/net/httpclient /test/jdk/java/net/httpclient/lib
* @build jdk.test.lib.net.SimpleSSLContext jdk.httpclient.test.lib.common.HttpServerAdapters
* jdk.httpclient.test.lib.http2.Http2TestServer
* jdk.test.lib.net.IPSupport
*
* @modules java.net.http/jdk.internal.net.http.common
* java.net.http/jdk.internal.net.http.frame
* java.net.http/jdk.internal.net.http.hpack
* java.logging
* java.base/sun.net.www.http
* java.base/sun.net.www
* java.base/sun.net
* java.base/jdk.internal.net.quic
* java.net.http/jdk.internal.net.http.quic
* java.net.http/jdk.internal.net.http.quic.packets
* java.net.http/jdk.internal.net.http.quic.frames
* java.net.http/jdk.internal.net.http.quic.streams
* java.net.http/jdk.internal.net.http.http3.streams
* java.net.http/jdk.internal.net.http.http3.frames
* java.net.http/jdk.internal.net.http.http3
* java.net.http/jdk.internal.net.http.qpack
* java.net.http/jdk.internal.net.http.qpack.readers
* java.net.http/jdk.internal.net.http.qpack.writers
* java.base/jdk.internal.util
*
* @run main/othervm SANTest
* @summary Update SimpleSSLContext keystore to use SANs for localhost IP addresses
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import jdk.test.lib.net.IPSupport;
import jdk.httpclient.test.lib.common.HttpServerAdapters;
import jdk.httpclient.test.lib.http2.Http2TestServer;
/*
* Will fail if the testkeys file belonging to SimpleSSLContext
* does not have SAN entries for 127.0.0.1 or ::1
*/
public class SANTest implements HttpServerAdapters {
private static final SSLContext ctx = SimpleSSLContext.findSSLContext();
static HttpServer getHttpsServer(InetSocketAddress addr, Executor exec, SSLContext ctx) throws Exception {
HttpsServer server = HttpsServer.create(addr, 0);
server.setExecutor(exec);
server.setHttpsConfigurator(new HttpsConfigurator (ctx));
return server;
}
static final boolean hasIPv4 = IPSupport.hasIPv4();
static final boolean hasIPv6 = IPSupport.hasIPv6();
static HttpTestServer initServer(boolean h2, InetAddress addr, SSLContext ctx,
String sni, ExecutorService e) throws Exception {
HttpTestServer s = null;
InetSocketAddress ia = new InetSocketAddress (addr, 0);
if ((addr instanceof Inet4Address) && !hasIPv4)
return null;
if ((addr instanceof Inet6Address) && !hasIPv6)
return null;
if (!h2) {
s = HttpTestServer.of(getHttpsServer(ia, e, ctx));
HttpTestHandler h = new HttpTestEchoHandler();
s.addHandler(h, "/test1");
s.start();
return s;
} else {
s = HttpTestServer.of(new Http2TestServer(addr, sni, true, 0, e,
10, null, ctx, false));
HttpTestHandler h = new HttpTestEchoHandler();
s.addHandler(h, "/test1");
s.start();
return s;
}
}
public static void main (String[] args) throws Exception {
// Http/1.1 servers
HttpTestServer h1s1 = null;
HttpTestServer h1s2 = null;
// Http/2 servers
HttpTestServer h2s1 = null;
HttpTestServer h2s2 = null;
ExecutorService executor=null;
try {
System.out.print ("SANTest: ");
executor = Executors.newCachedThreadPool();
InetAddress l1 = InetAddress.getByName("::1");
InetAddress l2 = InetAddress.getByName("127.0.0.1");
h1s1 = initServer(false, l1, ctx, "::1", executor);
h1s2 = initServer(false, l2, ctx, "127.0.0.1", executor);
h2s1 = initServer(true, l1, ctx, "::1", executor);
h2s2 = initServer(true, l2, ctx, "127.0.0.1", executor);
test("127.0.0.1", h1s2);
test("::1", h1s1);
testNew("127.0.0.1", h2s2, executor);
testNew("::1", h2s1, executor);
System.out.println ("OK");
} finally {
if (h1s1 != null)
h1s1.stop();
if (h1s2 != null)
h1s2.stop();
if (h2s1 != null)
h2s1.stop();
if (h2s2 != null)
h2s2.stop();
if (executor != null)
executor.shutdown ();
}
}
static void test (String host, HttpTestServer server) throws Exception {
if (server == null)
return;
int port = server.getAddress().getPort();
String body = "Yellow world";
URL url = URIBuilder.newBuilder()
.scheme("https")
.host(host)
.port(port)
.path("/test1/foo.txt")
.toURL();
System.out.println("URL = " + url);
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
System.out.println("urlc = " + urlc);
if (urlc instanceof HttpsURLConnection) {
HttpsURLConnection urlcs = (HttpsURLConnection) urlc;
urlcs.setSSLSocketFactory (ctx.getSocketFactory());
}
urlc.setRequestMethod("POST");
urlc.setDoOutput(true);
OutputStream os = urlc.getOutputStream();
os.write(body.getBytes(StandardCharsets.ISO_8859_1));
os.close();
InputStream is = urlc.getInputStream();
byte[] vv = is.readAllBytes();
String ff = new String(vv, StandardCharsets.ISO_8859_1);
System.out.println("resp = " + ff);
if (!ff.equals(body))
throw new RuntimeException();
is.close();
}
static void testNew (String host, HttpTestServer server, Executor exec) throws Exception {
if (server == null)
return;
int port = server.getAddress().getPort();
String body = "Red and Yellow world";
URI uri = URIBuilder.newBuilder()
.scheme("https")
.host(host)
.port(port)
.path("/test1/foo.txt")
.build();
HttpClient client = HttpClient.newBuilder()
.sslContext(ctx)
.executor(exec)
.build();
HttpRequest req = HttpRequest.newBuilder(uri)
.version(HttpClient.Version.HTTP_2)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println("resp = " + resp.body());
if (!resp.body().equals(body))
throw new RuntimeException();
}
}

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2006, 2025, 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.
*/
/*
* @test
* @bug 6270015
* @library /test/lib
* @build jdk.test.lib.Asserts
* jdk.test.lib.Utils
* jdk.test.lib.net.SimpleSSLContext
* jdk.test.lib.net.URIBuilder
* @run main/othervm -Dsun.net.httpserver.selCacheTimeout=2 SelCacheTest
* @run main/othervm -Djava.net.preferIPv6Addresses=true
-Dsun.net.httpserver.selCacheTimeout=2 SelCacheTest
* @summary Light weight HTTP server
*/
import com.sun.net.httpserver.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import static jdk.test.lib.Asserts.assertEquals;
import static jdk.test.lib.Asserts.assertFileContentsEqual;
import static jdk.test.lib.Utils.createTempFileOfSize;
/* basic http/s connectivity test
* (based on Test1)
*/
public class SelCacheTest extends Test {
private static final String TEMP_FILE_PREFIX =
HttpServer.class.getPackageName() + '-' + SelCacheTest.class.getSimpleName() + '-';
private static final SSLContext ctx = SimpleSSLContext.findSSLContext();
public static void main(String[] args) throws Exception {
HttpServer s1 = null;
HttpsServer s2 = null;
ExecutorService executor=null;
Path smallFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23);
Path largeFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 2730088);
InetAddress loopback = InetAddress.getLoopbackAddress();
try {
System.out.print("Test1: ");
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
s1 = HttpServer.create(addr, 0);
if (s1 instanceof HttpsServer) {
throw new RuntimeException("should not be httpsserver");
}
s2 = HttpsServer.create(addr, 0);
// Assert that both files share the same parent and can be served from the same `FileServerHandler`
assertEquals(smallFilePath.getParent(), largeFilePath.getParent());
HttpHandler h = new FileServerHandler(smallFilePath.getParent().toString());
HttpContext c1 = s1.createContext("/", h);
HttpContext c2 = s2.createContext("/", h);
executor = Executors.newCachedThreadPool();
s1.setExecutor(executor);
s2.setExecutor(executor);
s2.setHttpsConfigurator(new HttpsConfigurator(ctx));
s1.start();
s2.start();
int port = s1.getAddress().getPort();
int httpsport = s2.getAddress().getPort();
test(true, "http", loopback, port, smallFilePath);
test(true, "http", loopback, port, largeFilePath);
test(true, "https", loopback, httpsport, smallFilePath);
test(true, "https", loopback, httpsport, largeFilePath);
test(false, "http", loopback, port, smallFilePath);
test(false, "http", loopback, port, largeFilePath);
test(false, "https", loopback, httpsport, smallFilePath);
test(false, "https", loopback, httpsport, largeFilePath);
System.out.println("OK");
} finally {
s1.stop(0);
s2.stop(0);
executor.shutdown();
Files.delete(smallFilePath);
Files.delete(largeFilePath);
}
}
static void test(boolean fixedLen, String protocol,
InetAddress address, int port, Path filePath) throws Exception {
Thread.sleep(2000);
URL url = URIBuilder.newBuilder()
.scheme(protocol)
.host(address)
.port(port)
.path("/" + filePath.getFileName())
.toURL();
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
if (urlc instanceof HttpsURLConnection) {
HttpsURLConnection urlcs = (HttpsURLConnection) urlc;
urlcs.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String s, SSLSession s1) {
return true;
}
});
urlcs.setSSLSocketFactory(ctx.getSocketFactory());
}
byte [] buf = new byte [4096];
if (fixedLen) {
urlc.setRequestProperty("XFixed", "yes");
}
InputStream is = urlc.getInputStream();
File temp = File.createTempFile("Test1", null);
temp.deleteOnExit();
OutputStream fout = new BufferedOutputStream(new FileOutputStream(temp));
int c, count = 0;
while ((c=is.read(buf)) != -1) {
count += c;
fout.write(buf, 0, c);
}
is.close();
fout.close();
assertEquals(filePath.toFile().length(), (long) count, "wrong amount of data returned");
assertFileContentsEqual(filePath, temp.toPath());
temp.delete();
}
}

View file

@ -0,0 +1,347 @@
/*
* Copyright (c) 2025, 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.
*/
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.Utils;
import jdk.test.lib.net.URIBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.fail;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* @test
* @bug 8304065
* @summary HttpServer.stop() should terminate immediately if no exchanges are in progress,
* else terminate after a timeout
* @library /test/lib
* @run junit/othervm -Djdk.internal.httpclient.debug=err ServerStopTerminationTest
*/
public class ServerStopTerminationTest {
// enabling logging for the http server
private final static Logger logger = Logger.getLogger("com.sun.net.httpserver");
static {
logger.setLevel(Level.FINEST);
Logger.getLogger("").getHandlers()[0].setLevel(Level.FINEST);
}
// The server instance used to test shutdown timing
private HttpServer server;
// Client for initiating exchanges
private HttpClient client;
// Allows test to await the start of the exchange
private final CountDownLatch start = new CountDownLatch(1);
// Allows test to signal when exchange should complete
private final CountDownLatch complete = new CountDownLatch(1);
@BeforeEach
public void setup() throws IOException {
// Create an HttpServer binding to the loopback address with an ephemeral port
server = HttpServer.create();
final InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
server.bind(
new InetSocketAddress(loopbackAddress, 0),
0);
// A handler with completion timing controlled by tests
server.createContext("/", exchange -> {
// Let the test know the exchange is started
start.countDown();
try {
// Wait for test to signal that we can complete the exchange
complete.await();
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
exchange.close();
} catch (final InterruptedException e) {
throw new IOException(e);
}
});
// Start server and client
server.start();
client = HttpClient.newBuilder().build();
}
/**
* Clean up resources used by this test
*/
@AfterEach
public void cleanup() {
client.shutdown();
}
/**
* Verify that a stop operation with a 1-second exchange and a 2-second delay
* completes when the exchange completes.
*
* @throws InterruptedException if an unexpected interruption occurs
*/
@Test
public void shouldAwaitActiveExchange() throws InterruptedException {
// Initiate an exchange
startExchange();
// Wait for the server to receive the exchange
start.await();
log("Exchange started");
// Complete the exchange one second into the future
final Duration exchangeDuration = Duration.ofSeconds(1);
// taking start time before entering completeExchange to account for possible
// delays in reaching server.stop().
final long startTime = System.nanoTime();
completeExchange(exchangeDuration);
log("Complete Exchange triggered");
// Time the shutdown sequence
final Duration delayDuration = Duration.ofSeconds(Utils.adjustTimeout(20));
log("Shutdown triggered with the delay of " + delayDuration.getSeconds());
final long elapsed = timeShutdown(delayDuration, startTime);
log("Shutdown complete");
// The shutdown should take at least as long as the exchange duration
if (elapsed < exchangeDuration.toNanos()) {
fail("HttpServer.stop terminated before exchange completed");
}
// The delay should not have expired
if (elapsed >= delayDuration.toNanos()) {
fail("HttpServer.stop terminated after delay expired");
}
}
/**
* Verify that a stop operation with a 1-second delay and a 10-second exchange
* completes after the delay expires.
*
* @throws InterruptedException if an unexpected interruption occurs
*/
@Test
public void shouldCompleteAfterDelay() throws InterruptedException {
// Initiate an exchange
startExchange();
// Wait for the server to receive the exchange
start.await();
log("Exchange started");
// Time the shutdown sequence
final Duration delayDuration = Duration.ofSeconds(1);
log("Shutdown triggered with the delay of " + delayDuration.getSeconds());
final long elapsed = timeShutdown(delayDuration);
log("Shutdown complete");
complete.countDown();
log("Exchange completed");
// The shutdown delay should have expired
if (elapsed < delayDuration.toNanos()) {
fail("HttpServer.stop terminated before delay");
}
}
/**
* Verify that a stop operation with a 1-second delay and a 20-second executor
* completes after the delay expires.
*
* @throws InterruptedException if an unexpected interruption occurs
*/
@Test
public void shouldCompeteAfterDelayCustomHandler() throws IOException, InterruptedException {
// Custom server setup to include the executor
log("Changing the server to the server with a custom executor");
// Create an HttpServer binding
final InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
server = HttpServer.create(new InetSocketAddress(loopbackAddress, 0),
0,
"/", exchange -> {
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
exchange.close();
});
final Duration executorSleepTime = Duration.ofSeconds(Utils.adjustTimeout(20));
server.setExecutor(command -> Thread.ofVirtual().start(() -> {
try {
// Let the test know the executor was triggered
start.countDown();
log("Custom executor started, sleeping");
// waiting in the executor stage before running the exchange
Thread.sleep(executorSleepTime);
log("Custom executor sleep complete");
command.run();// start the exchange
} catch (final InterruptedException e) {
throw new RuntimeException(e);
}
}));
// Start server and client
server.start();
log("Custom setup complete");
// Initiate an exchange
startExchange();
// Wait for the server to start the executor
start.await();
log("Exchange (Executor) started");
// Time the shutdown sequence
final Duration delayDuration = Duration.ofSeconds(1);
log("Shutdown triggered with the delay of " + delayDuration.getSeconds());
final long elapsed = timeShutdown(delayDuration);
log("Shutdown complete");
// The shutdown should not await the exchange to complete
if (elapsed >= executorSleepTime.toNanos()) {
fail("HttpServer.stop terminated too late");
}
// The shutdown delay should have expired
if (elapsed < delayDuration.toNanos()) {
fail("HttpServer.stop terminated before delay");
}
}
/**
* Verify that an HttpServer with no active exchanges terminates
* before the delay timeout occurs.
*/
@Test
public void noActiveExchanges() {
// With no active exchanges, shutdown should complete immediately
final Duration delayDuration = Duration.ofSeconds(Utils.adjustTimeout(5));
final long elapsed = timeShutdown(delayDuration);
log("Shutting down the server with no exchanges");
if (elapsed >= delayDuration.toNanos()) {
fail("Expected HttpServer.stop to terminate immediately with no active exchanges");
}
}
/**
* Verify that an already stopped HttpServer can be stopped
*/
@Test
public void shouldAllowRepeatedStop() {
final Duration delayDuration = Duration.ofSeconds(1);
log("Shutting down the server the first time");
timeShutdown(delayDuration);
log("Shutting down the server the second time");
timeShutdown(delayDuration);
}
/**
* Run HttpServer::stop with the given delay, returning the
* elapsed time in nanoseconds for the shutdown to complete
*/
private long timeShutdown(Duration delayDuration) {
final long startTime = System.nanoTime();
return timeShutdown(delayDuration, startTime);
}
/**
* This allows passing a custom start time
*/
private long timeShutdown(Duration delayDuration,
long startTime) {
server.stop((int) delayDuration.toSeconds());
return System.nanoTime() - startTime;
}
/**
* Initiate an exchange asynchronously
*/
private void startExchange() {
try {
final HttpRequest request = HttpRequest.newBuilder()
.uri(URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.build())
// We need to use POST to prevent retries
.POST(HttpRequest.BodyPublishers.ofString(""))
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.whenCompleteAsync((r, t) -> {
System.out.println("request completed (" + r + ", " + t + ")");
// count the latch down to allow the handler to complete
// and the server's dispatcher thread to proceed; The handler
// is called within the dispatcher thread since we haven't
// set any executor on the server side
complete.countDown();
});
} catch (final URISyntaxException e) {
throw new RuntimeException(e);
}
}
/**
* At the specified time into the future, signal to the
* handler that the exchange can complete.
*
* @param exchangeDuration the duration to wait before signaling completion
*/
private void completeExchange(Duration exchangeDuration) {
Thread.ofVirtual().start(() -> {
try {
Thread.sleep(exchangeDuration);
complete.countDown();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
/**
* This logging method will log the name of the method which called the log
* for easier debug
*
* @param message message to include in the log
*/
private void log(final String message) {
String filename = "";
try {
filename = Thread.currentThread()
.getStackTrace()[2].getMethodName();
} finally {
System.out.printf("{%s}: %s%n", filename, message);
}
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2014, 2019, 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.
*/
/**
* @test
* @bug 8015692
* @key intermittent
* @summary Test HttpServer instantiation, start, and stop repeated in a loop
* Testing for Bind exception on Windows. This test may fail
* intermittently if other tests / process manage to bind to
* the same port that the test is using in the short window
* time where the port might appear available again.
*/
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import com.sun.net.httpserver.HttpServer;
public class SimpleHttpServerTest {
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("java.version"));
InetSocketAddress serverAddr = new InetSocketAddress(0);
HttpServer server = HttpServer.create(serverAddr, 0);
int serverPort = server.getAddress().getPort();
server.start();
server.stop(0);
serverAddr = new InetSocketAddress(serverPort);
int exceptionCount = 0;
boolean failedOnce = false;
System.out.println("Using serverPort == " + serverPort);
RETRY: while (exceptionCount == 0) {
for (int i = 0; i < 100; i++) {
try {
server = HttpServer.create(serverAddr, 0);
server.start();
server.stop(0);
} catch (Exception ex) {
if (!failedOnce) {
failedOnce = true;
server = HttpServer.create(new InetSocketAddress(0), 0);
serverPort = server.getAddress().getPort();
server.start();
server.stop(0);
serverAddr = new InetSocketAddress(serverPort);
System.out.println("Retrying with serverPort == " + serverPort);
continue RETRY;
}
System.err.println("Got exception at iteration: " + i );
ex.printStackTrace();
exceptionCount++;
}
}
break;
}
if (exceptionCount > 0) {
throw new RuntimeException("Test Failed: got "
+ exceptionCount + " exceptions.");
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2014, 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.
*/
/**
* @test
* @bug 8066130
* @summary Test HttpServer stop method invocation before a start has been called
*/
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpServer;
public class StopNoStartTest {
public static void main(String[] args) throws Exception {
InetSocketAddress serverAddr = new InetSocketAddress(0);
HttpServer server = HttpServer.create(serverAddr, 0);
server.stop(0);
}
}

View file

@ -0,0 +1 @@
modules = jdk.httpserver java.logging

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2018, 2026, 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.
*/
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* @test
* @bug 8169358
* @summary HttpServer does not close client connection when RejectedExecutionException occurs.
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run main/othervm ${test.main.class}
*/
public class TaskRejectedTest {
private static final int BACKLOG = 0;
private static final String REQUEST_PATH = "/";
private static final int TIMEOUT = 10000; // 10 sec
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
private static void setupLogging() {
final Handler consoleHandler = new ConsoleHandler();
consoleHandler.setLevel(Level.FINEST);
logger.setLevel(Level.FINEST);
logger.addHandler(consoleHandler);
}
private static void runClient(InetSocketAddress listenAddr)
throws MalformedURLException, IOException {
URL url = new URL("http", listenAddr.getHostString(),
listenAddr.getPort(), REQUEST_PATH);
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setConnectTimeout(TIMEOUT);
con.setReadTimeout(TIMEOUT);
try {
con.connect();
con.getResponseCode();
} catch (SocketTimeoutException e) {
throw new RuntimeException("Connection was not closed by peer.", e);
} catch (SocketException e) {
// Expected (connection reset)
} finally {
con.disconnect();
}
}
public static void main(String[] args) throws Exception {
setupLogging(); // merely for debugging
Executor executor = Executors.newSingleThreadExecutor(r -> {
throw new RejectedExecutionException("test");
});
InetSocketAddress addr = new InetSocketAddress(
InetAddress.getLoopbackAddress(), 0);
HttpServer httpServer = HttpServer.create(addr, BACKLOG);
httpServer.setExecutor(executor);
httpServer.createContext(REQUEST_PATH, exc -> {
exc.sendResponseHeaders(200, RSPBODY_EMPTY);
exc.close();
});
try {
httpServer.start();
runClient(httpServer.getAddress());
} finally {
httpServer.stop(0);
}
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2005, 2023, 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.
*/
import com.sun.net.httpserver.*;
import java.util.logging.*;
public class Test {
static Logger logger;
static void enableLogging() {
logger = Logger.getLogger("com.sun.net.httpserver");
Handler h = new ConsoleHandler();
h.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
logger.addHandler(h);
}
}

View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/*
* @test
* @bug 6270015
* @library /test/lib
* @build jdk.test.lib.Asserts
* jdk.test.lib.Utils
* jdk.test.lib.net.SimpleSSLContext
* jdk.test.lib.net.URIBuilder
* @run main/othervm Test1
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test1
* @run main/othervm -Dsun.net.httpserver.maxReqTime=10 Test1
* @run main/othervm -Dsun.net.httpserver.nodelay=true Test1
* @summary Light weight HTTP server
*/
import com.sun.net.httpserver.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import static jdk.test.lib.Asserts.assertEquals;
import static jdk.test.lib.Asserts.assertFileContentsEqual;
import static jdk.test.lib.Utils.createTempFileOfSize;
/* basic http/s connectivity test
* Tests:
* - client/server
* - send/receive large/small file
* - chunked encoding
* - via http and https
*
* The test is also run with sun.net.httpserver.nodelay simply to exercise
* this option. There is no specific pass or failure related to running with
* this option.
*/
public class Test1 extends Test {
private static final String TEMP_FILE_PREFIX =
HttpServer.class.getPackageName() + '-' + Test1.class.getSimpleName() + '-';
private static final SSLContext ctx = SimpleSSLContext.findSSLContext();
public static void main (String[] args) throws Exception {
HttpServer s1 = null;
HttpsServer s2 = null;
ExecutorService executor=null;
Path smallFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23);
Path largeFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 2730088);
try {
System.out.print ("Test1: ");
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
s1 = HttpServer.create (addr, 0);
if (s1 instanceof HttpsServer) {
throw new RuntimeException ("should not be httpsserver");
}
s2 = HttpsServer.create (addr, 0);
// Assert that both files share the same parent and can be served from the same `FileServerHandler`
assertEquals(smallFilePath.getParent(), largeFilePath.getParent());
HttpHandler h = new FileServerHandler (smallFilePath.getParent().toString());
HttpContext c1 = s1.createContext ("/", h);
HttpContext c2 = s2.createContext ("/", h);
executor = Executors.newCachedThreadPool();
s1.setExecutor (executor);
s2.setExecutor (executor);
s2.setHttpsConfigurator(new HttpsConfigurator (ctx));
s1.start();
s2.start();
int port = s1.getAddress().getPort();
int httpsport = s2.getAddress().getPort();
test (true, "http", port, smallFilePath);
test (true, "http", port, largeFilePath);
test (true, "https", httpsport, smallFilePath);
test (true, "https", httpsport, largeFilePath);
test (false, "http", port, smallFilePath);
test (false, "http", port, largeFilePath);
test (false, "https", httpsport, smallFilePath);
test (false, "https", httpsport, largeFilePath);
System.out.println ("OK");
} finally {
if (s1 != null)
s1.stop(0);
if (s2 != null)
s2.stop(0);
if (executor != null)
executor.shutdown ();
Files.delete(smallFilePath);
Files.delete(largeFilePath);
}
}
static void test (boolean fixedLen, String protocol, int port, Path filePath) throws Exception {
URL url = URIBuilder.newBuilder()
.scheme(protocol)
.loopback()
.port(port)
.path("/" + filePath.getFileName())
.toURL();
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
if (urlc instanceof HttpsURLConnection) {
HttpsURLConnection urlcs = (HttpsURLConnection) urlc;
urlcs.setHostnameVerifier (new HostnameVerifier () {
public boolean verify (String s, SSLSession s1) {
return true;
}
});
urlcs.setSSLSocketFactory (ctx.getSocketFactory());
}
byte [] buf = new byte [4096];
if (fixedLen) {
urlc.setRequestProperty ("XFixed", "yes");
}
InputStream is = urlc.getInputStream();
File temp = File.createTempFile ("Test1", null);
temp.deleteOnExit();
OutputStream fout = new BufferedOutputStream (new FileOutputStream(temp));
int c, count = 0;
while ((c=is.read(buf)) != -1) {
count += c;
fout.write (buf, 0, c);
}
is.close();
fout.close();
assertEquals(filePath.toFile().length(), (long) count, "wrong amount of data returned");
assertFileContentsEqual(filePath, temp.toPath());
temp.delete();
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 2010, 2025, 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.
*/
/**
* @test
* @bug 7005016
* @summary pit jdk7 b121 sqe test jhttp/HttpServer150013 failing
* @run main/othervm -Dsun.net.httpserver.clockTick=1000 -Dsun.net.httpserver.idleInterval=3 Test10
* @run main/othervm -Dsun.net.httpserver.clockTick=1000 -Dsun.net.httpserver.idleInterval=3
* -Djava.net.preferIPv6Addresses Test10
*/
import com.sun.net.httpserver.*;
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* Test handling of empty Http headers
*/
public class Test10 extends Test {
public static void main (String[] args) throws Exception {
System.out.print ("Test10: ");
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
int port = server.getAddress().getPort();
HttpContext c2 = server.createContext ("/test", handler);
ExecutorService exec = Executors.newCachedThreadPool();
server.setExecutor (exec);
try {
server.start ();
doClient(port);
System.out.println ("OK");
} finally {
if (server != null)
server.stop(0);
if (exec != null)
exec.shutdown();
}
}
static class Handler implements HttpHandler {
volatile int invocation = 0;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
while (is.read() != -1);
Headers map = t.getRequestHeaders();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
public static void doClient (int port) throws Exception {
String s = "GET /test/1.html HTTP/1.1\r\n\r\n";
Socket socket = new Socket (InetAddress.getLoopbackAddress(), port);
OutputStream os = socket.getOutputStream();
os.write (s.getBytes());
socket.setSoTimeout (10 * 1000);
InputStream is = socket.getInputStream();
int c;
byte[] b = new byte [1024];
while ((c=is.read(b)) != -1) ;
is.close();
socket.close();
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2005, 2023, 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.
*/
/**
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @library /test/lib
* @run main Test11
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test11
*/
import java.net.*;
import java.util.concurrent.*;
import java.io.*;
import com.sun.net.httpserver.*;
import jdk.test.lib.net.URIBuilder;
public class Test11 {
static class Handler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
read (t.getRequestBody());
String response = "response";
t.sendResponseHeaders (200, response.length());
OutputStream os = t.getResponseBody();
os.write (response.getBytes ("ISO8859_1"));
t.close();
}
void read (InputStream is ) throws IOException {
byte[] b = new byte [8096];
while (is.read (b) != -1) {}
}
}
public static void main (String[] args) throws Exception {
System.out.print ("Test 11: ");
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer server = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
ExecutorService s = Executors.newCachedThreadPool();
try {
HttpContext ctx = server.createContext (
"/foo/bar/", new Handler ()
);
s = Executors.newCachedThreadPool();
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/Foo/bar/test.html")
.toURL();
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
int r = urlc.getResponseCode();
if (r == 200) {
throw new RuntimeException ("wrong response received");
}
System.out.println ("OK");
} finally {
s.shutdown();
server.stop(0);
}
}
}

View file

@ -0,0 +1,174 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
import com.sun.net.httpserver.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import static jdk.test.lib.Asserts.assertEquals;
import static jdk.test.lib.Asserts.assertFileContentsEqual;
import static jdk.test.lib.Utils.createTempFileOfSize;
/*
* @test
* @bug 6270015 8359477
* @summary Light weight HTTP server - basic http/s connectivity test, same as Test1,
* but in parallel
* @library /test/lib
* @build jdk.test.lib.Asserts
* jdk.test.lib.Utils
* jdk.test.lib.net.SimpleSSLContext
* jdk.test.lib.net.URIBuilder
* @run main/othervm Test12
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test12
*/
public class Test12 extends Test {
private static final String TEMP_FILE_PREFIX =
HttpServer.class.getPackageName() + '-' + Test12.class.getSimpleName() + '-';
private static final SSLContext ctx = SimpleSSLContext.findSSLContext();
public static void main (String[] args) throws Exception {
HttpServer s1 = null;
HttpsServer s2 = null;
Path smallFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23);
Path largeFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 2730088);
final ExecutorService executor = Executors.newCachedThreadPool();
try {
System.out.print ("Test12: ");
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
s1 = HttpServer.create (addr, 0);
s2 = HttpsServer.create (addr, 0);
// Assert that both files share the same parent and can be served from the same `FileServerHandler`
assertEquals(smallFilePath.getParent(), largeFilePath.getParent());
HttpHandler h = new FileServerHandler(smallFilePath.getParent().toString());
HttpContext c1 = s1.createContext ("/", h);
HttpContext c2 = s2.createContext ("/", h);
s1.setExecutor (executor);
s2.setExecutor (executor);
s2.setHttpsConfigurator(new HttpsConfigurator (ctx));
s1.start();
s2.start();
int port = s1.getAddress().getPort();
int httpsport = s2.getAddress().getPort();
final Runner[] r = new Runner[8];
r[0] = new Runner (true, "http", port, smallFilePath);
r[1] = new Runner (true, "http", port, largeFilePath);
r[2] = new Runner (true, "https", httpsport, smallFilePath);
r[3] = new Runner (true, "https", httpsport, largeFilePath);
r[4] = new Runner (false, "http", port, smallFilePath);
r[5] = new Runner (false, "http", port, largeFilePath);
r[6] = new Runner (false, "https", httpsport, smallFilePath);
r[7] = new Runner (false, "https", httpsport, largeFilePath);
// submit the tasks
final List<Future<Void>> futures = new ArrayList<>();
for (Runner runner : r) {
futures.add(executor.submit(runner));
}
// wait for the tasks' completion
for (Future<Void> f : futures) {
f.get();
}
System.out.println ("All " + futures.size() + " tasks completed successfully");
} finally {
if (s1 != null) {
s1.stop(0);
}
if (s2 != null) {
s2.stop(0);
}
executor.close();
// it's OK to delete these files since the server side handlers
// serving these files have completed (guaranteed by the completion of Executor.close())
System.out.println("deleting " + smallFilePath);
Files.delete(smallFilePath);
System.out.println("deleting " + largeFilePath);
Files.delete(largeFilePath);
}
}
static class Runner implements Callable<Void> {
boolean fixedLen;
String protocol;
int port;
private final Path filePath;
Runner(boolean fixedLen, String protocol, int port, Path filePath) {
this.fixedLen=fixedLen;
this.protocol=protocol;
this.port=port;
this.filePath = filePath;
}
@Override
public Void call() throws Exception {
final URL url = URIBuilder.newBuilder()
.scheme(protocol)
.loopback()
.port(port)
.path("/" + filePath.getFileName())
.toURL();
final HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
if (urlc instanceof HttpsURLConnection) {
HttpsURLConnection urlcs = (HttpsURLConnection) urlc;
urlcs.setHostnameVerifier (new HostnameVerifier () {
public boolean verify (String s, SSLSession s1) {
return true;
}
});
urlcs.setSSLSocketFactory (ctx.getSocketFactory());
}
if (fixedLen) {
urlc.setRequestProperty ("XFixed", "yes");
}
final Path temp = Files.createTempFile(Path.of("."), "Test12", null);
final long numReceived;
try (InputStream is = urlc.getInputStream();
OutputStream fout = new BufferedOutputStream(new FileOutputStream(temp.toFile()))) {
numReceived = is.transferTo(fout);
}
System.out.println("received " + numReceived + " response bytes for " + url);
final long expected = filePath.toFile().length();
if (numReceived != expected) {
throw new RuntimeException ("expected " + expected + " bytes, but received "
+ numReceived);
}
assertFileContentsEqual(filePath, temp);
Files.delete(temp);
return null;
}
}
}

View file

@ -0,0 +1,199 @@
/*
* Copyright (c) 2005, 2026, 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.
*/
import com.sun.net.httpserver.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.*;
import java.util.logging.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import static jdk.test.lib.Asserts.assertFileContentsEqual;
import static jdk.test.lib.Utils.createTempFileOfSize;
/* basic http/s connectivity test
* Tests:
* - same as Test12, but with 64 threads
*/
/*
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @library /test/lib
* @build jdk.test.lib.Asserts
* jdk.test.lib.Utils
* jdk.test.lib.net.SimpleSSLContext
* jdk.test.lib.net.URIBuilder
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run main/othervm ${test.main.class}
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
*/
public class Test13 extends Test {
private static final String TEMP_FILE_PREFIX =
HttpServer.class.getPackageName() + '-' + Test13.class.getSimpleName() + '-';
private static final SSLContext ctx = SimpleSSLContext.findSSLContext();
private static final Logger logger = Logger.getLogger ("com.sun.net.httpserver");
final static int NUM = 32; // was 32
static boolean fail = false;
private static void setupLogging() {
final Handler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
logger.addHandler(handler);
}
public static void main (String[] args) throws Exception {
HttpServer s1 = null;
HttpsServer s2 = null;
ExecutorService executor=null;
Path filePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23);
setupLogging(); // merely for debugging
InetAddress loopback = InetAddress.getLoopbackAddress();
try {
System.out.print ("Test13: ");
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
s1 = HttpServer.create (addr, 0);
s2 = HttpsServer.create (addr, 0);
HttpHandler h = new FileServerHandler (filePath.getParent().toString());
HttpContext c1 = s1.createContext ("/", h);
HttpContext c2 = s2.createContext ("/", h);
executor = Executors.newCachedThreadPool();
s1.setExecutor (executor);
s2.setExecutor (executor);
s2.setHttpsConfigurator(new HttpsConfigurator (ctx));
s1.start();
s2.start();
int port = s1.getAddress().getPort();
int httpsport = s2.getAddress().getPort();
Runner r[] = new Runner[NUM*2];
for (int i=0; i<NUM; i++) {
r[i] = new Runner (true, "http", port, filePath);
r[i+NUM] = new Runner (true, "https", httpsport, filePath);
}
start (r);
join (r);
System.out.println ("OK");
} finally {
if (s1 != null)
s1.stop(0);
if (s2 != null)
s2.stop(0);
if (executor != null)
executor.shutdown ();
Files.delete(filePath);
}
}
static void start (Runner[] x) {
for (int i=0; i<x.length; i++) {
if (x[i] != null)
x[i].start();
}
}
static void join (Runner[] x) {
for (int i=0; i<x.length; i++) {
try {
if (x[i] != null)
x[i].join();
} catch (InterruptedException e) {}
}
}
static class Runner extends Thread {
boolean fixedLen;
String protocol;
int port;
private final Path filePath;
Runner (boolean fixedLen, String protocol, int port, Path filePath) {
this.fixedLen=fixedLen;
this.protocol=protocol;
this.port=port;
this.filePath = filePath;
}
public void run () {
try {
URL url = URIBuilder.newBuilder()
.scheme(protocol)
.loopback()
.port(port)
.path("/" + filePath.getFileName())
.toURL();
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
if (urlc instanceof HttpsURLConnection) {
HttpsURLConnection urlcs = (HttpsURLConnection) urlc;
urlcs.setHostnameVerifier (new HostnameVerifier () {
public boolean verify (String s, SSLSession s1) {
return true;
}
});
urlcs.setSSLSocketFactory (ctx.getSocketFactory());
}
byte [] buf = new byte [4096];
if (fixedLen) {
urlc.setRequestProperty ("XFixed", "yes");
}
InputStream is = urlc.getInputStream();
File temp = File.createTempFile ("Test1", null);
temp.deleteOnExit();
OutputStream fout = new BufferedOutputStream (new FileOutputStream(temp));
int c, count = 0;
while ((c=is.read(buf)) != -1) {
count += c;
fout.write (buf, 0, c);
}
is.close();
fout.close();
if (count != filePath.toFile().length()) {
throw new RuntimeException ("wrong amount of data returned");
}
assertFileContentsEqual(filePath, temp.toPath());
temp.delete();
} catch (Exception e) {
e.printStackTrace();
fail = true;
}
}
}
}

View file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2005, 2024, 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.
*/
/**
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @library /test/lib
* @run main Test14
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test14
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
/**
* Test filters
*/
public class Test14 extends Test {
static final String test_input = "Hello world";
static final String test_output = "Ifmmp!xpsme";
/* an outputstream which transforms the output data
* by adding one to each byte
*/
static class OffsetOutputStream extends FilterOutputStream {
OffsetOutputStream (OutputStream os) {
super (os);
}
public void write (int b) throws IOException {
super.write (b+1);
}
}
static class OffsetFilter extends Filter {
public String description() {
return "Translates outgoing data";
}
public void destroy(HttpContext c) {}
public void init(HttpContext c) {}
public void doFilter (HttpExchange exchange, Filter.Chain chain)
throws IOException {
exchange.setStreams (null, new OffsetOutputStream(
exchange.getResponseBody()
));
chain.doFilter (exchange);
}
}
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
File logfile = new File (
System.getProperty ("test.classes")+ "/log.txt"
);
ctx.getFilters().add (new OffsetFilter());
ctx.getFilters().add (new LogFilter(logfile));
if (ctx.getFilters().size() != 2) {
throw new RuntimeException ("wrong filter list size");
}
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
System.out.print ("Test14: " );
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
InputStream is = urlc.getInputStream();
int x = 0;
String output="";
while ((x=is.read())!= -1) {
output = output + (char)x;
}
error = !output.equals (test_output);
server.stop(0);
executor.shutdown();
if (error ) {
throw new RuntimeException ("test failed error");
}
System.out.println ("OK");
}
public static boolean error = false;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read () != -1) ;
is.close();
String response = test_input;
t.sendResponseHeaders (200, response.length());
OutputStream os = t.getResponseBody();
os.write (response.getBytes());
t.close();
}
}
}

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @library /test/lib
* @run main Test2
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test2
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test authentication
*/
public class Test2 extends Test {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
BasicAuthenticator a = new BasicAuthenticator ("foobar@test.realm") {
public boolean checkCredentials (String username, String pw) {
return "fred".equals(username) && pw.charAt(0) == 'x';
}
};
ctx.setAuthenticator (a);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
java.net.Authenticator.setDefault (new MyAuthenticator());
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
System.out.print ("Test2: " );
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
InputStream is = urlc.getInputStream();
int c = 0;
while (is.read()!= -1) {
c ++;
}
server.stop(0);
executor.shutdown();
if (error ) {
throw new RuntimeException ("test failed error");
}
if (c != 0) {
throw new RuntimeException ("test failed c");
}
if (count != 2) {
throw new RuntimeException ("test failed count = " + count);
}
System.out.println ("OK");
}
public static boolean error = false;
public static int count = 0;
static class MyAuthenticator extends java.net.Authenticator {
public PasswordAuthentication getPasswordAuthentication () {
PasswordAuthentication pw;
if (!getRequestingPrompt().equals ("foobar@test.realm")) {
Test2.error = true;
}
if (count == 0) {
pw = new PasswordAuthentication ("bad", "wrong".toCharArray());
} else {
pw = new PasswordAuthentication ("fred", "xyz".toCharArray());
}
count ++;
return pw;
}
}
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read () != -1) ;
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
HttpPrincipal p = t.getPrincipal ();
if (!p.getUsername().equals("fred")) {
error = true;
}
if (!p.getRealm().equals("foobar@test.realm")) {
error = true;
}
t.close();
}
}
}

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @run main/othervm -Dsun.net.httpserver.idleInterval=4 Test3
* @run main/othervm -Djava.net.preferIPv6Addresses=true
* -Dsun.net.httpserver.idleInterval=4 Test3
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.util.regex.*;
import java.io.*;
import java.net.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test pipe-lining over http
*/
public class Test3 extends Test {
static int count = 1;
public static void main (String[] args) throws Exception {
System.out.print ("Test3: ");
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
int port = server.getAddress().getPort();
HttpContext c2 = server.createContext ("/test", handler);
c2.getAttributes().put ("name", "This is the http handler");
ExecutorService exec = Executors.newCachedThreadPool();
server.setExecutor (exec);
try {
server.start ();
doClient(port);
System.out.println ("OK");
} finally {
if (server != null)
server.stop(0);
if (exec != null)
exec.shutdown();
}
}
static class Handler implements HttpHandler {
volatile int invocation = 0;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int x = invocation ++;
rmap.set ("XTest", Integer.toString (x));
switch (x) {
case 0:
try {Thread.sleep (2000); } catch (Exception e) {}
checkBody (is, body1);
break;
case 1:
try {Thread.sleep (1000); } catch (Exception e) {}
checkBody (is, body2);
break;
case 2:
checkBody (is, body3);
break;
case 3:
checkBody (is, body4);
break;
}
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
static void checkBody (InputStream is, String cmp) throws IOException {
byte [] b = new byte [1024];
int count = 0, c;
while ((c=is.read(b, count, b.length-count)) != -1) {
count+=c;
}
is.close();
String s = new String (b, 0, count, "ISO8859_1");
if (!s.equals (cmp)) {
throw new RuntimeException ("strings not equal");
}
}
static String body1 = "1234567890abcdefghij";
static String body2 = "2234567890abcdefghij0123456789";
static String body3 = "3wertyuiop";
static String body4 = "4234567890";
static String result =
"HTTP/1.1 200 OK.*Xtest: 0.*"+
"HTTP/1.1 200 OK.*Xtest: 1.*"+
"HTTP/1.1 200 OK.*Xtest: 2.*"+
"HTTP/1.1 200 OK.*Xtest: 3.*";
public static void doClient (int port) throws Exception {
String s = "GET /test/1.html HTTP/1.1\r\nContent-length: 20\r\n"+
"\r\n" +body1 +
"GET /test/2.html HTTP/1.1\r\nContent-length: 30\r\n"+
"\r\n"+ body2 +
"GET /test/3.html HTTP/1.1\r\nContent-length: 10\r\n"+
"\r\n"+ body3 +
"GET /test/4.html HTTP/1.1\r\nContent-length: 10\r\n"+
"\r\n"+body4;
Socket socket = new Socket (InetAddress.getLoopbackAddress(), port);
OutputStream os = socket.getOutputStream();
os.write (s.getBytes());
InputStream is = socket.getInputStream();
int c, count=0;
byte[] b = new byte [1024];
while ((c=is.read(b, count, b.length-count)) != -1) {
count +=c;
}
is.close();
socket.close();
s = new String (b,0,count, "ISO8859_1");
if (!compare (s, result)) {
throw new RuntimeException ("wrong string result");
}
}
static boolean compare (String s, String result) {
Pattern pattern = Pattern.compile (result,
Pattern.DOTALL|Pattern.CASE_INSENSITIVE
);
Matcher matcher = pattern.matcher (s);
return matcher.matches();
}
}

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @run main/othervm -Dsun.net.httpserver.idleInterval=4 Test4
* @run main/othervm -Djava.net.preferIPv6Addresses=true
* -Dsun.net.httpserver.idleInterval=4 Test4
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.util.regex.*;
import java.io.*;
import java.net.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test pipe-lining (block after read)
*/
public class Test4 extends Test {
static int count = 1;
public static void main (String[] args) throws Exception {
System.out.print ("Test4: ");
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
int port = server.getAddress().getPort();
HttpContext c2 = server.createContext ("/test", handler);
c2.getAttributes().put ("name", "This is the http handler");
ExecutorService exec = Executors.newCachedThreadPool();
server.setExecutor (exec);
try {
server.start ();
doClient(port);
System.out.println ("OK");
} finally {
if (server != null)
server.stop(0);
if (exec != null)
exec.shutdown();
}
}
static class Handler implements HttpHandler {
volatile int invocation = 0;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int x = invocation ++;
rmap.set ("XTest", Integer.toString (x));
switch (x) {
case 0:
checkBody (is, body1);
try {Thread.sleep (2000); } catch (Exception e) {}
break;
case 1:
checkBody (is, body2);
try {Thread.sleep (1000); } catch (Exception e) {}
break;
case 2:
checkBody (is, body3);
break;
case 3:
checkBody (is, body4);
break;
}
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
static void checkBody (InputStream is, String cmp) throws IOException {
byte [] b = new byte [1024];
int count = 0, c;
while ((c=is.read(b, count, b.length-count)) != -1) {
count+=c;
}
is.close();
String s = new String (b, 0, count, "ISO8859_1");
if (!s.equals (cmp)) {
throw new RuntimeException ("strings not equal");
}
}
static String body1 = "1234567890abcdefghij";
static String body2 = "2234567890abcdefghij0123456789";
static String body3 = "3wertyuiop";
static String body4 = "4234567890";
static String result =
"HTTP/1.1 200 OK.*Xtest: 0.*"+
"HTTP/1.1 200 OK.*Xtest: 1.*"+
"HTTP/1.1 200 OK.*Xtest: 2.*"+
"HTTP/1.1 200 OK.*Xtest: 3.*";
public static void doClient (int port) throws Exception {
String s = "GET /test/1.html HTTP/1.1\r\nContent-length: 20\r\n"+
"\r\n" +body1 +
"GET /test/2.html HTTP/1.1\r\nContent-length: 30\r\n"+
"\r\n"+ body2 +
"GET /test/3.html HTTP/1.1\r\nContent-length: 10\r\n"+
"\r\n"+ body3 +
"GET /test/4.html HTTP/1.1\r\nContent-length: 10\r\n"+
"\r\n"+body4;
Socket socket = new Socket (InetAddress.getLoopbackAddress(), port);
OutputStream os = socket.getOutputStream();
os.write (s.getBytes());
InputStream is = socket.getInputStream();
int c, count=0;
byte[] b = new byte [1024];
while ((c=is.read(b, count, b.length-count)) != -1) {
count +=c;
}
is.close();
socket.close();
s = new String (b,0,count, "ISO8859_1");
if (!compare (s, result)) {
throw new RuntimeException ("wrong string result");
}
}
static boolean compare (String s, String result) {
Pattern pattern = Pattern.compile (result,
Pattern.DOTALL|Pattern.CASE_INSENSITIVE
);
Matcher matcher = pattern.matcher (s);
return matcher.matches();
}
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @run main/othervm -Dsun.net.httpserver.idleInterval=4 Test5
* @run main/othervm -Djava.net.preferIPv6Addresses=true
* -Dsun.net.httpserver.idleInterval=4 Test5
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.util.regex.*;
import java.io.*;
import java.net.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test pipe-lining (no block)
*/
public class Test5 extends Test {
static int count = 1;
public static void main (String[] args) throws Exception {
System.out.print ("Test5: ");
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
int port = server.getAddress().getPort();
HttpContext c2 = server.createContext ("/test", handler);
c2.getAttributes().put ("name", "This is the http handler");
ExecutorService exec = Executors.newCachedThreadPool();
server.setExecutor (exec);
try {
server.start ();
doClient(port);
System.out.println ("OK");
} finally {
if (server != null)
server.stop(0);
if (exec != null)
exec.shutdown();
}
}
static class Handler implements HttpHandler {
volatile int invocation = 0;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int x = invocation ++;
rmap.set ("XTest", Integer.toString (x));
switch (x) {
case 0:
checkBody (is, body1);
break;
case 1:
checkBody (is, body2);
break;
case 2:
checkBody (is, body3);
break;
case 3:
checkBody (is, body4);
break;
}
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
static void checkBody (InputStream is, String cmp) throws IOException {
byte [] b = new byte [1024];
int count = 0, c;
while ((c=is.read(b, count, b.length-count)) != -1) {
count+=c;
}
is.close();
String s = new String (b, 0, count, "ISO8859_1");
if (!s.equals (cmp)) {
throw new RuntimeException ("strings not equal");
}
}
static String body1 = "1234567890abcdefghij";
static String body2 = "2234567890abcdefghij0123456789";
static String body3 = "3wertyuiop";
static String body4 = "4234567890";
static String result =
"HTTP/1.1 200 OK.*Xtest: 0.*"+
"HTTP/1.1 200 OK.*Xtest: 1.*"+
"HTTP/1.1 200 OK.*Xtest: 2.*"+
"HTTP/1.1 200 OK.*Xtest: 3.*";
public static void doClient (int port) throws Exception {
String s = "GET /test/1.html HTTP/1.1\r\nContent-length: 20\r\n"+
"\r\n" +body1 +
"GET /test/2.html HTTP/1.1\r\nContent-length: 30\r\n"+
"\r\n"+ body2 +
"GET /test/3.html HTTP/1.1\r\nContent-length: 10\r\n"+
"\r\n"+ body3 +
"GET /test/4.html HTTP/1.1\r\nContent-length: 10\r\n"+
"\r\n"+body4;
Socket socket = new Socket (InetAddress.getLoopbackAddress(), port);
OutputStream os = socket.getOutputStream();
os.write (s.getBytes());
InputStream is = socket.getInputStream();
int c, count=0;
byte[] b = new byte [1024];
while ((c=is.read(b, count, b.length-count)) != -1) {
count +=c;
}
is.close();
socket.close();
s = new String (b,0,count, "ISO8859_1");
if (!compare (s, result)) {
System.err.println(" Expected [" + result + "]\n actual [" + s + "]");
throw new RuntimeException ("wrong string result");
}
}
static boolean compare (String s, String result) {
Pattern pattern = Pattern.compile (result,
Pattern.DOTALL|Pattern.CASE_INSENSITIVE
);
Matcher matcher = pattern.matcher (s);
return matcher.matches();
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @library /test/lib
* @run main Test6
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test6
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test POST large file via chunked encoding (unusually small chunks)
*/
public class Test6 extends Test {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
System.out.print ("Test6: " );
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
urlc.setDoOutput (true);
urlc.setRequestMethod ("POST");
urlc.setChunkedStreamingMode (32); // small chunks
OutputStream os = new BufferedOutputStream (urlc.getOutputStream());
for (int i=0; i<SIZE; i++) {
os.write (i % 100);
}
os.close();
int resp = urlc.getResponseCode();
if (resp != 200) {
throw new RuntimeException ("test failed response code");
}
if (error) {
throw new RuntimeException ("test failed error");
}
server.stop(0);
executor.shutdown();
System.out.println ("OK");
}
public static boolean error = false;
final static int SIZE = 999999;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int c, count=0;
while ((c=is.read ()) != -1) {
if (c != (count % 100)) {
error = true;
break;
}
count ++;
}
if (count != SIZE) {
error = true;
}
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/*
* @test
* @bug 6270015
* @library /test/lib
* @build jdk.test.lib.net.SimpleSSLContext
* @run main/othervm Test6a
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test6a
* @summary Light weight HTTP server
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test https POST large file via chunked encoding (unusually small chunks)
*/
public class Test6a extends Test {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpsServer server = HttpsServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
SSLContext ssl = SimpleSSLContext.findSSLContext();
server.setExecutor (executor);
server.setHttpsConfigurator(new HttpsConfigurator (ssl));
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("https")
.host(server.getAddress().getAddress())
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
System.out.println("Test6a: URL=" + url);
System.out.print("Test6a: ");
HttpsURLConnection urlc = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
urlc.setDoOutput (true);
urlc.setRequestMethod ("POST");
urlc.setChunkedStreamingMode (32); // small chunks
urlc.setSSLSocketFactory (ssl.getSocketFactory());
urlc.setHostnameVerifier (new DummyVerifier());
OutputStream os = new BufferedOutputStream (urlc.getOutputStream());
for (int i=0; i<SIZE; i++) {
os.write (i % 100);
}
os.close();
int resp = urlc.getResponseCode();
if (resp != 200) {
throw new RuntimeException ("test failed response code");
}
if (error) {
throw new RuntimeException ("test failed error");
}
server.stop(0);
executor.shutdown();
System.out.println ("OK");
}
public static boolean error = false;
final static int SIZE = 999999;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int c, count=0;
while ((c=is.read ()) != -1) {
if (c != (count % 100)) {
error = true;
break;
}
count ++;
}
if (count != SIZE) {
error = true;
}
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6270015
* @library /test/lib
* @summary Light weight HTTP server
* @run main Test7
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test7
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test POST large file via chunked encoding (large chunks)
*/
public class Test7 extends Test {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
System.out.print ("Test7: " );
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
urlc.setDoOutput (true);
urlc.setRequestMethod ("POST");
urlc.setChunkedStreamingMode (16 * 1024); // big chunks
OutputStream os = new BufferedOutputStream (urlc.getOutputStream());
for (int i=0; i<SIZE; i++) {
os.write (i % 100);
}
os.close();
int resp = urlc.getResponseCode();
if (resp != 200) {
throw new RuntimeException ("test failed response code");
}
if (error) {
throw new RuntimeException ("test failed error");
}
server.stop(0);
executor.shutdown();
System.out.println ("OK");
}
public static boolean error = false;
final static int SIZE = 999999;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int c, count=0;
while ((c=is.read ()) != -1) {
if (c != (count % 100)) {
error = true;
break;
}
count ++;
}
if (count != SIZE) {
error = true;
}
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2005, 2026, 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.
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test POST large file via chunked encoding (large chunks)
*/
/*
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @library /test/lib
* @build jdk.test.lib.net.SimpleSSLContext jdk.test.lib.net.URIBuilder
* @run main/othervm ${test.main.class}
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
*/
public class Test7a extends Test {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpsServer server = HttpsServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
SSLContext ssl = SimpleSSLContext.findSSLContext();
server.setHttpsConfigurator(new HttpsConfigurator (ssl));
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("https")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
System.out.print ("Test7a: " );
HttpsURLConnection urlc = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
urlc.setDoOutput (true);
urlc.setRequestMethod ("POST");
urlc.setChunkedStreamingMode (16 * 1024); // big chunks
urlc.setHostnameVerifier (new DummyVerifier());
urlc.setSSLSocketFactory (ssl.getSocketFactory());
OutputStream os = new BufferedOutputStream (urlc.getOutputStream(), 8000);
for (int i=0; i<SIZE; i++) {
os.write (i % 100);
}
os.close();
int resp = urlc.getResponseCode();
if (resp != 200) {
throw new RuntimeException ("test failed response code");
}
if (error) {
throw new RuntimeException ("test failed error");
}
server.stop(0);
executor.shutdown();
System.out.println ("OK");
}
public static boolean error = false;
final static int SIZE = 999999;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int c, count=0;
while ((c=is.read ()) != -1) {
if (c != (count % 100)) {
error = true;
break;
}
count ++;
}
if (count != SIZE) {
error = true;
}
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6270015
* @library /test/lib
* @summary Light weight HTTP server
* @run main Test8
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test8
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test POST large file via fixed len encoding
*/
public class Test8 extends Test {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
System.out.print ("Test8: " );
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
urlc.setDoOutput (true);
urlc.setRequestMethod ("POST");
OutputStream os = new BufferedOutputStream (urlc.getOutputStream());
for (int i=0; i<SIZE; i++) {
os.write (i % 100);
}
os.close();
int resp = urlc.getResponseCode();
if (resp != 200) {
throw new RuntimeException ("test failed response code");
}
if (error) {
throw new RuntimeException ("test failed error");
}
server.stop(0);
executor.shutdown();
System.out.println ("OK");
}
public static boolean error = false;
final static int SIZE = 999999;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int c, count=0;
while ((c=is.read ()) != -1) {
if (c != (count % 100)) {
error = true;
break;
}
count ++;
}
if (count != SIZE) {
error = true;
}
if (!t.getRequestMethod().equals ("POST")) {
error = true;
}
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (c) 2005, 2026, 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.
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
/**
* Test POST large file via fixed len encoding
*/
/*
* @test
* @bug 6270015
* @summary Light weight HTTP server
* @library /test/lib
* @build jdk.test.lib.net.SimpleSSLContext jdk.test.lib.net.URIBuilder
* @run main/othervm ${test.main.class}
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
*/
public class Test8a extends Test {
public static void main (String[] args) throws Exception {
HttpsServer server = null;
ExecutorService executor = null;
try {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
server = HttpsServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
executor = Executors.newCachedThreadPool();
SSLContext ssl = SimpleSSLContext.findSSLContext();
server.setHttpsConfigurator(new HttpsConfigurator (ssl));
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("https")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
System.out.print ("Test8a: " );
HttpsURLConnection urlc = (HttpsURLConnection)url.openConnection(Proxy.NO_PROXY);
urlc.setDoOutput (true);
urlc.setRequestMethod ("POST");
urlc.setHostnameVerifier (new DummyVerifier());
urlc.setSSLSocketFactory (ssl.getSocketFactory());
OutputStream os = new BufferedOutputStream (urlc.getOutputStream(), 8000);
for (int i=0; i<SIZE; i++) {
os.write (i % 250);
}
os.close();
int resp = urlc.getResponseCode();
if (resp != 200) {
throw new RuntimeException ("test failed response code");
}
InputStream is = urlc.getInputStream ();
for (int i=0; i<SIZE; i++) {
int f = is.read();
if (f != (i % 250)) {
System.out.println ("Setting error(" +f +")("+i+")" );
error = true;
break;
}
}
is.close();
} finally {
if (server != null) server.stop(0);
if (executor != null) executor.shutdown();
}
if (error) {
throw new RuntimeException ("test failed error");
}
System.out.println ("OK");
}
public static boolean error = false;
//final static int SIZE = 999999;
final static int SIZE = 9999;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
System.out.println ("Handler.handle");
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
int c, count=0;
while ((c=is.read ()) != -1) {
if (c != (count % 250)) {
System.out.println ("Setting error 1");
error = true;
break;
}
count ++;
}
if (count != SIZE) {
System.out.println ("Setting error 2");
error = true;
}
is.close();
t.sendResponseHeaders (200, SIZE);
System.out.println ("Sending 200 OK");
OutputStream os = new BufferedOutputStream(t.getResponseBody(), 8000);
for (int i=0; i<SIZE; i++) {
os.write (i % 250);
}
os.close();
System.out.println ("Finished");
}
}
}

View file

@ -0,0 +1,210 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/*
* @test
* @bug 6270015
* @library /test/lib
* @build jdk.test.lib.Asserts
* jdk.test.lib.Utils
* jdk.test.lib.net.SimpleSSLContext
* jdk.test.lib.net.URIBuilder
* @run main/othervm Test9
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test9
* @summary Light weight HTTP server
*/
import com.sun.net.httpserver.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import static jdk.test.lib.Asserts.assertEquals;
import static jdk.test.lib.Asserts.assertFileContentsEqual;
import static jdk.test.lib.Utils.createTempFileOfSize;
/* Same as Test1 but requests run in parallel.
*/
public class Test9 extends Test {
private static final String TEMP_FILE_PREFIX =
HttpServer.class.getPackageName() + '-' + Test9.class.getSimpleName() + '-';
private static final SSLContext ctx = SimpleSSLContext.findSSLContext();
static volatile boolean error = false;
public static void main (String[] args) throws Exception {
HttpServer s1 = null;
HttpsServer s2 = null;
ExecutorService executor=null;
Path smallFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23);
Path largeFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 2730088);
smallFilePath.toFile().deleteOnExit();
largeFilePath.toFile().deleteOnExit();
try {
System.out.print ("Test9: ");
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
s1 = HttpServer.create (addr, 0);
s2 = HttpsServer.create (addr, 0);
// Assert that both files share the same parent and can be served from the same `FileServerHandler`
assertEquals(smallFilePath.getParent(), largeFilePath.getParent());
HttpHandler h = new FileServerHandler (smallFilePath.getParent().toString());
HttpContext c1 = s1.createContext ("/", h);
HttpContext c2 = s2.createContext ("/", h);
executor = Executors.newCachedThreadPool();
s1.setExecutor (executor);
s2.setExecutor (executor);
s2.setHttpsConfigurator(new HttpsConfigurator (ctx));
s1.start();
s2.start();
int p1 = s1.getAddress().getPort();
int p2 = s2.getAddress().getPort();
error = false;
Thread[] t = new Thread[100];
t[0] = test (true, "http", p1, smallFilePath);
t[1] = test (true, "http", p1, largeFilePath);
t[2] = test (true, "https", p2, smallFilePath);
t[3] = test (true, "https", p2, largeFilePath);
t[4] = test (false, "http", p1, smallFilePath);
t[5] = test (false, "http", p1, largeFilePath);
t[6] = test (false, "https", p2, smallFilePath);
t[7] = test (false, "https", p2, largeFilePath);
t[8] = test (true, "http", p1, smallFilePath);
t[9] = test (true, "http", p1, largeFilePath);
t[10] = test (true, "https", p2, smallFilePath);
t[11] = test (true, "https", p2, largeFilePath);
t[12] = test (false, "http", p1, smallFilePath);
t[13] = test (false, "http", p1, largeFilePath);
t[14] = test (false, "https", p2, smallFilePath);
t[15] = test (false, "https", p2, largeFilePath);
for (int i=0; i<16; i++) {
t[i].join();
}
if (error) {
throw new RuntimeException ("error");
}
System.out.println ("OK");
} finally {
if (s1 != null)
s1.stop(0);
if (s2 != null)
s2.stop(0);
if (executor != null)
executor.shutdown ();
}
}
static ClientThread test (boolean fixedLen, String protocol, int port, Path filePath) throws Exception {
ClientThread t = new ClientThread (fixedLen, protocol, port, filePath);
t.start();
return t;
}
static final Object fileLock = new Object();
static class ClientThread extends Thread {
boolean fixedLen;
String protocol;
int port;
private final Path filePath;
ClientThread (boolean fixedLen, String protocol, int port, Path filePath) {
this.fixedLen = fixedLen;
this.protocol = protocol;
this.port = port;
this.filePath = filePath;
}
public void run () {
try {
URL url = URIBuilder.newBuilder()
.scheme(protocol)
.loopback()
.port(port)
.path("/" + filePath.getFileName())
.toURL();
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
if (urlc instanceof HttpsURLConnection) {
HttpsURLConnection urlcs = (HttpsURLConnection) urlc;
urlcs.setHostnameVerifier (new HostnameVerifier () {
public boolean verify (String s, SSLSession s1) {
return true;
}
});
urlcs.setSSLSocketFactory (ctx.getSocketFactory());
}
byte [] buf = new byte [4096];
String s = "chunk";
if (fixedLen) {
urlc.setRequestProperty ("XFixed", "yes");
s = "fixed";
}
InputStream is = urlc.getInputStream();
File temp;
synchronized (fileLock) {
temp = File.createTempFile (s, null);
temp.deleteOnExit();
}
OutputStream fout = new BufferedOutputStream (new FileOutputStream(temp));
int c, count = 0;
while ((c=is.read(buf)) != -1) {
count += c;
fout.write (buf, 0, c);
}
is.close();
fout.close();
if (count != filePath.toFile().length()) {
System.out.println ("wrong amount of data returned");
System.out.println ("fixedLen = "+fixedLen);
System.out.println ("protocol = "+protocol);
System.out.println ("port = "+port);
System.out.println ("file = " + filePath);
System.out.println ("temp = "+temp);
System.out.println ("count = "+count);
error = true;
}
assertFileContentsEqual(filePath, temp.toPath());
} catch (Exception e) {
System.err.println("Error occurred: " + e);
error = true;
}
}
}
}

View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/*
* @test
* @bug 6270015
* @library /test/lib
* @build jdk.test.lib.Utils
* jdk.test.lib.net.SimpleSSLContext
* jdk.test.lib.net.URIBuilder
* @run main/othervm Test9a
* @run main/othervm -Djava.net.preferIPv6Addresses=true Test9a
* @summary Light weight HTTP server
*/
import com.sun.net.httpserver.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import static jdk.test.lib.Asserts.assertEquals;
import static jdk.test.lib.Asserts.assertFileContentsEqual;
import static jdk.test.lib.Utils.createTempFileOfSize;
/* Same as Test1 but requests run in parallel.
*/
public class Test9a extends Test {
private static final String TEMP_FILE_PREFIX =
HttpServer.class.getPackageName() + '-' + Test9a.class.getSimpleName() + '-';
private static final SSLContext serverCtx = SimpleSSLContext.findSSLContext();
private static final SSLContext clientCtx = SimpleSSLContext.findSSLContext();
static volatile boolean error = false;
public static void main (String[] args) throws Exception {
HttpsServer server = null;
ExecutorService executor=null;
Path smallFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 23);
Path largeFilePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 2730088);
try {
System.out.print ("Test9a: ");
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
server = HttpsServer.create (addr, 0);
// Assert that both files share the same parent and can be served from the same `FileServerHandler`
assertEquals(smallFilePath.getParent(), largeFilePath.getParent());
HttpHandler h = new FileServerHandler (smallFilePath.getParent().toString());
HttpContext c1 = server.createContext ("/", h);
executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.setHttpsConfigurator(new HttpsConfigurator (serverCtx));
server.start();
int port = server.getAddress().getPort();
error = false;
Thread[] t = new Thread[100];
t[0] = test (true, "https", port, smallFilePath);
t[1] = test (true, "https", port, largeFilePath);
t[2] = test (true, "https", port, smallFilePath);
t[3] = test (true, "https", port, largeFilePath);
t[4] = test (true, "https", port, smallFilePath);
t[5] = test (true, "https", port, largeFilePath);
t[6] = test (true, "https", port, smallFilePath);
t[7] = test (true, "https", port, largeFilePath);
t[8] = test (true, "https", port, smallFilePath);
t[9] = test (true, "https", port, largeFilePath);
t[10] = test (true, "https", port, smallFilePath);
t[11] = test (true, "https", port, largeFilePath);
t[12] = test (true, "https", port, smallFilePath);
t[13] = test (true, "https", port, largeFilePath);
t[14] = test (true, "https", port, smallFilePath);
t[15] = test (true, "https", port, largeFilePath);
for (int i=0; i<16; i++) {
t[i].join();
}
if (error) {
throw new RuntimeException ("error");
}
System.out.println ("OK");
} finally {
if (server != null)
server.stop(0);
if (executor != null)
executor.shutdown();
Files.delete(smallFilePath);
Files.delete(largeFilePath);
}
}
static int foo = 1;
static ClientThread test (boolean fixedLen, String protocol, int port, Path filePath) throws Exception {
ClientThread t = new ClientThread (fixedLen, protocol, port, filePath);
t.start();
return t;
}
static Object fileLock = new Object();
static class ClientThread extends Thread {
boolean fixedLen;
String protocol;
int port;
private final Path filePath;
ClientThread (boolean fixedLen, String protocol, int port, Path filePath) {
this.fixedLen = fixedLen;
this.protocol = protocol;
this.port = port;
this.filePath = filePath;
}
public void run () {
try {
URL url = URIBuilder.newBuilder()
.scheme(protocol)
.loopback()
.port(port)
.path("/" + filePath.getFileName())
.toURL();
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
if (urlc instanceof HttpsURLConnection) {
HttpsURLConnection urlcs = (HttpsURLConnection) urlc;
urlcs.setHostnameVerifier (new HostnameVerifier () {
public boolean verify (String s, SSLSession s1) {
return true;
}
});
urlcs.setSSLSocketFactory (clientCtx.getSocketFactory());
}
byte [] buf = new byte [4096];
String s = "chunk";
if (fixedLen) {
urlc.setRequestProperty ("XFixed", "yes");
s = "fixed";
}
InputStream is = urlc.getInputStream();
File temp;
synchronized (fileLock) {
temp = File.createTempFile (s, null);
temp.deleteOnExit();
}
OutputStream fout = new BufferedOutputStream (new FileOutputStream(temp));
int c, count = 0;
while ((c=is.read(buf)) != -1) {
count += c;
fout.write (buf, 0, c);
}
is.close();
fout.close();
if (count != filePath.toFile().length()) {
System.out.println ("wrong amount of data returned");
System.out.println ("fixedLen = "+fixedLen);
System.out.println ("protocol = "+protocol);
System.out.println ("port = "+port);
System.out.println ("file = " + filePath);
System.out.println ("temp = "+temp);
System.out.println ("count = "+count);
error = true;
}
assertFileContentsEqual(filePath, temp.toPath());
temp.delete();
} catch (Exception e) {
e.printStackTrace();
error = true;
}
}
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2006, 2026, 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.
*/
import com.sun.net.httpserver.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.*;
import java.util.logging.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static jdk.test.lib.Utils.createTempFileOfSize;
/*
* @test
* @bug 6422914
* @summary change httpserver exception printouts
* @library /test/lib
* @build jdk.test.lib.Utils
* jdk.test.lib.net.URIBuilder
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run main/othervm ${test.main.class}
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
*/
public class TestLogging extends Test {
private static final String TEMP_FILE_PREFIX =
HttpServer.class.getPackageName() + '-' + TestLogging.class.getSimpleName() + '-';
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
private static void setupLogging() {
logger.setLevel(Level.ALL);
final Handler handler = new ConsoleHandler();
handler.setLevel(Level.ALL);
logger.addHandler(handler);
}
public static void main (String[] args) throws Exception {
HttpServer s1 = null;
ExecutorService executor=null;
Path filePath = createTempFileOfSize(TEMP_FILE_PREFIX, null, 0xBEEF);
try {
System.out.print ("Test9: ");
setupLogging();
String root = filePath.getParent().toString();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
s1 = HttpServer.create (addr, 0);
logger.info(root);
HttpHandler h = new FileServerHandler (root);
HttpContext c1 = s1.createContext ("/", h);
executor = Executors.newCachedThreadPool();
s1.setExecutor (executor);
s1.start();
int p1 = s1.getAddress().getPort();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(p1)
.path("/" + filePath.getFileName().toString())
.toURL();
System.out.println("URL: " + url);
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
InputStream is = urlc.getInputStream();
while (is.read() != -1) ;
is.close();
url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(p1)
.path("/doesntexist.txt")
.toURLUnchecked();
System.out.println("URL: " + url);
urlc = (HttpURLConnection)url.openConnection();
try {
is = urlc.getInputStream();
while (is.read() != -1) ;
is.close();
} catch (IOException e) {
System.out.println ("caught expected exception");
}
Socket s = new Socket (InetAddress.getLoopbackAddress(), p1);
OutputStream os = s.getOutputStream();
//os.write ("GET xxx HTTP/1.1\r\n".getBytes());
os.write ("HELLO WORLD\r\n".getBytes());
is = s.getInputStream();
while (is.read() != -1) ;
os.close(); is.close(); s.close();
System.out.println ("OK");
} finally {
Files.delete(filePath);
if (s1 != null)
s1.stop(0);
if (executor != null)
executor.shutdown();
}
}
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @bug 8251496 8333590
* @summary Test that UnmodifiableHeaders is in fact immutable
* @modules jdk.httpserver/sun.net.httpserver:+open
* @run junit/othervm UnmodifiableHeadersTest
*/
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;
import sun.net.httpserver.UnmodifiableHeaders;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class UnmodifiableHeadersTest {
@Test
public void testEquality() {
var headers = new Headers();
var unmodifiableHeaders1 = new UnmodifiableHeaders(headers);
assertEquals(headers, unmodifiableHeaders1);
assertEquals(headers.hashCode(), unmodifiableHeaders1.hashCode());
assertEquals(headers.get("Foo"), unmodifiableHeaders1.get("Foo"));
headers.add("Foo", "Bar");
var unmodifiableHeaders2 = new UnmodifiableHeaders(headers);
assertEquals(headers, unmodifiableHeaders2);
assertEquals(headers.hashCode(), unmodifiableHeaders2.hashCode());
assertEquals(headers.get("Foo"), unmodifiableHeaders2.get("Foo"));
}
public static Stream<Headers> headers() {
var headers = new Headers();
headers.add("Foo", "Bar");
var exchange = new TestHttpExchange(headers);
return Stream.of(exchange.getRequestHeaders(),
Headers.of("Foo", "Bar"),
Headers.of(Map.of("Foo", List.of("Bar"))));
}
@ParameterizedTest
@MethodSource("headers")
public void testUnmodifiableHeaders(Headers headers) {
assertUnsupportedOperation(headers);
assertUnmodifiableCollection(headers);
assertUnmodifiableList(headers);
}
public static Stream<Headers> toStringHeaders() {
final Headers headers = new Headers();
headers.add("hello", "World");
return Stream.of(
headers,
Headers.of("abc", "XYZ"),
Headers.of(Map.of("foo", List.of("Bar"))),
Headers.of(Map.of("Hello", List.of())),
Headers.of(Map.of("one", List.of("two", "THREE")))
);
}
/*
* Verify that the String returned by Headers.toString() contains the expected
* key/value(s)
*/
@ParameterizedTest
@MethodSource("toStringHeaders")
public void testToString(final Headers headers) {
final Headers copy = Headers.of(headers);
assertNotNull(copy, "Headers.of() returned null");
final String actualToString = copy.toString();
assertNotNull(actualToString, "toString() returned null");
for (final Map.Entry<String, List<String>> originalHeadersEntry : headers.entrySet()) {
final String expectedKey = originalHeadersEntry.getKey();
// We just verify the presence of key and value in the toString()
// return value. We intentionally don't expect or verify that the
// toString() content is in some specific form.
assertTrue(actualToString.contains(expectedKey),
expectedKey + " missing in output of Headers.of().toString()");
final List<String> expectedVals = originalHeadersEntry.getValue();
for (final String val : expectedVals) {
assertTrue(actualToString.contains(val), val + " for header key "
+ expectedKey + " missing in output of Headers.of().toString()");
}
}
}
static final Class<UnsupportedOperationException> UOP = UnsupportedOperationException.class;
static void assertUnsupportedOperation(Headers headers) {
assertThrows(UOP, () -> headers.add("a", "b"));
assertThrows(UOP, () -> headers.compute("c", (k, v) -> List.of("c")));
assertThrows(UOP, () -> headers.computeIfAbsent("d", k -> List.of("d")));
assertThrows(UOP, () -> headers.computeIfPresent("Foo", (k, v) -> null));
assertThrows(UOP, () -> headers.merge("e", List.of("e"), (k, v) -> List.of("e")));
assertThrows(UOP, () -> headers.put("f", List.of("f")));
assertThrows(UOP, () -> headers.putAll(Map.of()));
assertThrows(UOP, () -> headers.putIfAbsent("g", List.of("g")));
assertThrows(UOP, () -> headers.remove("h"));
assertThrows(UOP, () -> headers.replace("i", List.of("i")));
assertThrows(UOP, () -> headers.replace("j", List.of("j"), List.of("j")));
assertThrows(UOP, () -> headers.replaceAll((k, v) -> List.of("k")));
assertThrows(UOP, () -> headers.set("l", "m"));
assertThrows(UOP, () -> headers.clear());
}
static void assertUnmodifiableCollection(Headers headers) {
var entry = new AbstractMap.SimpleEntry<>("n", List.of("n"));
assertThrows(UOP, () -> headers.values().remove(List.of("Bar")));
assertThrows(UOP, () -> headers.values().removeAll(List.of("Bar")));
assertThrows(UOP, () -> headers.keySet().remove("Foo"));
assertThrows(UOP, () -> headers.keySet().removeAll(List.of("Foo")));
assertThrows(UOP, () -> headers.entrySet().remove(entry));
assertThrows(UOP, () -> headers.entrySet().removeAll(List.of(entry)));
}
static void assertUnmodifiableList(Headers headers) {
assertThrows(UOP, () -> headers.get("Foo").remove(0));
assertThrows(UOP, () -> headers.get("foo").remove(0));
assertThrows(UOP, () -> headers.values().stream().findFirst().orElseThrow().remove(0));
assertThrows(UOP, () -> headers.entrySet().stream().findFirst().orElseThrow().getValue().remove(0));
}
static class TestHttpExchange extends StubHttpExchange {
final UnmodifiableHeaders headers;
TestHttpExchange(Headers headers) {
this.headers = new UnmodifiableHeaders(headers);
}
@Override
public Headers getRequestHeaders() {
return headers;
}
}
static class StubHttpExchange extends HttpExchange {
@Override public Headers getRequestHeaders() { return null; }
@Override public Headers getResponseHeaders() { return null; }
@Override public URI getRequestURI() { return null; }
@Override public String getRequestMethod() { return null; }
@Override public HttpContext getHttpContext() { return null; }
@Override public void close() { }
@Override public InputStream getRequestBody() { return null; }
@Override public OutputStream getResponseBody() { return null; }
@Override public void sendResponseHeaders(int rCode, long responseLength) { }
@Override public InetSocketAddress getRemoteAddress() { return null; }
@Override public int getResponseCode() { return 0; }
@Override public InetSocketAddress getLocalAddress() { return null; }
@Override public String getProtocol() { return null; }
@Override public Object getAttribute(String name) { return null; }
@Override public void setAttribute(String name, Object value) { }
@Override public void setStreams(InputStream i, OutputStream o) { }
@Override public HttpPrincipal getPrincipal() { return null; }
}
}

View file

@ -0,0 +1,277 @@
/*
* Copyright (c) 2005, 2022, 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.
*/
/**
* @test
* @bug 6725892
* @library /test/lib
* @run main/othervm -Dsun.net.httpserver.maxReqTime=2 -Dsun.net.httpserver.clockTick=2000 Test
* @run main/othervm -Djava.net.preferIPv6Addresses=true -Dsun.net.httpserver.maxReqTime=2 -Dsun.net.httpserver.clockTick=2000 Test
* @summary
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import jdk.test.lib.net.URIBuilder;
public class Test {
static HttpServer s1;
static int port;
static URL url;
static final String RESPONSE_BODY = "response";
static boolean failed = false;
static class Handler implements HttpHandler {
public void handle(HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
InetSocketAddress rem = t.getRemoteAddress();
System.out.println("Request from: " + rem);
while (is.read () != -1) ;
is.close();
String requrl = t.getRequestURI().toString();
OutputStream os = t.getResponseBody();
t.sendResponseHeaders(200, RESPONSE_BODY.length());
os.write(RESPONSE_BODY.getBytes());
t.close();
}
}
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
InetAddress loopback = InetAddress.getLoopbackAddress();
try {
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
s1 = HttpServer.create(addr, 100);
HttpHandler h = new Handler();
HttpContext c1 = s1.createContext("/", h);
s1.setExecutor(exec);
s1.start();
port = s1.getAddress().getPort();
System.out.println("Server on port " + port);
url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/foo")
.toURLUnchecked();
System.out.println("URL: " + url);
test1();
test2();
test3();
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
System.out.println("FAIL");
throw new RuntimeException();
} finally {
s1.stop(0);
System.out.println("After Shutdown");
exec.shutdown();
}
}
// open TCP connection without sending anything. Check server closes it.
static void test1() throws IOException {
failed = false;
Socket s = new Socket(InetAddress.getLoopbackAddress(), port);
InputStream is = s.getInputStream();
// server should close connection after 2 seconds. We wait up to 10
s.setSoTimeout(10000);
try {
is.read();
} catch (SocketTimeoutException e) {
failed = true;
}
s.close();
if (failed) {
System.out.println("test1: FAIL");
throw new RuntimeException();
} else {
System.out.println("test1: OK");
}
}
// send request and don't read response. Check server closes connection
static void test2() throws IOException {
HttpURLConnection urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
urlc.setReadTimeout(20 * 1000);
InputStream is = urlc.getInputStream();
// we won't read response and check if it times out
// on server. If it timesout at client then there is a problem
try {
Thread.sleep(10 * 1000);
while (is.read() != -1) ;
} catch (InterruptedException e) {
System.out.println(e);
System.out.println("test2: FAIL");
throw new RuntimeException("unexpected error");
} catch (SocketTimeoutException e1) {
System.out.println(e1);
System.out.println("test2: FAIL");
throw new RuntimeException("client timedout");
} finally {
is.close();
}
System.out.println("test2: OK");
}
// same as test2, but repeated with multiple connections
// including a number of valid request/responses
// Worker: a thread opens a connection to the server in one of three modes.
// NORMAL - sends a request, waits for response, and checks valid response
// REQUEST - sends a partial request, and blocks, to see if
// server closes the connection.
// RESPONSE - sends a request, partially reads response and blocks,
// to see if server closes the connection.
static class Worker extends Thread {
CountDownLatch latch;
Mode mode;
enum Mode {
REQUEST, // block during sending of request
RESPONSE, // block during reading of response
NORMAL // don't block
};
Worker (CountDownLatch latch, Mode mode) {
this.latch = latch;
this.mode = mode;
}
void fail(String msg) {
System.out.println(msg);
failed = true;
}
public void run() {
HttpURLConnection urlc;
InputStream is = null;
try {
urlc = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
urlc.setReadTimeout(20 * 1000);
urlc.setDoOutput(true);
} catch (IOException e) {
fail("Worker: failed to connect to server");
latch.countDown();
return;
}
try {
OutputStream os = urlc.getOutputStream();
os.write("foo".getBytes());
if (mode == Mode.REQUEST) {
Thread.sleep(3000);
}
os.close();
is = urlc.getInputStream();
if (mode == Mode.RESPONSE) {
Thread.sleep(3000);
}
if (!checkResponse(is, RESPONSE_BODY)) {
fail("Worker: response");
}
is.close();
return;
} catch (InterruptedException e0) {
fail("Worker: timedout");
} catch (SocketTimeoutException e1) {
fail("Worker: timedout");
} catch (IOException e2) {
switch (mode) {
case NORMAL:
fail("Worker: " + e2.getMessage());
break;
case RESPONSE:
if (is == null) {
fail("Worker: " + e2.getMessage());
break;
}
// default: is ok
}
} finally {
latch.countDown();
}
}
}
static final int NUM = 20;
static void test3() throws Exception {
failed = false;
CountDownLatch l = new CountDownLatch(NUM*3);
Worker[] workers = new Worker[NUM*3];
for (int i=0; i<NUM; i++) {
workers[i*3] = new Worker(l, Worker.Mode.NORMAL);
workers[i*3+1] = new Worker(l, Worker.Mode.REQUEST);
workers[i*3+2] = new Worker(l, Worker.Mode.RESPONSE);
workers[i*3].start();
workers[i*3+1].start();
workers[i*3+2].start();
}
l.await();
for (int i=0; i<NUM*3; i++) {
workers[i].join();
}
if (failed) {
throw new RuntimeException("test3: failed");
}
System.out.println("test3: OK");
}
static boolean checkResponse(InputStream is, String resp) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[64];
int c;
while ((c=is.read(buf)) != -1) {
bos.write(buf, 0, c);
}
bos.close();
if (!bos.toString().equals(resp)) {
System.out.println("Wrong response: " + bos.toString());
return false;
}
} catch (IOException e) {
System.out.println(e);
return false;
}
return true;
}
}

View file

@ -0,0 +1,192 @@
/*
* Copyright (c) 2019, 2025, 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.
*/
/**
* @test
* @bug 8199849
* @library /test/lib
* @run main/othervm/timeout=6000 BasicAuthenticatorCharset
* @summary Check for correct use of character sets with BasicAuthenticator() authentication
*/
import com.sun.net.httpserver.*;
import jdk.test.lib.net.URIBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.nio.charset.Charset;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* Test authentication
*/
public class BasicAuthenticatorCharset {
public static volatile int failCount = 0;
static final String TEST_USER = "test";
static final String UNICODE_PW = "Selam D\u00fcnya. Ho\u015f\u00e7akal D\u00fcnya";
static Handler testHandler;
static HttpServer testHttpServer;
static java.net.Authenticator clientAuth;
static HttpClient client;
static class Handler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
InputStream is = t.getRequestBody();
while (is.read() != -1) ;
is.close();
t.sendResponseHeaders(200, RSPBODY_EMPTY);
t.close();
}
}
static class ClientAuthenticator extends java.net.Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(TEST_USER, UNICODE_PW.toCharArray());
}
}
static void setAuthenticationPW(String path, String realm, String testPW, Charset charset) {
HttpContext ctx = testHttpServer.createContext(path, testHandler);
BasicAuthenticator auth;
if (charset != null) {
auth = new BasicAuthenticator(realm, charset) {
public boolean checkCredentials(String username, String pw) {
return username.equals(TEST_USER) && pw.equals(testPW);
}
};
} else {
auth = new BasicAuthenticator(realm) {
public boolean checkCredentials(String username, String pw) {
return username.equals(TEST_USER) && pw.equals(testPW);
}
};
}
ctx.setAuthenticator(auth);
}
static void connectAndAuth(String path, int expectedStatus) throws Exception {
// path is prepended with /old or /new for old and new http client
URL oldurl = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(testHttpServer.getAddress().getPort())
.path("/old" + path)
.toURL();
URI newuri = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(testHttpServer.getAddress().getPort())
.path("/new" + path)
.build();
// check old client
HttpURLConnection testConnection = (HttpURLConnection) oldurl.openConnection(Proxy.NO_PROXY);
// Check for successful authentication
int status = testConnection.getResponseCode();
if (status != 401) {
InputStream is = testConnection.getInputStream();
while (is.read() != -1) ;
is.close();
}
if (status != expectedStatus) {
System.err.println("Error (old): " + path);
failCount++;
}
HttpRequest request = HttpRequest.newBuilder()
.uri(newuri)
.GET()
.build();
status = -1;
try {
HttpResponse<Void> response = client.send(request, HttpResponse.BodyHandlers.discarding());
status = response.statusCode();
} catch (IOException e) {
System.out.println("NEW: " + e);
status = 401; // limitation in new API.
}
if (status != expectedStatus) {
System.err.println("Error (new): " + path);
failCount++;
}
}
public static void main(String[] args) throws Exception {
clientAuth = new ClientAuthenticator();
client = HttpClient.newBuilder()
.authenticator(clientAuth)
.build();
String defaultCharset = System.getProperty("file.encoding");
boolean isUTF8 = defaultCharset.equalsIgnoreCase("UTF-8");
testHandler = new Handler();
InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
testHttpServer = HttpServer.create(addr, 0);
// Set the passing credentials OLD client
setAuthenticationPW("/old/test1/", "passingCharset@test.realm", UNICODE_PW, UTF_8);
setAuthenticationPW("/old/test2/", "failingCharset@test.realm", UNICODE_PW, ISO_8859_1);
setAuthenticationPW("/old/test3/", "defaultCharset@test.realm", UNICODE_PW, null);
// Set the passing credentials NEW client
setAuthenticationPW("/new/test1/", "passingCharset@test.realm", UNICODE_PW, UTF_8);
setAuthenticationPW("/new/test2/", "failingCharset@test.realm", UNICODE_PW, ISO_8859_1);
setAuthenticationPW("/new/test3/", "defaultCharset@test.realm", UNICODE_PW, null);
ExecutorService executor = Executors.newCachedThreadPool();
testHttpServer.setExecutor(executor);
testHttpServer.start();
java.net.Authenticator.setDefault(clientAuth);
connectAndAuth("/test1/passingCharset.html", 200);
connectAndAuth("/test2/failingCharset.html", 401);
if (isUTF8) {
connectAndAuth("/test3/defaultCharset.html", 200);
}
testHttpServer.stop(0);
executor.shutdown();
// should fail once with UNICODE_PW and unsupporting character set
if (failCount > 0)
throw new RuntimeException("Fail count : " + failCount);
}
}

View file

@ -0,0 +1,230 @@
/*
* Copyright (c) 2019, 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.
*/
import java.io.*;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.*;
import java.nio.charset.StandardCharsets;
import jdk.test.lib.net.URIBuilder;
/**
* @test
* @bug 8199849 8235976
* @summary
* @library /test/lib
* @run main/othervm ParamTest
* @run main/othervm -Djava.net.preferIPv6Addresses=true ParamTest
*/
public class ParamTest {
static final String[] variants = {
" ,charset=utf-8",
" ,charset=UtF-8",
" ,charset=\"utF-8\"",
" ,charset=\"UtF-8\""
};
static final int LOOPS = variants.length;
volatile static boolean error = false;
static class BasicServer extends Thread {
final ServerSocket server;
Socket s;
InputStream is;
OutputStream os;
static final String realm = "wallyworld";
String reply1 = "HTTP/1.1 401 Unauthorized\r\n"+
"WWW-Authenticate: Basic realm=\""+realm+"\"\r\n";
String reply2 = "HTTP/1.1 200 OK\r\n"+
"Date: Mon, 15 Jan 2001 12:18:21 GMT\r\n" +
"Server: Apache/1.3.14 (Unix)\r\n" +
"Connection: close\r\n" +
"Content-Type: text/html; charset=iso-8859-1\r\n" +
"Content-Length: 10\r\n\r\n";
BasicServer(ServerSocket s) {
server = s;
}
String readHeaders(Socket sock) throws IOException {
InputStream is = sock.getInputStream();
String s = "";
byte[] buf = new byte[1024];
while (!s.endsWith("\r\n\r\n")) {
int c = is.read(buf);
if (c == -1)
return s;
String f = new String(buf, 0, c, StandardCharsets.ISO_8859_1);
s = s + f;
}
return s;
}
void check(String s, int iteration) {
if (s.indexOf(encodedAuthString) == -1) {
System.err.printf("On iteration %d, wrong auth string received %s\n", iteration, s);
error = true;
} else {
System.err.println("check: correct auth string received");
}
}
public void run() {
try {
for (int j = 0; j < 2; j++)
for (int i = 0; i < LOOPS; i++) {
System.out.println("Server 1: accept");
s = server.accept();
readHeaders(s);
System.out.println("accepted");
os = s.getOutputStream();
String str = reply1 + variants[i] + "\r\n\r\n";
os.write(str.getBytes());
System.out.println("Server 2: accept");
Socket s1 = server.accept();
String request = readHeaders(s1);
check(request, i);
System.out.println("accepted");
os = s1.getOutputStream();
os.write((reply2 + "HelloWorld").getBytes());
os.flush();
s.close();
s1.close();
finished();
}
} catch (Exception e) {
System.out.println(e);
error = true;
}
}
public synchronized void finished() {
notifyAll();
}
}
static final String password = "Selam D\u00fcnya.";
// "user : <password above>" encoded in UTF-8 and converted to Base 64
static final String encodedAuthString = "dXNlcjpTZWxhbSBEw7xueWEu";
static class MyAuthenticator extends Authenticator {
MyAuthenticator() {
super();
}
public PasswordAuthentication getPasswordAuthentication()
{
System.out.println("Auth called");
return (new PasswordAuthentication ("user", password.toCharArray()));
}
}
static void read(InputStream is) throws IOException {
int c;
System.out.println("reading");
while ((c=is.read()) != -1) {
System.out.write(c);
}
System.out.println("");
System.out.println("finished reading");
}
public static void main(String args[]) throws Exception {
MyAuthenticator auth = new MyAuthenticator();
Authenticator.setDefault(auth);
InetAddress loopback = InetAddress.getLoopbackAddress();
ServerSocket ss = new ServerSocket();
ss.bind(new InetSocketAddress(loopback, 0));
int port = ss.getLocalPort();
BasicServer server = new BasicServer(ss);
synchronized (server) {
server.start();
System.out.println("client 1");
String base = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/")
.build()
.toString();
URL url = new URL(base + "d1/d2/d3/foo.html");
for (int i = 0; i < LOOPS; i++) {
URLConnection urlc = url.openConnection(Proxy.NO_PROXY);
InputStream is = urlc.getInputStream();
read(is);
System.out.println("Client: waiting for notify");
server.wait();
System.out.println("Client: continue");
// check if authenticator was called once (ok) or twice (not)
if (error) {
System.err.println("Error old client iteration " + i);
}
}
URI uri = url.toURI();
HttpClient client = HttpClient.newBuilder()
.authenticator(auth)
.proxy(ProxySelector.of(null))
.build();
HttpRequest request = HttpRequest
.newBuilder(uri)
.GET()
.build();
for (int i = 0; i < LOOPS; i++) {
HttpResponse<Void> response = client.send(request, HttpResponse.BodyHandlers.discarding());
int status = response.statusCode();
if (status != 200) {
System.err.printf("Error new client (%d) iteration ",
status, i);
error = true;
} else
System.err.println("New client ok iteration " + i);
System.out.println("New Client: waiting for notify");
server.wait();
System.out.println("New Client: continue");
}
if (error) {
throw new RuntimeException("Test failed");
}
}
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2019, 2025, 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.
*/
/**
* @test
* @bug 8199849
* @library /test/lib
* @summary Checks that unicode bytes are being handled correctly
* @run main/othervm -Dfile.encoding=UTF_8 TestHttpUnicode
*/
import com.sun.net.httpserver.*;
import jdk.test.lib.net.URIBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
public class TestHttpUnicode {
private static final String TEST_USER = "Selam D\u00fcnya. Ho\u015f\u00e7akal D\u00fcnya";
private static final String TEST_PW = "Selam D\u00fcnya. Ho\u015f\u00e7akal D\u00fcnya";
static class ClientAuthenticator extends java.net.Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(TEST_USER, TEST_PW.toCharArray());
}
}
static class Handler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
InputStream is = t.getRequestBody();
while (is.read() != -1) ;
is.close();
HttpPrincipal p = t.getPrincipal();
if (p.getUsername().equals(TEST_USER)) {
t.sendResponseHeaders(200, RSPBODY_EMPTY);
}
t.close();
}
}
public static void main(String[] args) throws Exception {
HttpServer testHttpServer = null;
try {
InetSocketAddress loopbackAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
testHttpServer = HttpServer.create(loopbackAddress, 0);
HttpContext context = testHttpServer.createContext("/test", new Handler());
System.setProperty("http.maxRedirects", "3");
BasicAuthenticator serverAuthenticator = new BasicAuthenticator("authCharacterSet@test.realm") {
public boolean checkCredentials(String username, String pw) {
return username.equals(TEST_USER) && pw.equals(TEST_PW);
}
};
context.setAuthenticator(serverAuthenticator);
java.net.Authenticator.setDefault(new ClientAuthenticator());
testHttpServer.start();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(testHttpServer.getAddress().getPort())
.path("/test/authCharacterSet.html")
.toURL();
HttpURLConnection testConnection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
// Authenication CHECK
if (testConnection.getResponseCode() == 401) {
throw new RuntimeException("Test Authentication failed with HTTP Status 401.");
}
InputStream is = testConnection.getInputStream();
while (is.read() != -1) ;
} finally {
testHttpServer.stop(0);
}
}
}

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2023, 2025, 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.
*/
/**
* @test
* @bug 8300268
* @library /test/lib
* @modules jdk.httpserver/sun.net.httpserver
* @build jdk.httpserver/sun.net.httpserver.HttpServerAccess MaxIdleConnectionsTest
* @run junit/othervm -Dsun.net.httpserver.maxIdleConnections=4 MaxIdleConnectionsTest
*/
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import sun.net.httpserver.HttpServerAccess;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MaxIdleConnectionsTest {
HttpServer server;
int maxIdleConnections, totalConnections;
CountDownLatch reqFinishedProcessing;
@BeforeAll
void before() throws Exception {
maxIdleConnections = Integer.getInteger("sun.net.httpserver.maxIdleConnections");
totalConnections = maxIdleConnections + 1;
reqFinishedProcessing = new CountDownLatch(totalConnections);
server = startServer(reqFinishedProcessing);
}
@AfterAll
void after() throws Exception {
server.stop(0);
}
// Issue one too many requests and assert that the idle connection pool doesn't
// exceed maxIdleConnections
@Test
public void test() throws Exception {
final int port = server.getAddress().getPort();
final List<Future<Void>> responses = new ArrayList<>();
try (final ExecutorService requestIssuer = Executors.newFixedThreadPool(totalConnections)) {
for (int i = 1; i <= totalConnections; i++) {
URL requestURL = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/MaxIdleConnectionTest/" + i)
.toURL();
final Future<Void> result = requestIssuer.submit(() -> {
System.out.println("Issuing request " + requestURL);
final URLConnection conn = requestURL.openConnection();
try (final InputStream is = conn.getInputStream()) {
is.readAllBytes();
}
return null;
});
responses.add(result);
}
// wait for all the requests to reach each of the handlers
System.out.println("Waiting for all " + totalConnections + " requests to reach" +
" the server side request handler");
reqFinishedProcessing.await();
}
// verify every request got served before checking idle count
for (int i = 0; i < totalConnections; i++) {
responses.get(i).get();
System.out.println("Received successful response for request " + i);
}
// assert that the limit set by maxIdleConnections was not exceeded
int idleConnectionCount = HttpServerAccess.getIdleConnectionCount(server);
System.out.println("count " + idleConnectionCount);
assertTrue(maxIdleConnections >= idleConnectionCount,
String.format("Too many idle connections: %d, limit: %d", idleConnectionCount, maxIdleConnections));
}
// Create HttpServer that will handle requests with multiple threads
private static HttpServer startServer(final CountDownLatch reqFinishedProcessing) throws IOException {
final var bindAddr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
final HttpServer server = HttpServer.create(bindAddr, 0);
final AtomicInteger threadId = new AtomicInteger();
server.setExecutor(Executors.newCachedThreadPool(r -> {
final Thread t = new Thread(r);
t.setName("http-request-handler-" + threadId.incrementAndGet());
t.setDaemon(true);
return t;
}));
server.createContext("/MaxIdleConnectionTest/", (exchange) -> {
System.out.println("Request " + exchange.getRequestURI() + " received");
System.out.println("Sending response for request " + exchange.getRequestURI() + " from " + exchange.getRemoteAddress());
reqFinishedProcessing.countDown();
exchange.sendResponseHeaders(200, RSPBODY_CHUNKED);
exchange.getResponseBody().close();
});
server.start();
System.out.println("Server started at address " + server.getAddress());
return server;
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2023, 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 sun.net.httpserver;
import com.sun.net.httpserver.HttpServer;
import java.lang.reflect.Field;
import java.util.Set;
public class HttpServerAccess {
// Given a HttpServer object get the number of idleConnections it currently has
public static int getIdleConnectionCount(HttpServer server) throws Exception{
// Use reflection to get server object which is HTTPServerImpl
Field serverField = server.getClass().getDeclaredField("server");
serverField.setAccessible(true);
// Get the actual serverImpl class, then get the IdleConnection Field
Object serverImpl = serverField.get(server);
Field idleConnectionField = serverImpl.getClass().getDeclaredField("idleConnections");
idleConnectionField.setAccessible(true);
// Finally get the IdleConnection object which is of type Set<HttpConnection>
Object idleConnectionSet = idleConnectionField.get(serverImpl);
Set<HttpConnection> idleConnectionPool = (Set<HttpConnection>) idleConnectionSet;
return idleConnectionPool.size();
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2005, 2024, 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.
*/
/**
* @test
* @bug 6339483
* @summary NullPointerException when creating a HttpContext with no handler
* @library /test/lib
* @run main B6339483
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6339483
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
public class B6339483 {
public static void main (String[] args) throws Exception {
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test");
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
try {
InputStream is = urlc.getInputStream();
int c = 0;
while (is.read()!= -1) {
c ++;
}
} catch (IOException e) {
server.stop(0);
executor.shutdown();
System.out.println ("OK");
}
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6341616
* @library /test/lib
* @run main B6341616
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6341616
* @summary Server doesnt send response if there is a RuntimeException in validate of BasicAuthFilter
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
public class B6341616 {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
BasicAuthenticator filter = new BasicAuthenticator ("foobar@test.realm") {
public boolean checkCredentials (String username, String pw) {
throw new RuntimeException ("");
}
};
ctx.setAuthenticator (filter);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
java.net.Authenticator.setDefault (new MyAuthenticator());
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
try {
InputStream is = urlc.getInputStream();
int c = 0;
while (is.read()!= -1) {
c ++;
}
} catch (IOException e) {
server.stop(0);
executor.shutdown();
System.out.println ("OK");
}
}
public static boolean error = false;
static class MyAuthenticator extends java.net.Authenticator {
public PasswordAuthentication getPasswordAuthentication () {
PasswordAuthentication pw;
if (!getRequestingPrompt().equals ("foobar@test.realm")) {
B6341616.error = true;
}
pw = new PasswordAuthentication ("fred", "xyz".toCharArray());
return pw;
}
}
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read () != -1) ;
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
}

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2006, 2025, 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.
*/
/**
* @test
* @bug 6361557
* @run main/othervm B6361557
* @summary Lightweight HTTP server quickly runs out of file descriptors on Linux
*/
import com.sun.net.httpserver.*;
import java.util.*;
import java.util.concurrent.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* The test simply opens 1,000 separate connections
* and invokes one http request on each. The client does
* not close any sockets until after they are closed
* by the server. This verifies the basic ability
* of the server to manage a reasonable number of connections
*/
public class B6361557 {
public static boolean error = false;
static final int NUM = 1000;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read () != -1) ;
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
final static String request = "GET /test/foo.html HTTP/1.1\r\nContent-length: 0\r\n\r\n";
final static ByteBuffer requestBuf = ByteBuffer.wrap(request.getBytes());
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
InetSocketAddress destaddr = new InetSocketAddress (
loopback, server.getAddress().getPort()
);
System.out.println ("destaddr " + destaddr);
Selector selector = Selector.open ();
int requests = 0;
int responses = 0;
while (true) {
// we need to read responses from time to time: slightly
// increase the timeout with the amount of pending responses
// to give a chance to the server to reply.
int selres = selector.select (requests - responses + 1);
Set<SelectionKey> selkeys = selector.selectedKeys();
for (SelectionKey key : selkeys) {
if (key.isReadable()) {
SocketChannel chan = (SocketChannel)key.channel();
ByteBuffer buf = (ByteBuffer)key.attachment();
try {
int x = chan.read(buf);
if (x == -1 || responseComplete(buf)) {
System.out.print("_");
key.attach(null);
chan.close();
responses++;
}
} catch (IOException e) {
System.out.println(e);
}
}
}
if (requests < NUM) {
System.out.print(".");
SocketChannel schan = SocketChannel.open(destaddr);
requestBuf.rewind();
int c = 0;
while (requestBuf.remaining() > 0) {
c += schan.write(requestBuf);
}
schan.configureBlocking(false);
schan.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(100));
requests++;
}
if (responses == NUM) {
System.out.println ("Finished clients");
break;
}
}
server.stop(0);
selector.close();
executor.shutdown ();
}
/* Look for CR LF CR LF */
static boolean responseComplete(ByteBuffer buf) {
int pos = buf.position();
buf.flip();
byte[] lookingFor = new byte[] {'\r', '\n', '\r', '\n' };
int lookingForCount = 0;
while (buf.hasRemaining()) {
byte b = buf.get();
if (b == lookingFor[lookingForCount]) {
lookingForCount++;
if (lookingForCount == 4) {
return true;
}
} else {
lookingForCount = 0;
}
}
buf.position(pos);
buf.limit(buf.capacity());
return false;
}
}

View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 2006, 2019, 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.
*/
/**
* @test
* @bug 6373555
* @library /test/lib
* @summary HTTP Server failing to answer client requests
* @run main B6373555
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6373555
*/
import java.net.*;
import java.io.*;
import java.util.*;
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import jdk.test.lib.net.URIBuilder;
public class B6373555 {
private static int s_received = 0;
private static int sent = 0;
private static int received = 0;
private static int port;
private static volatile boolean error = false;
static HttpServer httpServer;
static ExecutorService pool, execs;
static int NUM = 1000;
public static void main(String[] args) throws Exception {
try {
if (args.length > 0) {
NUM = Integer.parseInt (args[0]);
}
execs = Executors.newFixedThreadPool(5);
httpServer = createHttpServer(execs);
port = httpServer.getAddress().getPort();
pool = Executors.newFixedThreadPool(10);
httpServer.start();
for (int i=0; i < NUM; i++) {
pool.execute(new Client());
if (error) {
throw new Exception ("error in test");
}
}
System.out.println("Main thread waiting");
pool.shutdown();
long latest = System.currentTimeMillis() + 200 * 1000;
while (System.currentTimeMillis() < latest) {
if (pool.awaitTermination(2000L, TimeUnit.MILLISECONDS)) {
System.out.println("Main thread done!");
return;
}
if (error) {
throw new Exception ("error in test");
}
}
throw new Exception ("error in test: timed out");
} finally {
httpServer.stop(0);
pool.shutdownNow();
execs.shutdownNow();
}
}
public static class Client implements Runnable {
byte[] getBuf () {
byte[] buf = new byte [5200];
for (int i=0; i< 5200; i++) {
buf [i] = (byte)i;
}
return buf;
}
public void run() {
try {
Thread.sleep(10);
byte[] buf = getBuf();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/test")
.toURLUnchecked();
System.out.println("URL: " + url);
HttpURLConnection con = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
con.setDoOutput(true);
con.setDoInput(true);
con.setRequestMethod("POST");
con.setRequestProperty(
"Content-Type",
"Multipart/Related; type=\"application/xop+xml\"; boundary=\"----=_Part_0_6251267.1128549570165\"; start-info=\"text/xml\"");
OutputStream out = con.getOutputStream();
out.write(buf);
out.close();
InputStream in = con.getInputStream();
byte[] newBuf = readFully(in);
in.close();
if (buf.length != newBuf.length) {
System.out.println("Doesn't match");
error = true;
}
}
catch(Exception e) {
e.printStackTrace();
System.out.print (".");
error = true;
}
}
}
private static byte[] readFully(InputStream istream) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int num = 0;
if (istream != null) {
while ((num = istream.read(buf)) != -1) {
bout.write(buf, 0, num);
}
}
byte[] ret = bout.toByteArray();
return ret;
}
private static HttpServer createHttpServer(ExecutorService execs)
throws Exception {
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress inetAddress = new InetSocketAddress(loopback, 0);
HttpServer testServer = HttpServer.create(inetAddress, 15);
testServer.setExecutor(execs);
HttpContext context = testServer.createContext("/test");
context.setHandler(new HttpHandler() {
public void handle(HttpExchange msg) {
try {
String method = msg.getRequestMethod();
if (method.equals("POST")) {
InputStream is = msg.getRequestBody();
byte[] buf = readFully(is);
is.close();
writePostReply(msg, buf);
} else {
System.out.println("****** METHOD not handled ***** "+method);
System.out.println("Received="+s_received);
}
}
catch(Exception e) {
e.printStackTrace();
}
finally {
msg.close();
}
}
}
);
return testServer;
}
private static void writePostReply(HttpExchange msg, byte[] buf)
throws Exception {
msg.getResponseHeaders().add("Content-Type", "text/xml");
msg.sendResponseHeaders(200, buf.length);
OutputStream out = msg.getResponseBody();
out.write(buf);
out.close();
}
}

View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2006, 2025, 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.
*/
/**
* @test
* @bug 6393710
* @library /test/lib
* @summary Non authenticated call followed by authenticated call never returns
* @run main B6393710
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6393710
*/
import com.sun.net.httpserver.*;
import java.util.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.Utils;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* Test checks for following bug(s) when a POST containing a request body
* needs to be authenticated
*
* 1) we were not reading the request body
*
* 2) we were not re-enabling the interestops for the socket channel
*/
public class B6393710 {
static String CRLF = "\r\n";
/* Two post requests containing data. The second one
* has the expected authorization credentials
*/
static String cmd =
"POST /test/foo HTTP/1.1"+CRLF+
"Content-Length: 22"+CRLF+
"Pragma: no-cache"+CRLF+
"Cache-Control: no-cache"+CRLF+ CRLF+
"<item desc=\"excuse\" />"+
"POST /test/foo HTTP/1.1"+CRLF+
"Content-Length: 22"+CRLF+
"Pragma: no-cache"+CRLF+
"Authorization: Basic ZnJlZDpmcmVkcGFzc3dvcmQ="+CRLF+
"Cache-Control: no-cache"+CRLF+ CRLF+
"<item desc=\"excuse\" />";
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ctx.setAuthenticator (new BasicAuthenticator ("test") {
public boolean checkCredentials (String user, String pass) {
return user.equals ("fred") && pass.equals("fredpassword");
}
});
server.start ();
Socket s = new Socket (loopback, server.getAddress().getPort());
s.setSoTimeout ((int) Utils.adjustTimeout(5000));
OutputStream os = s.getOutputStream();
os.write (cmd.getBytes());
InputStream is = s.getInputStream ();
try {
ok = readAndCheck (is, "401 Unauthorized") &&
readAndCheck (is, "200 OK");
} catch (SocketTimeoutException e) {
System.out.println ("Did not received expected data");
ok = false;
} finally {
s.close();
server.stop(0);
}
if (requests != 1) {
throw new RuntimeException ("server handler did not receive the request");
}
if (!ok) {
throw new RuntimeException ("did not get 200 OK");
}
System.out.println ("OK");
}
/* check for expected string and return true if found in stream */
static boolean readAndCheck (InputStream is, String expected) throws IOException {
int c;
int count = 0;
int expLen = expected.length();
expected = expected.toLowerCase();
while ((c=is.read()) != -1) {
c = Character.toLowerCase (c);
if (c == expected.charAt (count)) {
count ++;
if (count == expLen) {
return true;
}
} else {
count = 0;
}
}
return false;
}
public static volatile boolean ok = false;
static volatile int requests = 0;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
int count = 0;
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read () != -1) {
count ++;
}
if (count != 22) {
System.out.println ("Handler expected 22. got " + count);
ok = false;
}
is.close();
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
requests ++;
}
}
}

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2006, 2025, 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.
*/
/**
* @test
* @library /test/lib
* @bug 6401598
* @summary new HttpServer cannot serve binary stream data
* @run main B6401598
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6401598
*/
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.*;
import jdk.test.lib.net.URIBuilder;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
public class B6401598 {
static class MyHandler implements HttpHandler {
public MyHandler() {
}
public void handle(HttpExchange arg0) throws IOException {
try {
InputStream is = arg0.getRequestBody();
OutputStream os = arg0.getResponseBody();
DataInputStream dis = new DataInputStream(is);
short input = dis.readShort();
while (dis.read() != -1) ;
dis.close();
DataOutputStream dos = new DataOutputStream(os);
arg0.sendResponseHeaders(200, RSPBODY_CHUNKED);
dos.writeShort(input);
dos.flush();
dos.close();
} catch (IOException e) {
e.printStackTrace();
error = true;
}
}
}
static int port;
static boolean error = false;
static ExecutorService exec;
static HttpServer server;
public static void main(String[] args) {
try {
InetAddress loopback = InetAddress.getLoopbackAddress();
server = HttpServer.create(new InetSocketAddress(loopback, 0), 400);
server.createContext("/server/", new MyHandler());
exec = Executors.newFixedThreadPool(3);
server.setExecutor(exec);
port = server.getAddress().getPort();
server.start();
short counter;
for (counter = 0; counter < 1000; counter++) {
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/server/")
.toURLUnchecked();
System.out.println("URL: " + url);
HttpURLConnection connection = getHttpURLConnection(url, 10000);
OutputStream os = connection.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeShort(counter);
dos.flush();
dos.close();
counter++;
InputStream is = connection.getInputStream();
DataInputStream dis = new DataInputStream(is);
short ret = dis.readShort();
dis.close();
}
System.out.println ("Stopping");
} catch (Exception e) {
throw new AssertionError("Unexpected exception: " + e, e);
} finally {
server.stop(0);
exec.shutdown();
}
}
static HttpURLConnection getHttpURLConnection(URL url, int timeout) throws IOException {
HttpURLConnection httpURLConnection =
(HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
httpURLConnection.setConnectTimeout(40000);
httpURLConnection.setReadTimeout(timeout);
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
httpURLConnection.setUseCaches(false);
httpURLConnection.setAllowUserInteraction(false);
httpURLConnection.setRequestMethod("POST");
// HttpURLConnection httpURLConnection = new MyHttpURLConnection(url);
return httpURLConnection;
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2006, 2025, 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.
*/
/**
* @test
* @bug 6421581
* @summary NPE while closing HttpExchange.getResonseBody()
*/
import com.sun.net.httpserver.*;
import java.net.*;
import java.io.*;
import java.util.concurrent.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
public class B6421581 {
static boolean error = false;
static int iter = 0;
public static void main(String[] args) throws Exception {
once();
}
public static void once() throws Exception {
InetSocketAddress inetAddress = new InetSocketAddress(
"localhost", 0);
HttpServer server = HttpServer.create(inetAddress, 5);
int port = server.getAddress().getPort();
ExecutorService e = (Executors.newFixedThreadPool(5));
server.setExecutor(e);
HttpContext context = server.createContext("/hello");
server.start();
context.setHandler(new HttpHandler() {
public void handle(HttpExchange msg) {
iter ++;
System.out.println("Got request");
switch (iter) {
case 1:
/* close output stream without opening inpustream */
/* chunked encoding */
try {
msg.sendResponseHeaders(200, RSPBODY_CHUNKED);
OutputStream out = msg.getResponseBody();
out.write("hello".getBytes());
out.close();
} catch(Exception e) {
error = true;
} finally {
msg.close();
}
break;
case 2:
/* close output stream without opening inpustream */
/* fixed encoding */
try {
msg.sendResponseHeaders(200, 5);
OutputStream out = msg.getResponseBody();
out.write("hello".getBytes());
out.close();
} catch(Exception e) {
error = true;
} finally {
msg.close();
}
break;
case 3:
/* close exchange without opening any stream */
try {
msg.sendResponseHeaders(200, RSPBODY_EMPTY);
msg.close();
} catch(Exception e) {
error = true;
}
break;
}
}
});
URL url = new URL("http://localhost:"+port+"/hello/url.text");
doURL(url);
doURL(url);
doURL(url);
e.shutdown();
e.awaitTermination(4, TimeUnit.SECONDS);
server.stop(0);
if (error) {
throw new RuntimeException ("test failed");
}
}
static void doURL (URL url) throws Exception {
InputStream is = url.openStream();
while (is.read() != -1) ;
is.close();
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2006, 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.
*/
/**
* @test
* @bug 6424196
* @summary PIT build 85 mustang: two jhttp tests fail
*/
import java.net.InetSocketAddress;
import java.io.IOException;
import com.sun.net.httpserver.*;
public class B6424196 {
public static void main(String[] args) throws IOException {
HttpServer server = HttpServer.create();
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2006, 2025, 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.
*/
/**
* @test
* @bug 6431193
* @summary The new HTTP server exits immediately
* @requires test.thread.factory != "Virtual"
* @library /test/lib
* @run main B6431193
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6431193
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import com.sun.net.httpserver.*;
public class B6431193 {
static boolean handlerIsDaemon = true;
public static void main(String[] args) throws IOException {
class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
try (InputStream is = t.getRequestBody();
OutputStream os = t.getResponseBody()) {
is.readAllBytes();
// .. read the request body
String response = "This is the response";
handlerIsDaemon = Thread.currentThread().isDaemon();
t.sendResponseHeaders(200, response.length());
os.write(response.getBytes());
}
}
}
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer server = HttpServer.create(new InetSocketAddress(loopback, 0), 10);
server.createContext("/apps", new MyHandler());
server.setExecutor(null);
server.start();
try {
int port = server.getAddress().getPort();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/apps/foo")
.toURL();
try (InputStream is = url.openConnection(Proxy.NO_PROXY).getInputStream()) {
is.readAllBytes();
}
if (handlerIsDaemon) {
throw new RuntimeException("request was handled by a daemon thread");
}
} catch (Exception e) {
throw new AssertionError("Unexpected exception: " + e, e);
} finally {
server.stop(0);
}
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2006, 2025, 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.
*/
/**
* @test
* @bug 6433018
* @summary HTTP server sometimes sends bad request for browsers javascript
* @run main B6433018
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6433018
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
public class B6433018 {
static final String CRLF = "\r\n";
/* invalid HTTP POST with extra CRLF at end */
/* This checks that the server is able to handle it
* and recognise the second request */
static final String cmd =
"POST /test/item HTTP/1.1"+CRLF+
"Keep-Alive: 300"+CRLF+
"Proxy-Connection: keep-alive"+CRLF+
"Content-Type: text/xml"+CRLF+
"Content-Length: 22"+CRLF+
"Pragma: no-cache"+CRLF+
"Cache-Control: no-cache"+CRLF+ CRLF+
"<item desc=\"excuse\" />"+CRLF+
"GET /test/items HTTP/1.1"+CRLF+
"Host: araku:9999"+CRLF+
"Accept-Language: en-us,en;q=0.5"+CRLF+
"Accept-Encoding: gzip,deflate"+CRLF+
"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"+CRLF+
"Keep-Alive: 300"+CRLF+
"Proxy-Connection: keep-alive"+CRLF+
"Pragma: no-cache"+CRLF+
"Cache-Control: no-cache"+CRLF+CRLF;
public static void main(String[] args) throws Exception {
CountDownLatch finished = new CountDownLatch(2);
Handler handler = new Handler(finished);
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create(addr, 0);
HttpContext ctx = server.createContext("/test", handler);
server.start();
int port = server.getAddress().getPort();
try (Socket s = new Socket(loopback, port);
OutputStream os = s.getOutputStream()) {
os.write(cmd.getBytes());
finished.await(30, TimeUnit.SECONDS);
} finally {
server.stop(0);
}
if (finished.getCount() != 0)
throw new RuntimeException("did not receive the 2 requests");
System.out.println("OK");
}
static class Handler implements HttpHandler {
private final CountDownLatch finished;
Handler(CountDownLatch finished) {
this.finished = finished;
}
@Override
public void handle(HttpExchange t) throws IOException {
try (InputStream is = t.getRequestBody()) {
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read() != -1);
}
t.sendResponseHeaders(200, RSPBODY_EMPTY);
t.close();
finished.countDown();
}
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2007, 2025, 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.
*/
/**
* @test
* @bug 6526158
* @library /test/lib
* @run main B6526158
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6526158
* @summary HttpExchange.getRequestBody().close() throws Exception
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
public class B6526158 {
/* keep under 64 k */
final static int SIZE = 60 * 1024;
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
urlc.setDoOutput (true);
try {
OutputStream os = new BufferedOutputStream (urlc.getOutputStream());
for (int i=0; i< SIZE; i++) {
os.write (i);
}
os.close();
InputStream is = urlc.getInputStream();
int c = 0;
while (is.read()!= -1) {
c ++;
}
is.close();
} finally {
server.stop(0);
executor.shutdown();
}
if (error) {
throw new RuntimeException ("Test failed");
}
}
public static boolean error = false;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
error = true;
}
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
}
}
}

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2007, 2025, 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.
*/
/**
* @test
* @bug 6526913
* @library /test/lib
* @run main/othervm -Dhttp.keepAlive=false B6526913
* @run main/othervm -Djava.net.preferIPv6Addresses=true
* -Dhttp.keepAlive=false B6526913
* @summary HttpExchange.getResponseBody().close() throws Exception
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
public class B6526913 {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
HttpURLConnection urlc = (HttpURLConnection)url.openConnection (Proxy.NO_PROXY);
try {
InputStream is = urlc.getInputStream();
int c ,count = 0;
byte [] buf = new byte [32 * 1024];
while (count < 32 * 1024) {
count += is.read (buf);
}
is.close();
} finally {
server.stop(0);
executor.shutdown();
}
if (error) {
throw new RuntimeException ("Test failed");
}
}
public static boolean error = false;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
try {
while (is.read() != -1) ;
is.close();
} catch (IOException e) {
e.printStackTrace();
error = true;
}
/* send a chunked response, but wait a while before
* sending the final empty chunk
*/
t.sendResponseHeaders (200, RSPBODY_CHUNKED);
OutputStream os = t.getResponseBody();
byte[] bb = new byte [32 * 1024];
os.write (bb);
os.flush();
try {Thread.sleep (5000); } catch (InterruptedException e){}
try {
/* empty chunk sent here */
os.close();
} catch (IOException e) {
error = true;
e.printStackTrace();
}
t.close();
}
}
}

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2007, 2025, 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.
*/
/**
* @test
* @bug 6529200
* @run main/othervm B6529200
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6529200
* @summary lightweight http server does not work with http1.0 clients
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
public class B6529200 {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
/* test 1: keep-alive */
Socket sock = new Socket (loopback, server.getAddress().getPort());
OutputStream os = sock.getOutputStream();
System.out.println ("GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n");
os.write ("GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n".getBytes());
os.flush();
InputStream is = sock.getInputStream();
StringBuffer s = new StringBuffer();
boolean finished = false;
sock.setSoTimeout (10 * 1000);
try {
while (!finished) {
char c = (char) is.read();
s.append (c);
finished = s.indexOf ("\r\n\r\nhello") != -1;
/* test will timeout otherwise */
}
} catch (SocketTimeoutException e) {
server.stop(0);
executor.shutdown ();
throw new RuntimeException ("Test failed in test1");
}
System.out.println (new String (s));
/* test 2: even though we request keep-alive, server must close
* because it is sending unknown content length response */
System.out.println("GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n");
os.write ("GET /test/foo HTTP/1.0\r\nConnection: keep-alive\r\n\r\n".getBytes());
os.flush();
int i=0,c;
byte [] buf = new byte [8*1024];
try {
while ((c=is.read()) != -1) {
buf[i++] = (byte)c;
}
} catch (SocketTimeoutException e) {
server.stop(0);
executor.shutdown ();
throw new RuntimeException ("Test failed in test2");
}
String ss = new String (buf, "ISO-8859-1");
if (ss.indexOf ("\r\n\r\nhello world") == -1) {
server.stop(0);
executor.shutdown ();
throw new RuntimeException ("Test failed in test2: wrong string");
}
System.out.println (ss);
is.close ();
server.stop(0);
executor.shutdown();
}
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is;
OutputStream os;
switch (invocation++) {
case 1:
is = t.getRequestBody();
while (is.read() != -1) ;
is.close();
t.sendResponseHeaders (200, "hello".length());
os = t.getResponseBody();
os.write ("hello".getBytes());
os.close();
break;
case 2:
is = t.getRequestBody();
while (is.read() != -1) ;
is.close();
t.sendResponseHeaders (200, RSPBODY_CHUNKED);
os = t.getResponseBody();
os.write ("hello world".getBytes());
os.close();
break;
}
}
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2005, 2025, 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.
*/
/**
* @test
* @bug 6744329
* @summary Exception in light weight Http server
* @library /test/lib
* @run main B6744329
* @run main/othervm -Djava.net.preferIPv6Addresses=true B6744329
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
public class B6744329 {
public static void main (String[] args) throws Exception {
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
try {
InputStream is = urlc.getInputStream();
int c = 0;
while (is.read()!= -1) {
c ++;
}
System.out.println ("OK");
} catch (IOException e) {
System.out.println ("exception");
e.printStackTrace();
error = true;
}
server.stop(0);
executor.shutdown();
if (error) {
throw new RuntimeException ("Test failed");
}
}
public static boolean error = false;
/* this must be the same size as in ChunkedOutputStream.java
*/
final static int CHUNK_SIZE = 4096;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read () != -1) ;
is.close();
/* chunked response */
t.sendResponseHeaders (200, RSPBODY_CHUNKED);
OutputStream os = t.getResponseBody();
byte[] first = new byte [CHUNK_SIZE * 2];
byte[] second = new byte [2];
os.write (first);
os.write ('x');
os.write ('x');
/* An index out of bounds exception will be thrown
* below, which is caught by server, and connection
* will be closed. resulting in IOException to client
* - if bug present
*/
os.write ('x');
os.write ('x');
os.write ('x');
t.close();
}
}
}

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2009, 2026, 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.
*/
import com.sun.net.httpserver.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;
/*
* @test
* @bug 6886436
* @summary HttpServer should not send a body with 204 response.
* @library /test/lib
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run main/othervm ${test.main.class}
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
*/
public class B6886436 {
private static final Logger logger = Logger.getLogger ("com.sun.net.httpserver");
private static void setupLogging() {
final ConsoleHandler c = new ConsoleHandler();
c.setLevel(Level.WARNING);
logger.addHandler(c);
logger.setLevel(Level.WARNING);
}
public static void main (String[] args) throws Exception {
setupLogging(); // merely for debugging
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress (loopback, 0);
HttpServer server = HttpServer.create (addr, 0);
HttpContext ctx = server.createContext ("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor (executor);
server.start ();
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
try {
InputStream is = urlc.getInputStream();
while (is.read()!= -1) ;
is.close ();
urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
urlc.setReadTimeout (3000);
is = urlc.getInputStream();
while (is.read()!= -1);
is.close ();
} catch (IOException e) {
server.stop(0);
executor.shutdown();
throw new RuntimeException ("Test failed");
}
server.stop(0);
executor.shutdown();
System.out.println ("OK");
}
public static boolean error = false;
static class Handler implements HttpHandler {
int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read () != -1) ;
is.close();
// send a 204 response with an empty chunked body
t.sendResponseHeaders (204, RSPBODY_CHUNKED);
t.close();
}
}
}

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2018, 2026, 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.
*/
import com.sun.net.httpserver.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.io.*;
import java.net.*;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* @test
* @bug 8211420
* @library /test/lib
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run main/othervm ${test.main.class}
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
* @summary
*/
public class B8211420 {
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
private static void setupLogging() {
final ConsoleHandler c = new ConsoleHandler();
c.setLevel(Level.WARNING);
logger.addHandler(c);
logger.setLevel(Level.WARNING);
}
public static void main(String[] args) throws Exception {
setupLogging(); // merely for debugging
Handler handler = new Handler();
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create(addr, 0);
HttpContext ctx = server.createContext("/test", handler);
ExecutorService executor = Executors.newCachedThreadPool();
server.setExecutor(executor);
server.start();
URL url = URIBuilder.newBuilder()
.scheme("http")
.host(server.getAddress().getAddress())
.port(server.getAddress().getPort())
.path("/test/foo.html")
.toURL();
HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
try {
InputStream is = urlc.getInputStream();
while (is.read()!= -1) ;
is.close ();
String prop = urlc.getHeaderField("Content-length");
System.out.println("Content-length = " + prop + " should be null");
if (prop != null)
throw new RuntimeException("Content-length was present");
urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
is = urlc.getInputStream();
while (is.read()!= -1) ;
is.close();
if (urlc.getResponseCode() != 304) // expected for 2nd test
throw new RuntimeException("wrong response code");
String clen = urlc.getHeaderField("Content-length");
System.out.println("Content-length = " + clen + " should be 99");
System.out.println("len = " + clen.length());
if (clen == null || !clen.equals("99"))
throw new RuntimeException("Content-length not present or has wrong value");
System.out.println("OK");
} finally {
server.stop(0);
executor.shutdown();
}
}
public static boolean error = false;
static class Handler implements HttpHandler {
volatile int invocation = 1;
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
Headers map = t.getRequestHeaders();
Headers rmap = t.getResponseHeaders();
while (is.read() != -1) ;
is.close();
if (invocation++ == 1) {
// send a 204 response with no body
t.sendResponseHeaders(204, RSPBODY_EMPTY);
t.close();
} else {
// send a 304 response with no body but with content - length
rmap.add("Content-length", "99");
t.sendResponseHeaders(304, RSPBODY_EMPTY);
t.close();
}
}
}
}

View file

@ -0,0 +1,108 @@
/*
* Copyright (c) 2019, 2025, 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.
*/
/*
* @test
* @bug 8230159 8275534
* @library /test/lib
* @summary Ensure that correct exceptions are being thrown in
* BasicAuthenticator constructor
* @run junit BasicAuthenticatorExceptionCheck
*/
import java.nio.charset.Charset;
import com.sun.net.httpserver.BasicAuthenticator;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class BasicAuthenticatorExceptionCheck {
static final Class<NullPointerException> NPE = NullPointerException.class;
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
static BasicAuthenticator createBasicAuthenticator(String realm, Charset charset) {
return new BasicAuthenticator(realm, charset) {
public boolean checkCredentials(String username, String pw) {
return true;
}
};
}
static BasicAuthenticator createBasicAuthenticator(String realm) {
return new BasicAuthenticator(realm) {
public boolean checkCredentials(String username, String pw) {
return true;
}
};
}
@Test
public void testAuthenticationException() {
Throwable ex = Assertions.assertThrows(NPE, () ->
createBasicAuthenticator("/test", null));
System.out.println("Valid realm and Null charset provided - " +
"NullPointerException thrown as expected: " + ex);
ex = Assertions.assertThrows(NPE, () ->
createBasicAuthenticator(null, UTF_8));
System.out.println("Null realm and valid charset provided - " +
"NullPointerException thrown as expected: " + ex);
ex = Assertions.assertThrows(IAE, () ->
createBasicAuthenticator("", UTF_8));
assertEquals("realm must not be empty", ex.getMessage());
System.out.println("Empty string for realm and valid charset provided - " +
"IllegalArgumentException thrown as expected: " + ex);
ex = Assertions.assertThrows(NPE, () ->
createBasicAuthenticator(null));
System.out.println("Null realm provided - " +
"NullPointerException thrown as expected: " + ex);
ex = Assertions.assertThrows(IAE, () ->
createBasicAuthenticator(""));
assertEquals("realm must not be empty", ex.getMessage());
System.out.println("Empty string for realm provided - " +
"IllegalArgumentException thrown as expected: " + ex);
ex = Assertions.assertThrows(IAE, () ->
createBasicAuthenticator("\"/test\""));
assertEquals("realm invalid: \"/test\"", ex.getMessage());
System.out.println("Invalid string for realm provided - " +
"IllegalArgumentException thrown as expected: " + ex);
ex = Assertions.assertThrows(IAE, () ->
createBasicAuthenticator("\""));
assertEquals("realm invalid: \"", ex.getMessage());
System.out.println("Invalid string for realm provided - " +
"IllegalArgumentException thrown as expected: " + ex);
createBasicAuthenticator("\\\"/test\\\"");
}
}

View file

@ -0,0 +1,134 @@
/*
* Copyright (c) 2023, 2026, 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.
*/
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* @test
* @bug 8219083
* @summary Exceptions thrown from HttpHandler.handle should not close connection
* if response is completed
* @library /test/lib
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run junit/othervm ${test.main.class}
*/
public class ExceptionKeepAlive {
public static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
private static void setupLogging() {
final Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINEST);
LOGGER.setLevel(Level.FINEST);
LOGGER.addHandler(handler);
}
@Test
void test() throws IOException, InterruptedException {
HttpServer httpServer = startHttpServer();
int port = httpServer.getAddress().getPort();
try {
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/firstCall")
.toURLUnchecked();
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
int responseCode = uc.getResponseCode();
assertEquals(200, responseCode, "First request should succeed");
URL url2 = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/secondCall")
.toURLUnchecked();
HttpURLConnection uc2 = (HttpURLConnection)url2.openConnection(Proxy.NO_PROXY);
responseCode = uc2.getResponseCode();
assertEquals(200, responseCode, "Second request should reuse connection");
} finally {
httpServer.stop(0);
}
}
/**
* Http Server
*/
HttpServer startHttpServer() throws IOException {
setupLogging(); // merely for debugging
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
httpServer.createContext("/", new MyHandler());
httpServer.start();
return httpServer;
}
class MyHandler implements HttpHandler {
volatile int port1;
@Override
public void handle(HttpExchange t) throws IOException {
String path = t.getRequestURI().getPath();
if (path.equals("/firstCall")) {
port1 = t.getRemoteAddress().getPort();
System.out.println("First connection on client port = " + port1);
// send response
t.sendResponseHeaders(200, RSPBODY_EMPTY);
// response is completed now; throw exception
throw new NumberFormatException();
// the connection should still be reusable
} else if (path.equals("/secondCall")) {
int port2 = t.getRemoteAddress().getPort();
System.out.println("Second connection on client port = " + port2);
if (port1 == port2) {
t.sendResponseHeaders(200, RSPBODY_EMPTY);
} else {
t.sendResponseHeaders(500, RSPBODY_EMPTY);
}
}
t.close();
}
}
}

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2008, 2026, 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.
*/
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.util.logging.*;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* @test
* @bug 6756771 6755625
* @summary com.sun.net.httpserver.HttpServer should handle POSTs larger than 2Gig
* @library /test/lib
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run main/othervm ${test.main.class}
* @run main/othervm -Djava.net.preferIPv6Addresses=true ${test.main.class}
*/
public class FixedLengthInputStream {
static final long POST_SIZE = 4L * 1024L * 1024L * 1024L; // 4Gig
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
void test(String[] args) throws IOException {
HttpServer httpServer = startHttpServer();
int port = httpServer.getAddress().getPort();
try {
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/flis/")
.toURLUnchecked();
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
uc.setDoOutput(true);
uc.setRequestMethod("POST");
uc.setFixedLengthStreamingMode(POST_SIZE);
OutputStream os = uc.getOutputStream();
/* create a 32K byte array with data to POST */
int thirtyTwoK = 32 * 1024;
byte[] ba = new byte[thirtyTwoK];
for (int i =0; i<thirtyTwoK; i++)
ba[i] = (byte)i;
long times = POST_SIZE / thirtyTwoK;
for (int i=0; i<times; i++) {
os.write(ba);
}
os.close();
InputStream is = uc.getInputStream();
while(is.read(ba) != -1);
is.close();
pass();
} finally {
httpServer.stop(0);
}
}
private static void setupLogging() {
final ConsoleHandler handler = new ConsoleHandler();
handler.setLevel(Level.FINEST);
logger.setLevel(Level.FINEST);
logger.addHandler(handler);
}
/**
* Http Server
*/
HttpServer startHttpServer() throws IOException {
if (debug) {
setupLogging(); // merely for debugging
}
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
httpServer.createContext("/flis/", new MyHandler(POST_SIZE));
httpServer.start();
return httpServer;
}
class MyHandler implements HttpHandler {
static final int BUFFER_SIZE = 32 * 1024;
long expected;
MyHandler(long expected){
this.expected = expected;
}
@Override
public void handle(HttpExchange t) throws IOException {
InputStream is = t.getRequestBody();
byte[] ba = new byte[BUFFER_SIZE];
int read;
long count = 0L;
while((read = is.read(ba)) != -1) {
count += read;
}
is.close();
check(count == expected, "Expected: " + expected + ", received "
+ count);
debug("Received " + count + " bytes");
t.sendResponseHeaders(200, RSPBODY_EMPTY);
t.close();
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,438 @@
/*
* Copyright (c) 2019, 2026, 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.
*/
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import jdk.test.lib.net.SimpleSSLContext;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
/*
* @test
* @bug 8218554
* @summary test that the handler can request a connection close.
* @library /test/lib
* @build jdk.test.lib.net.SimpleSSLContext
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run main/othervm ${test.main.class}
*/
public class HandlerConnectionClose {
static final int ONEK = 1024;
static final long POST_SIZE = ONEK * 1L;
private static final SSLContext sslContext = SimpleSSLContext.findSSLContext();
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
void test(String[] args) throws Exception {
HttpServer httpServer = startHttpServer("http");
try {
testHttpURLConnection(httpServer, "http","/close/legacy/http/chunked");
testHttpURLConnection(httpServer, "http","/close/legacy/http/fixed");
testPlainSocket(httpServer, "http","/close/plain/http/chunked");
testPlainSocket(httpServer, "http","/close/plain/http/fixed");
} finally {
httpServer.stop(0);
}
HttpServer httpsServer = startHttpServer("https");
try {
testHttpURLConnection(httpsServer, "https","/close/legacy/https/chunked");
testHttpURLConnection(httpsServer, "https","/close/legacy/https/fixed");
testPlainSocket(httpsServer, "https","/close/plain/https/chunked");
testPlainSocket(httpsServer, "https","/close/plain/https/fixed");
} finally{
httpsServer.stop(0);
}
}
void testHttpURLConnection(HttpServer httpServer, String protocol, String path) throws Exception {
int port = httpServer.getAddress().getPort();
String host = httpServer.getAddress().getHostString();
URL url = new URI(protocol, null, host, port, path, null, null).toURL();
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
if ("https".equalsIgnoreCase(protocol)) {
((HttpsURLConnection)uc).setSSLSocketFactory(sslContext.getSocketFactory());
((HttpsURLConnection)uc).setHostnameVerifier((String hostname, SSLSession session) -> true);
}
uc.setDoOutput(true);
uc.setRequestMethod("POST");
uc.setFixedLengthStreamingMode(POST_SIZE);
OutputStream os = uc.getOutputStream();
/* create a 1K byte array with data to POST */
byte[] ba = new byte[ONEK];
for (int i = 0; i < ONEK; i++)
ba[i] = (byte) i;
System.out.println("\n" + uc.getClass().getSimpleName() +": POST " + url + " HTTP/1.1");
long times = POST_SIZE / ONEK;
for (int i = 0; i < times; i++) {
os.write(ba);
}
os.close();
InputStream is = uc.getInputStream();
int read;
long count = 0;
while ((read = is.read(ba)) != -1) {
for (int i = 0; i < read; i++) {
byte expected = (byte) count++;
if (ba[i] != expected) {
throw new IOException("byte mismatch at "
+ (count - 1) + ": expected " + expected + " got " + ba[i]);
}
}
}
if (count != POST_SIZE) {
throw new IOException("Unexpected length: " + count + " expected " + POST_SIZE);
}
is.close();
pass();
}
void testPlainSocket(HttpServer httpServer, String protocol, String path) throws Exception {
int port = httpServer.getAddress().getPort();
String host = httpServer.getAddress().getHostString();
URL url = new URI(protocol, null, host, port, path, null, null).toURL();
Socket socket;
if ("https".equalsIgnoreCase(protocol)) {
socket = sslContext.getSocketFactory().createSocket(host, port);
} else {
socket = new Socket(host, port);
}
try (Socket sock = socket) {
OutputStream os = socket.getOutputStream();
// send request headers
String request = new StringBuilder()
.append("POST ").append(path).append(" HTTP/1.1").append("\r\n")
.append("host: ").append(host).append(':').append(port).append("\r\n")
.append("Content-Length: ").append(POST_SIZE).append("\r\n")
.append("\r\n")
.toString();
os.write(request.getBytes(StandardCharsets.US_ASCII));
/* create a 1K byte array with data to POST */
byte[] ba = new byte[ONEK];
for (int i = 0; i < ONEK; i++)
ba[i] = (byte) i;
// send request data
long times = POST_SIZE / ONEK;
for (int i = 0; i < times; i++) {
os.write(ba);
}
os.flush();
InputStream is = socket.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// read all response headers
int c;
int crlf = 0;
while ((c = is.read()) != -1) {
if (c == '\r') continue;
if (c == '\n') crlf++;
else crlf = 0;
bos.write(c);
if (crlf == 2) break;
}
String responseHeadersStr = bos.toString(StandardCharsets.US_ASCII);
List<String> responseHeaders = List.of(responseHeadersStr.split("\n"));
System.out.println("\nPOST " + url + " HTTP/1.1");
responseHeaders.stream().forEach(s -> System.out.println("[reply]\t" + s));
String statusLine = responseHeaders.get(0);
if (!statusLine.startsWith("HTTP/1.1 200 "))
throw new IOException("Unexpected status: " + statusLine);
String cl = responseHeaders.stream()
.map(s -> s.toLowerCase(Locale.ROOT))
.filter(s -> s.startsWith("content-length: "))
.findFirst()
.orElse(null);
String te = responseHeaders.stream()
.map(s -> s.toLowerCase(Locale.ROOT))
.filter(s -> s.startsWith("transfer-encoding: "))
.findFirst()
.orElse(null);
// check content-length and transfer-encoding are as expected
int read = 0;
long count = 0;
if (path.endsWith("/fixed")) {
if (!("content-length: " + POST_SIZE).equalsIgnoreCase(cl)) {
throw new IOException("Unexpected Content-Length: [" + cl + "]");
}
if (te != null) {
throw new IOException("Unexpected Transfer-Encoding: [" + te + "]");
}
// Got expected Content-Length: 1024 - read response data
while ((read = is.read()) != -1) {
int expected = (int) (count & 0xFF);
if ((read & 0xFF) != expected) {
throw new IOException("byte mismatch at "
+ (count - 1) + ": expected " + expected + " got " + read);
}
if (++count == POST_SIZE) break;
}
} else if (cl != null) {
throw new IOException("Unexpected Content-Length: [" + cl + "]");
} else {
if (!("transfer-encoding: chunked").equalsIgnoreCase(te)) {
throw new IOException("Unexpected Transfer-Encoding: [" + te + "]");
}
// This is a quick & dirty implementation of
// chunk decoding - no trailers - no extensions
StringBuilder chunks = new StringBuilder();
int cs = -1;
while (cs != 0) {
cs = 0;
chunks.setLength(0);
// read next chunk length
while ((read = is.read()) != -1) {
if (read == '\r') continue;
if (read == '\n') break;
chunks.append((char) read);
}
cs = Integer.parseInt(chunks.toString().trim(), 16);
System.out.println("Got chunk length: " + cs);
// If chunk size is 0, then we have read the last chunk.
if (cs == 0) break;
// Read the chunk data
while (--cs >= 0) {
read = is.read();
if (read == -1) break; // EOF
int expected = (int) (count & 0xFF);
if ((read & 0xFF) != expected) {
throw new IOException("byte mismatch at "
+ (count - 1) + ": expected " + expected + " got " + read);
}
// This is cheating: we know the size :-)
if (++count == POST_SIZE) break;
}
if (read == -1) {
throw new IOException("Unexpected EOF after " + count + " data bytes");
}
// read CRLF
if ((read = is.read()) != '\r') {
throw new IOException("Expected CR at " + count + "after chunk data - got " + read);
}
if ((read = is.read()) != '\n') {
throw new IOException("Expected LF at " + count + "after chunk data - got " + read);
}
if (cs == 0 && count == POST_SIZE) {
cs = -1;
}
if (cs != -1) {
// count == POST_SIZE, but some chunk data still to be read?
throw new IOException("Unexpected chunk size, "
+ cs + " bytes still to read after " + count +
" data bytes received.");
}
}
// Last CRLF?
for (int i = 0; i < 2; i++) {
if ((read = is.read()) == -1) break;
}
}
if (count != POST_SIZE) {
throw new IOException("Unexpected length: " + count + " expected " + POST_SIZE);
}
if (!sock.isClosed()) {
try {
// We send an end request to the server to verify that the
// connection is closed. If the server has not closed the
// connection, it will reply. If we receive a response,
// we should fail...
String endrequest = new StringBuilder()
.append("GET ").append("/close/end").append(" HTTP/1.1").append("\r\n")
.append("host: ").append(host).append(':').append(port).append("\r\n")
.append("Content-Length: ").append(0).append("\r\n")
.append("\r\n")
.toString();
os.write(endrequest.getBytes(StandardCharsets.US_ASCII));
os.flush();
StringBuilder resp = new StringBuilder();
crlf = 0;
// read all headers.
// If the server closed the connection as expected
// we should immediately read EOF
while ((read = is.read()) != -1) {
if (read == '\r') continue;
if (read == '\n') crlf++;
else crlf = 0;
if (crlf == 2) break;
resp.append((char) read);
}
List<String> lines = List.of(resp.toString().split("\n"));
if (read != -1 || resp.length() != 0) {
System.err.println("Connection not closed!");
System.err.println("Got: ");
lines.stream().forEach(s -> System.err.println("[end]\t" + s));
throw new AssertionError("EOF not received after " + count + " data bytes");
}
if (read != -1) {
throw new AssertionError("EOF was expected after " + count + " bytes, but got: " + read);
} else {
System.out.println("Got expected EOF (" + read + ")");
}
} catch (IOException x) {
// expected! all is well
System.out.println("Socket closed as expected, got exception writing to it.");
}
} else {
System.out.println("Socket closed as expected");
}
pass();
}
}
private static void setupLogging() {
final Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINEST);
logger.setLevel(Level.FINEST);
logger.addHandler(handler);
}
/**
* Http Server
*/
HttpServer startHttpServer(String protocol) throws IOException {
if (debug) {
setupLogging(); // merely for debugging
}
InetSocketAddress serverAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
HttpServer httpServer = null;
if ("http".equalsIgnoreCase(protocol)) {
httpServer = HttpServer.create(serverAddress, 0);
}
if ("https".equalsIgnoreCase(protocol)) {
HttpsServer httpsServer = HttpsServer.create(serverAddress, 0);
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
httpServer = httpsServer;
}
httpServer.createContext("/close/", new MyHandler(POST_SIZE));
System.out.println("Server created at: " + httpServer.getAddress());
httpServer.start();
return httpServer;
}
class MyHandler implements HttpHandler {
static final int BUFFER_SIZE = 512;
final long expected;
MyHandler(long expected){
this.expected = expected;
}
@Override
public void handle(HttpExchange t) throws IOException {
System.out.println("Server: serving " + t.getRequestURI());
boolean chunked = t.getRequestURI().getPath().endsWith("/chunked");
boolean fixed = t.getRequestURI().getPath().endsWith("/fixed");
boolean end = t.getRequestURI().getPath().endsWith("/end");
long responseLength = fixed ? POST_SIZE : 0;
responseLength = end ? -1 : responseLength;
responseLength = chunked ? 0 : responseLength;
if (!end) t.getResponseHeaders().add("connection", "CLose");
t.sendResponseHeaders(200, responseLength);
if (!end) {
OutputStream os = t.getResponseBody();
InputStream is = t.getRequestBody();
byte[] ba = new byte[BUFFER_SIZE];
int read;
long count = 0L;
while ((read = is.read(ba)) != -1) {
count += read;
os.write(ba, 0, read);
}
is.close();
check(count == expected, "Expected: " + expected + ", received "
+ count);
debug("Received " + count + " bytes");
os.close();
}
t.close();
}
}
//--------------------- Infrastructure ---------------------------
boolean debug = true;
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);}
void debug(String message) {if(debug) { System.out.println(message); } }
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2023, 2026, 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.
*/
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* @test
* @bug 8304963
* @summary Connection should be reusable after HEAD request
* @library /test/lib
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run junit/othervm ${test.main.class}
*/
public class HeadKeepAlive {
private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
private static void setupLogging() {
final Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINEST);
LOGGER.setLevel(Level.FINEST);
LOGGER.addHandler(handler);
}
@Test
void test() throws IOException, InterruptedException {
HttpServer httpServer = startHttpServer();
int port = httpServer.getAddress().getPort();
try {
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/firstCall")
.toURLUnchecked();
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
uc.setRequestMethod("HEAD");
int responseCode = uc.getResponseCode();
assertEquals(200, responseCode, "First request should succeed");
URL url2 = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/secondCall")
.toURLUnchecked();
HttpURLConnection uc2 = (HttpURLConnection)url2.openConnection(Proxy.NO_PROXY);
uc2.setRequestMethod("HEAD");
responseCode = uc2.getResponseCode();
assertEquals(200, responseCode, "Second request should reuse connection");
} finally {
httpServer.stop(0);
}
}
/**
* Http Server
*/
HttpServer startHttpServer() throws IOException {
setupLogging(); // merely for debugging
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
httpServer.createContext("/", new MyHandler());
httpServer.start();
return httpServer;
}
class MyHandler implements HttpHandler {
volatile int port1;
@Override
public void handle(HttpExchange t) throws IOException {
String path = t.getRequestURI().getPath();
if (path.equals("/firstCall")) {
port1 = t.getRemoteAddress().getPort();
System.out.println("First connection on client port = " + port1);
// send response
t.sendResponseHeaders(200, RSPBODY_EMPTY);
// the connection should still be reusable
} else if (path.equals("/secondCall")) {
int port2 = t.getRemoteAddress().getPort();
System.out.println("Second connection on client port = " + port2);
if (port1 == port2) {
t.sendResponseHeaders(200, RSPBODY_EMPTY);
} else {
t.sendResponseHeaders(500, RSPBODY_EMPTY);
}
}
t.close();
}
}
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2010, 2025, 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.
*/
/**
* @test
* @bug 6886723
* @library /test/lib
* @run main HeadTest
* @run main/othervm -Djava.net.preferIPv6Addresses=true HeadTest
* @summary light weight http server doesn't return correct status code for HEAD requests
*/
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
public class HeadTest {
public static void main(String[] args) throws Exception {
server();
}
static void server() throws Exception {
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress inetAddress = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create(inetAddress, 5);
try {
server.setExecutor(Executors.newFixedThreadPool(5));
HttpContext chunkedContext = server.createContext("/chunked");
chunkedContext.setHandler(new HttpHandler() {
@Override
public void handle(HttpExchange msg) {
try {
try {
if (msg.getRequestMethod().equals("HEAD")) {
msg.getRequestBody().close();
msg.getResponseHeaders().add("Transfer-encoding", "chunked");
msg.sendResponseHeaders(200, RSPBODY_EMPTY);
}
} catch(IOException ioe) {
ioe.printStackTrace();
}
} finally {
msg.close();
}
}
});
HttpContext clContext = server.createContext("/content");
clContext.setHandler(new HttpHandler() {
@Override
public void handle(HttpExchange msg) {
try {
try {
if (msg.getRequestMethod().equals("HEAD")) {
msg.getRequestBody().close();
msg.getResponseHeaders().add("Content-length", "1024");
msg.sendResponseHeaders(200, RSPBODY_EMPTY);
}
} catch(IOException ioe) {
ioe.printStackTrace();
}
} finally {
msg.close();
}
}
});
server.start();
String urlStr = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(server.getAddress().getPort())
.path("/")
.build()
.toString();
System.out.println("Server is at " + urlStr);
// Run the chunked client
for(int i=0; i < 10; i++) {
runClient(urlStr + "chunked/");
}
// Run the content length client
for(int i=0; i < 10; i++) {
runClient(urlStr + "content/");
}
} finally {
// Stop the server
((ExecutorService)server.getExecutor()).shutdown();
server.stop(0);
}
}
static void runClient(String urlStr) throws Exception {
HttpURLConnection conn = (HttpURLConnection) new URL(urlStr).openConnection(Proxy.NO_PROXY);
conn.setRequestMethod("HEAD");
int status = conn.getResponseCode();
if (status != 200) {
throw new RuntimeException("HEAD request doesn't return 200, but returns " + status);
}
}
}

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2019, 2025, 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.
*/
import com.sun.net.httpserver.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import jdk.test.lib.net.URIBuilder;
import sun.net.httpserver.HttpExchangeAccess;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/**
* @test
* @bug 8203036
* @library /test/lib
* @modules jdk.httpserver/sun.net.httpserver
* @build jdk.httpserver/sun.net.httpserver.HttpExchangeAccess AutoCloseableHttpExchange
* @run main/othervm AutoCloseableHttpExchange
* @summary Ensure that HttpExchange closes correctly when utilising the
* AutoCloseable interface e.g. both request InputStream and response OutputStream
* are closed, if not already.
*/
public class AutoCloseableHttpExchange {
static HttpServer testHttpServer;
static AtomicBoolean exchangeCloseFail = new AtomicBoolean(false);
static class Handler implements HttpHandler {
private CountDownLatch latch;
Handler(CountDownLatch latch) {
this.latch = latch;
}
public void handle(HttpExchange t) throws IOException {
InputStream is = t.getRequestBody();
try (HttpExchange e = t) {
while (is.read() != -1) ;
t.sendResponseHeaders(200, RSPBODY_EMPTY);
}
if (!HttpExchangeAccess.isClosed(t)) {
exchangeCloseFail.set(true);
}
latch.countDown();
}
}
static void connectAndCheck(String realm) throws Exception {
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(testHttpServer.getAddress().getPort())
.path(realm)
.toURL();
HttpURLConnection testConnection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY);
InputStream is = testConnection.getInputStream();
while (is.read() != -1) ;
is.close();
}
public static void main(String[] args) throws Exception {
int CONNECTION_COUNT = 5;
CountDownLatch latch = new CountDownLatch(CONNECTION_COUNT);
InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
testHttpServer = HttpServer.create(addr, 0);
testHttpServer.createContext("/test", new Handler(latch));
ExecutorService executor = Executors.newFixedThreadPool(CONNECTION_COUNT);
testHttpServer.setExecutor(executor);
testHttpServer.start();
while (CONNECTION_COUNT-- != 0) {
connectAndCheck("/test");
}
latch.await();
testHttpServer.stop(0);
executor.shutdown();
if (exchangeCloseFail.get())
throw new RuntimeException("The exchange was not closed properly");
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2019, 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 sun.net.httpserver;
public class HttpExchangeAccess {
public static boolean isClosed(com.sun.net.httpserver.HttpExchange exch) {
synchronized (exch) {
return ((HttpExchangeImpl) exch).getExchangeImpl().closed;
}
}
}

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2017, 2026, 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.
*/
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* @test
* @bug 8190793
* @summary Httpserver does not detect truncated request body
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run main/othervm ${test.main.class}
*/
/*
* Send two POST requests to the server which are both trucated
* and socket closed. Server needs to detect this and throw an IOException
* in getRequestBody().read(). Two variants for fixed length and chunked.
*/
public class TruncatedRequestBody {
private static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
static volatile boolean error = false;
static CountDownLatch latch = new CountDownLatch(2);
static class Handler implements HttpHandler {
@Override
public void handle(HttpExchange exch) throws IOException {
InputStream is = exch.getRequestBody();
int c, count = 0;
byte[] buf = new byte[128];
try {
while ((c=is.read(buf)) > 0)
count += c;
} catch (IOException e) {
System.out.println("Exception caught");
latch.countDown();
throw e;
}
// shouldn't get to here
error = true;
latch.countDown();
System.out.println("Read " + count + " bytes");
is.close();
exch.sendResponseHeaders(200, RSPBODY_EMPTY);
}
}
private static void setupLogging() {
final ConsoleHandler h = new ConsoleHandler();
h.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
logger.addHandler(h);
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) throws IOException, InterruptedException {
setupLogging(); // merely for debugging
InetAddress loopback = InetAddress.getLoopbackAddress();
InetSocketAddress addr = new InetSocketAddress(loopback, 0);
HttpServer server = HttpServer.create(addr, 10);
HttpContext ct = server.createContext("/", new Handler());
ExecutorService ex = Executors.newCachedThreadPool();
server.setExecutor(ex);
server.start();
int port = server.getAddress().getPort();
// Test 1: fixed length
Socket sock = new Socket(loopback, port);
String s1 = "POST /foo HTTP/1.1\r\nContent-length: 200000\r\n"
+ "\r\nfoo bar99";
OutputStream os = sock.getOutputStream();
os.write(s1.getBytes(StandardCharsets.ISO_8859_1));
Thread.sleep(500);
sock.close();
// Test 2: chunked
String s2 = "POST /foo HTTP/1.1\r\nTransfer-encoding: chunked\r\n\r\n" +
"100\r\nFoo bar";
sock = new Socket(loopback, port);
os = sock.getOutputStream();
os.write(s2.getBytes(StandardCharsets.ISO_8859_1));
Thread.sleep(500);
sock.close();
latch.await();
server.stop(0);
ex.shutdownNow();
if (error)
throw new RuntimeException("Test failed");
}
}

View file

@ -0,0 +1,118 @@
/*
* Copyright (c) 2023, 2026, 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.
*/
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import jdk.test.lib.net.URIBuilder;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
/*
* @test
* @bug 8219083
* @summary HttpExchange.getResponseBody write and close should not throw
* even when response length is zero
* @library /test/lib
* @comment We use othervm because this test configures logging handlers
* for the system wide "com.sun.net.httpserver" logger
* @run junit/othervm ${test.main.class}
*/
public class ZeroLengthOutputStream {
private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
public volatile boolean closed;
public CountDownLatch cdl = new CountDownLatch(1);
@Test
void test() throws IOException, InterruptedException {
HttpServer httpServer = startHttpServer();
int port = httpServer.getAddress().getPort();
try {
URL url = URIBuilder.newBuilder()
.scheme("http")
.loopback()
.port(port)
.path("/flis/")
.toURLUnchecked();
HttpURLConnection uc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
uc.getResponseCode();
cdl.await();
assertTrue(closed, "OutputStream close did not complete");
} finally {
httpServer.stop(0);
}
}
private static void setupLogging() {
final Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINEST);
LOGGER.setLevel(Level.FINEST);
LOGGER.addHandler(handler);
}
/**
* Http Server
*/
HttpServer startHttpServer() throws IOException {
setupLogging(); // merely for debugging
InetAddress loopback = InetAddress.getLoopbackAddress();
HttpServer httpServer = HttpServer.create(new InetSocketAddress(loopback, 0), 0);
httpServer.createContext("/flis/", new MyHandler());
httpServer.start();
return httpServer;
}
class MyHandler implements HttpHandler {
@Override
public void handle(HttpExchange t) throws IOException {
try {
OutputStream os = t.getResponseBody();
t.sendResponseHeaders(200, RSPBODY_EMPTY);
os.write(new byte[0]);
os.close();
System.out.println("Output stream closed");
closed = true;
} finally {
cdl.countDown();
}
}
}
}

View file

@ -0,0 +1,240 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Negative tests for java -m jdk.httpserver command
* @library /test/lib
* @modules jdk.httpserver
* @run junit/othervm CommandLineNegativeTest
*/
import java.io.IOException;
import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import jdk.test.lib.Platform;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.util.FileUtils;
import static java.lang.System.out;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertFalse;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class CommandLineNegativeTest {
static final Path JAVA_HOME = Path.of(System.getProperty("java.home"));
static final String LOCALE_OPT = "-Duser.language=en -Duser.country=US";
static final String JAVA = getJava(JAVA_HOME);
static final Path CWD = Path.of(".").toAbsolutePath().normalize();
static final Path TEST_DIR = CWD.resolve("CommandLineNegativeTest");
static final Path TEST_FILE = TEST_DIR.resolve("file.txt");
static final String LOOPBACK_ADDR = InetAddress.getLoopbackAddress().getHostAddress();
@BeforeAll
public static void setup() throws IOException {
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
Files.createDirectories(TEST_DIR);
Files.createFile(TEST_FILE);
}
public static Object[][] unknownOption() {
return new Object[][] {
{"--unknownOption"},
{"null"}
};
}
@ParameterizedTest
@MethodSource("unknownOption")
public void testBadOption(String opt) throws Throwable {
out.println("\n--- testUnknownOption, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt)
.shouldNotHaveExitValue(0)
.shouldContain("Error: unknown option: " + opt);
}
public static Object[][] tooManyOptionArgs() {
return new Object[][] {
{"-b", "localhost"},
{"-d", "/some/path"},
{"-o", "none"},
{"-p", "0"},
{"--bind-address", "localhost"},
{"--directory", "/some/path"},
{"--output", "none"},
{"--port", "0"}
// doesn't fail for -h option
};
}
@ParameterizedTest
@MethodSource("tooManyOptionArgs")
public void testTooManyOptionArgs(String opt, String arg) throws Throwable {
out.println("\n--- testTooManyOptionArgs, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, arg, arg)
.shouldNotHaveExitValue(0)
.shouldContain("Error: unknown option: " + arg);
}
public static Object[][] noArg() {
return new Object[][] {
{"-b", """
-b, --bind-address - Address to bind to. Default: %s (loopback).
For all interfaces use "-b 0.0.0.0" or "-b ::".""".formatted(LOOPBACK_ADDR)},
{"-d", "-d, --directory - Directory to serve. Default: current directory."},
{"-o", "-o, --output - Output format. none|info|verbose. Default: info."},
{"-p", "-p, --port - Port to listen on. Default: 8000."},
{"--bind-address", """
-b, --bind-address - Address to bind to. Default: %s (loopback).
For all interfaces use "-b 0.0.0.0" or "-b ::".""".formatted(LOOPBACK_ADDR)},
{"--directory", "-d, --directory - Directory to serve. Default: current directory."},
{"--output", "-o, --output - Output format. none|info|verbose. Default: info."},
{"--port", "-p, --port - Port to listen on. Default: 8000."}
// doesn't fail for -h option
};
}
@ParameterizedTest
@MethodSource("noArg")
public void testNoArg(String opt, String msg) throws Throwable {
out.println("\n--- testNoArg, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt)
.shouldNotHaveExitValue(0)
.shouldContain("Error: no value given for " + opt)
.shouldContain(msg);
}
public static Object[][] invalidValue() {
return new Object[][] {
{"-b", "[127.0.0.1]"},
{"-b", "badhost"},
{"--bind-address", "192.168.1.220..."},
{"-o", "bad-output-level"},
{"--output", "bad-output-level"},
{"-p", "+-"},
{"--port", "+-"}
};
}
@ParameterizedTest
@MethodSource("invalidValue")
public void testInvalidValue(String opt, String val) throws Throwable {
out.println("\n--- testInvalidValue, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, val)
.shouldNotHaveExitValue(0)
.shouldContain("Error: invalid value given for " + opt + ": " + val);
}
public static Object[][] portOptions() { return new Object[][] {{"-p"}, {"--port"}}; }
@ParameterizedTest
@MethodSource("portOptions")
public void testPortOutOfRange(String opt) throws Throwable {
out.println("\n--- testPortOutOfRange, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, "65536") // range 0 to 65535
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "port out of range:65536");
}
public static Object[][] directoryOptions() { return new Object[][] {{"-d"}, {"--directory"}}; }
@ParameterizedTest
@MethodSource("directoryOptions")
public void testRootNotADirectory(String opt) throws Throwable {
out.println("\n--- testRootNotADirectory, opt=\"%s\" ".formatted(opt));
var file = TEST_FILE.toString();
assertFalse(Files.isDirectory(TEST_FILE));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, file)
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "Path is not a directory: " + file);
}
@ParameterizedTest
@MethodSource("directoryOptions")
public void testRootDoesNotExist(String opt) throws Throwable {
out.println("\n--- testRootDoesNotExist, opt=\"%s\" ".formatted(opt));
Path root = TEST_DIR.resolve("not/existent/dir");
assertFalse(Files.exists(root));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, root.toString())
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "Path does not exist: " + root.toString());
}
@ParameterizedTest
@MethodSource("directoryOptions")
public void testRootNotReadable(String opt) throws Throwable {
out.println("\n--- testRootNotReadable, opt=\"%s\" ".formatted(opt));
Assumptions.assumeFalse(Platform.isWindows(), "cannot run on Windows"); // Not applicable to Windows. Reason: cannot revoke an owner's read
// access to a directory that was created by that owner
Path root = Files.createDirectories(TEST_DIR.resolve("not/readable/dir"));
try {
root.toFile().setReadable(false, false);
assertFalse(Files.isReadable(root));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, root.toString())
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "Path is not readable: " + root.toString());
} finally {
root.toFile().setReadable(true, false);
}
}
@AfterAll
public static void teardown() throws IOException {
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
}
// --- infra ---
static String getJava(Path image) {
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
Path java = image.resolve("bin").resolve(isWindows ? "java.exe" : "java");
if (Files.notExists(java))
throw new RuntimeException(java + " not found");
return java.toAbsolutePath().toString();
}
static OutputAnalyzer simpleserver(String... args) throws Throwable {
String[] nargs = new String[args.length + 1];
nargs[0] = args[0];
System.arraycopy(args, 1, nargs, 2, args.length-1);
nargs[1] = "-Djdk.includeInExceptions=hostInfo";
var pb = new ProcessBuilder(nargs)
.directory(TEST_DIR.toFile());
var outputAnalyser = ProcessTools.executeCommand(pb)
.outputTo(System.out)
.errorTo(System.out);
return outputAnalyser;
}
}

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @bug 8276848
* @summary Tests the java -m jdk.httpserver command with port not specified
* @modules jdk.httpserver
* @library /test/lib
* @run junit/othervm/manual CommandLinePortNotSpecifiedTest
*/
import java.io.IOException;
import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import jdk.test.lib.Platform;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.util.FileUtils;
import static java.lang.System.out;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class CommandLinePortNotSpecifiedTest {
static final Path JAVA_HOME = Path.of(System.getProperty("java.home"));
static final String LOCALE_OPT = "-Duser.language=en -Duser.country=US";
static final String JAVA = getJava(JAVA_HOME);
static final Path CWD = Path.of(".").toAbsolutePath().normalize();
static final Path TEST_DIR = CWD.resolve("CommandLinePortNotSpecifiedTest");
static final Path TEST_FILE = TEST_DIR.resolve("file.txt");
static final String TEST_DIR_STR = TEST_DIR.toString();
static final String LOOPBACK_ADDR = InetAddress.getLoopbackAddress().getHostAddress();
@BeforeAll
public static void setup() throws IOException {
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
Files.createDirectories(TEST_DIR);
Files.createFile(TEST_FILE);
}
static final int SIGTERM = 15;
static final int NORMAL_EXIT_CODE = normalExitCode();
static int normalExitCode() {
if (Platform.isWindows()) {
return 1; // expected process destroy exit code
} else {
// signal terminated exit code on Unix is 128 + signal value
return 128 + SIGTERM;
}
}
/**
* This is a manual test to confirm the command-line tool starts successfully
* in the case where the port is not specified. In this case the server uses
* the default port 8000. The test is manual to avoid a BindException in the
* unlikely but not impossible case that the port is already in use.
*/
@Test
public void testPortNotSpecified() throws Throwable {
out.println("\n--- testPortNotSpecified");
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver")
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Binding to loopback by default. For all interfaces use \"-b 0.0.0.0\" or \"-b ::\".")
.shouldContain("Serving " + TEST_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@AfterAll
public static void teardown() throws IOException {
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
}
// --- infra ---
static String getJava(Path image) {
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
Path java = image.resolve("bin").resolve(isWindows ? "java.exe" : "java");
if (Files.notExists(java))
throw new RuntimeException(java + " not found");
return java.toAbsolutePath().toString();
}
static final String REGULAR_STARTUP_LINE1_STRING = "Serving";
static final String REGULAR_STARTUP_LINE2_STRING = "URL http://";
static final String OPTIONS_TEXT = """
Options:
-b, --bind-address - Address to bind to. Default: %s (loopback).
For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory - Directory to serve. Default: current directory.
-o, --output - Output format. none|info|verbose. Default: info.
-p, --port - Port to listen on. Default: 8000.
-h, -?, --help - Prints this help message and exits.
-version, --version - Prints version information and exits.
To stop the server, press Ctrl + C.""".formatted(LOOPBACK_ADDR);
// The stdout/stderr output line to wait for when starting the simpleserver
enum WaitForLine {
REGULAR_STARTUP_LINE (REGULAR_STARTUP_LINE2_STRING) ,
HELP_STARTUP_LINE (OPTIONS_TEXT.lines().reduce((first, second) -> second).orElseThrow());
final String value;
WaitForLine(String value) { this.value = value; }
}
static OutputAnalyzer simpleserver(String... args) throws Throwable {
return simpleserver(WaitForLine.REGULAR_STARTUP_LINE, true, args);
}
static OutputAnalyzer simpleserver(WaitForLine waitForLine, boolean destroy, String... args) throws Throwable {
StringBuffer sb = new StringBuffer(); // stdout & stderr
// start the process and await the waitForLine before returning
var p = ProcessTools.startProcess("simpleserver",
new ProcessBuilder(args).directory(TEST_DIR.toFile()),
line -> sb.append(line + "\n"),
line -> line.startsWith(waitForLine.value),
30, // suitably high default timeout, not expected to timeout
TimeUnit.SECONDS);
if (destroy) {
p.destroy(); // SIGTERM on Unix
}
int ec = p.waitFor();
var outputAnalyser = new OutputAnalyzer(sb.toString(), "", ec);
return outputAnalyser;
}
}

View file

@ -0,0 +1,304 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Positive tests for java -m jdk.httpserver command
* @library /test/lib
* @build jdk.test.lib.net.IPSupport
* @modules jdk.httpserver
* @run junit/othervm CommandLinePositiveTest
*/
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import jdk.test.lib.Platform;
import jdk.test.lib.net.IPSupport;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.util.FileUtils;
import static java.lang.System.out;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class CommandLinePositiveTest {
static final String JAVA_VERSION = System.getProperty("java.version");
static final Path JAVA_HOME = Path.of(System.getProperty("java.home"));
static final String LOCALE_OPT = "-Duser.language=en -Duser.country=US";
static final String JAVA = getJava(JAVA_HOME);
/**
* The <b>real path</b> to the current working directory where
* <ol>
* <li>the web server process will be started in,</li>
* <li>and hence, unless given an explicit content root directory, the web
* server will be serving from.</li>
* </ol>
*/
private static final Path CWD;
static {
try {
CWD = Path.of(".").toRealPath();
} catch (IOException exception) {
throw new UncheckedIOException(exception);
}
}
private static final String CWD_STR = CWD.toString();
/**
* The <b>real path</b> to the web server content root directory, if one
* needs to be provided explicitly.
*/
private static final Path ROOT_DIR = CWD.resolve("www");
private static final String ROOT_DIR_STR = ROOT_DIR.toString();
static final String LOOPBACK_ADDR = InetAddress.getLoopbackAddress().getHostAddress();
@BeforeAll
public static void setup() throws IOException {
if (Files.exists(ROOT_DIR)) {
FileUtils.deleteFileTreeWithRetry(ROOT_DIR);
}
Files.createDirectories(ROOT_DIR);
Files.createFile(ROOT_DIR.resolve("file.txt"));
}
static final int SIGTERM = 15;
static final int NORMAL_EXIT_CODE = normalExitCode();
static int normalExitCode() {
if (Platform.isWindows()) {
return 1; // expected process destroy exit code
} else {
// signal terminated exit code on Unix is 128 + signal value
return 128 + SIGTERM;
}
}
public static Object[][] directoryOptions() { return new Object[][] {{"-d"}, {"--directory"}}; }
@ParameterizedTest
@MethodSource("directoryOptions")
public void testAbsDirectory(String opt) throws Throwable {
out.printf("\n--- testAbsDirectory, opt=\"%s\"%n", opt);
testDirectory(opt, ROOT_DIR_STR);
}
@ParameterizedTest
@MethodSource("directoryOptions")
public void testRelDirectory(String opt) throws Throwable {
out.printf("\n--- testRelDirectory, opt=\"%s\"%n", opt);
Path rootRelDir = CWD.relativize(ROOT_DIR);
testDirectory(opt, rootRelDir.toString());
}
private static void testDirectory(String opt, String rootDir) throws Throwable {
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", "-p", "0", opt, rootDir)
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Binding to loopback by default. For all interfaces use \"-b 0.0.0.0\" or \"-b ::\".")
.shouldContain("Serving " + ROOT_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
public static Object[][] portOptions() { return new Object[][] {{"-p"}, {"--port"}}; }
@ParameterizedTest
@MethodSource("portOptions")
public void testPort(String opt) throws Throwable {
out.println("\n--- testPort, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, "0")
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Binding to loopback by default. For all interfaces use \"-b 0.0.0.0\" or \"-b ::\".")
.shouldContain("Serving " + CWD_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
public static Object[][] helpOptions() { return new Object[][] {{"-h"}, {"-?"}, {"--help"}}; }
static final String USAGE_TEXT = """
Usage: java -m jdk.httpserver [-b bind address] [-p port] [-d directory]
[-o none|info|verbose] [-h to show options]
[-version to show version information]""";
static final String OPTIONS_TEXT = """
Options:
-b, --bind-address - Address to bind to. Default: %s (loopback).
For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory - Directory to serve. Default: current directory.
-o, --output - Output format. none|info|verbose. Default: info.
-p, --port - Port to listen on. Default: 8000.
-h, -?, --help - Prints this help message and exits.
-version, --version - Prints version information and exits.
To stop the server, press Ctrl + C.""".formatted(LOOPBACK_ADDR);
@ParameterizedTest
@MethodSource("helpOptions")
public void testHelp(String opt) throws Throwable {
out.println("\n--- testHelp, opt=\"%s\" ".formatted(opt));
simpleserver(WaitForLine.HELP_STARTUP_LINE,
false, // do not explicitly destroy the process
JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt)
.shouldHaveExitValue(0)
.shouldContain(USAGE_TEXT)
.shouldContain(OPTIONS_TEXT);
}
public static Object[][] versionOptions() { return new Object[][] {{"-version"}, {"--version"}}; }
@ParameterizedTest
@MethodSource("versionOptions")
public void testVersion(String opt) throws Throwable {
out.println("\n--- testVersion, opt=\"%s\" ".formatted(opt));
simpleserver(WaitForLine.VERSION_STARTUP_LINE,
false, // do not explicitly destroy the process
JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt)
.shouldHaveExitValue(0);
}
public static Object[][] bindOptions() { return new Object[][] {{"-b"}, {"--bind-address"}}; }
@ParameterizedTest
@MethodSource("bindOptions")
public void testBindAllInterfaces(String opt) throws Throwable {
out.println("\n--- testBindAllInterfaces, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", "-p", "0", opt, "0.0.0.0")
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Serving " + CWD_STR + " and subdirectories on 0.0.0.0 (all interfaces) port")
.shouldContain("URL http://" + InetAddress.getLocalHost().getHostAddress());
if (IPSupport.hasIPv6()) {
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, "::0")
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Serving " + CWD_STR + " and subdirectories on 0.0.0.0 (all interfaces) port")
.shouldContain("URL http://" + InetAddress.getLocalHost().getHostAddress());
}
}
@ParameterizedTest
@MethodSource("bindOptions")
public void testLastOneWinsBindAddress(String opt) throws Throwable {
out.println("\n--- testLastOneWinsBindAddress, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", "-p", "0", opt, "123.4.5.6", opt, LOOPBACK_ADDR)
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Serving " + CWD_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@ParameterizedTest
@MethodSource("directoryOptions")
public void testLastOneWinsDirectory(String opt) throws Throwable {
out.println("\n--- testLastOneWinsDirectory, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", "-p", "0", opt, ROOT_DIR_STR, opt, ROOT_DIR_STR)
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Binding to loopback by default. For all interfaces use \"-b 0.0.0.0\" or \"-b ::\".")
.shouldContain("Serving " + ROOT_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
public static Object[][] outputOptions() { return new Object[][] {{"-o"}, {"--output"}}; }
@ParameterizedTest
@MethodSource("outputOptions")
public void testLastOneWinsOutput(String opt) throws Throwable {
out.println("\n--- testLastOneWinsOutput, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", "-p", "0", opt, "none", opt, "verbose")
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Binding to loopback by default. For all interfaces use \"-b 0.0.0.0\" or \"-b ::\".")
.shouldContain("Serving " + CWD_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@ParameterizedTest
@MethodSource("portOptions")
public void testLastOneWinsPort(String opt) throws Throwable {
out.println("\n--- testLastOneWinsPort, opt=\"%s\" ".formatted(opt));
simpleserver(JAVA, LOCALE_OPT, "-m", "jdk.httpserver", opt, "-999", opt, "0")
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Binding to loopback by default. For all interfaces use \"-b 0.0.0.0\" or \"-b ::\".")
.shouldContain("Serving " + CWD_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@AfterAll
public static void teardown() throws IOException {
if (Files.exists(ROOT_DIR)) {
FileUtils.deleteFileTreeWithRetry(ROOT_DIR);
}
}
// --- infra ---
static String getJava(Path image) {
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
Path java = image.resolve("bin").resolve(isWindows ? "java.exe" : "java");
if (Files.notExists(java))
throw new RuntimeException(java + " not found");
return java.toAbsolutePath().toString();
}
static final String REGULAR_STARTUP_LINE1_STRING = "Serving";
static final String REGULAR_STARTUP_LINE2_STRING = "URL http://";
static final String VERSION_STARTUP_LINE_STRING = "java " + JAVA_VERSION;
// The stdout/stderr output line to wait for when starting the simpleserver
enum WaitForLine {
REGULAR_STARTUP_LINE (REGULAR_STARTUP_LINE2_STRING) ,
HELP_STARTUP_LINE (OPTIONS_TEXT.lines().reduce((first, second) -> second).orElseThrow()),
VERSION_STARTUP_LINE (VERSION_STARTUP_LINE_STRING);
final String value;
WaitForLine(String value) { this.value = value; }
}
static OutputAnalyzer simpleserver(String... args) throws Throwable {
return simpleserver(WaitForLine.REGULAR_STARTUP_LINE, true, args);
}
static OutputAnalyzer simpleserver(WaitForLine waitForLine, boolean destroy, String... args) throws Throwable {
StringBuffer sb = new StringBuffer(); // stdout & stderr
// start the process and await the waitForLine before returning
var p = ProcessTools.startProcess("simpleserver",
new ProcessBuilder(args),
line -> sb.append(line + "\n"),
line -> line.startsWith(waitForLine.value),
30, // suitably high default timeout, not expected to timeout
TimeUnit.SECONDS);
if (destroy) {
p.destroy(); // SIGTERM on Unix
}
int ec = p.waitFor();
var outputAnalyser = new OutputAnalyzer(sb.toString(), "", ec);
return outputAnalyser;
}
}

View file

@ -0,0 +1,981 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Tests for SimpleFileServer with a root that is not of the default
* file system
* @library /test/lib
* @build jdk.test.lib.Platform jdk.test.lib.net.URIBuilder
* @run junit/othervm CustomFileSystemTest
*/
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessMode;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.ProviderMismatchException;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;
import jdk.test.lib.Platform;
import jdk.test.lib.net.URIBuilder;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardOpenOption.CREATE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class CustomFileSystemTest {
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
@BeforeAll
public static void setup() throws Exception {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
}
@Test
public void testFileGET() throws Exception {
var root = createDirectoryInCustomFs("testFileGET");
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
var lastModified = getLastModified(file);
var expectedLength = Long.toString(Files.size(file));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("some text", response.body());
assertEquals("text/plain", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
} finally {
server.stop(0);
}
}
@Test
public void testDirectoryGET() throws Exception {
var expectedBody = openHTML + """
<h1>Directory listing for &#x2F;</h1>
<ul>
<li><a href="aFile.txt">aFile.txt</a></li>
</ul>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testDirectoryGET");
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
var lastModified = getLastModified(root);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("text/html; charset=UTF-8", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testFileHEAD() throws Exception {
var root = createDirectoryInCustomFs("testFileHEAD");
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
var lastModified = getLastModified(file);
var expectedLength = Long.toString(Files.size(file));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile.txt"))
.method("HEAD", HttpRequest.BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("text/plain", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
assertEquals("", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testDirectoryHEAD() throws Exception {
var expectedLength = Integer.toString(
(openHTML + """
<h1>Directory listing for &#x2F;</h1>
<ul>
<li><a href="aFile.txt">aFile.txt</a></li>
</ul>
""" + closeHTML).getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testDirectoryHEAD");
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
var lastModified = getLastModified(root);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, ""))
.method("HEAD", HttpRequest.BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("text/html; charset=UTF-8", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
assertEquals("", response.body());
} finally {
server.stop(0);
}
}
public static Object[][] indexFiles() {
var fileContent = openHTML + """
<h1>This is an index file</h1>
""" + closeHTML;
var dirListing = openHTML + """
<h1>Directory listing for &#x2F;</h1>
<ul>
</ul>
""" + closeHTML;
return new Object[][] {
{"1", "index.html", "text/html", "116", fileContent, true},
{"2", "index.htm", "text/html", "116", fileContent, true},
{"3", "index.txt", "text/html; charset=UTF-8", "134", dirListing, false}
};
}
@ParameterizedTest
@MethodSource("indexFiles")
public void testDirectoryWithIndexGET(String id,
String filename,
String contentType,
String contentLength,
String expectedBody,
boolean serveIndexFile) throws Exception {
var root = createDirectoryInCustomFs("testDirectoryWithIndexGET"+id);
var lastModified = getLastModified(root);
if (serveIndexFile) {
var file = Files.writeString(root.resolve(filename), expectedBody, CREATE);
lastModified = getLastModified(file);
}
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(contentType, response.headers().firstValue("content-type").get());
assertEquals(contentLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
if (serveIndexFile) {
Files.delete(root.resolve(filename));
}
}
}
@Test
public void testNotReadableFileGET() throws Exception {
Assumptions.assumeFalse(Platform.isWindows(), "Not applicable on Windows");
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;aFile.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testNotReadableFileGET");
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
file.toFile().setReadable(false, false);
assert !Files.isReadable(file);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
file.toFile().setReadable(true, false);
}
}
@Test
public void testNotReadableSegmentGET() throws Exception {
Assumptions.assumeFalse(Platform.isWindows(), "Not applicable on Windows");
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;dir&#x2F;aFile.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testNotReadableSegmentGET");
var dir = Files.createDirectory(root.resolve("dir"));
var file = Files.writeString(dir.resolve("aFile.txt"), "some text", CREATE);
dir.toFile().setReadable(false, false);
assert !Files.isReadable(dir);
assert Files.isReadable(file);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "dir/aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
dir.toFile().setReadable(true, false);
}
}
@Test
public void testInvalidRequestURIGET() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;aFile?#.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testInvalidRequestURIGET");
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile?#.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testNotFoundGET() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;doesNotExist.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testNotFoundGET");
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "doesNotExist.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testNotFoundHEAD() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;doesNotExist.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testNotFoundHEAD");
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "doesNotExist.txt"))
.method("HEAD", HttpRequest.BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals("", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testSymlinkGET() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;symlink</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testSymlinkGET");
var symlink = root.resolve("symlink");
var target = Files.writeString(root.resolve("target.txt"), "some text", CREATE);
createSymLink(symlink, target);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "symlink")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testSymlinkSegmentGET() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;symlink&#x2F;aFile.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = createDirectoryInCustomFs("testSymlinkSegmentGET");
var symlink = root.resolve("symlink");
var target = Files.createDirectory(root.resolve("target"));
Files.writeString(target.resolve("aFile.txt"), "some text", CREATE);
createSymLink(symlink, target);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "symlink/aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
private static void createSymLink(Path symlink, Path target) {
try {
Files.createSymbolicLink(symlink, target);
} catch (UnsupportedOperationException uoe) {
Assumptions.abort("sym link creation not supported");
} catch (IOException ioe) {
Assumptions.abort("probably insufficient privileges to create sym links (Windows)");
}
}
@Test
public void testHiddenFileGET() throws Exception {
var root = createDirectoryInCustomFs("testHiddenFileGET");
var file = createHiddenFile(root);
var fileName = file.getFileName().toString();
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;""" + fileName +
"""
</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, fileName)).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testHiddenSegmentGET() throws Exception {
var root = createDirectoryInCustomFs("testHiddenSegmentGET");
var file = createFileInHiddenDirectory(root);
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;.hiddenDirectory&#x2F;aFile.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, ".hiddenDirectory/aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
private static Path createHiddenFile(Path root) throws IOException {
Path file;
if (Platform.isWindows()) {
file = Files.createFile(root.resolve("aFile.txt"));
Files.setAttribute(file, "dos:hidden", true, LinkOption.NOFOLLOW_LINKS);
} else {
file = Files.writeString(root.resolve(".aFile.txt"), "some text", CREATE);
}
assertTrue(Files.isHidden(file));
return file;
}
private static Path createFileInHiddenDirectory(Path root) throws IOException {
Path dir;
Path file;
if (Platform.isWindows()) {
dir = Files.createDirectory(root.resolve("hiddenDirectory"));
Files.setAttribute(dir, "dos:hidden", true, LinkOption.NOFOLLOW_LINKS);
} else {
dir = Files.createDirectory(root.resolve(".hiddenDirectory"));
}
file = Files.writeString(dir.resolve("aFile.txt"), "some text", CREATE);
assertTrue(Files.isHidden(dir));
assertFalse(Files.isHidden(file));
return file;
}
@Test
public void testMovedPermanently() throws Exception {
var root = createDirectoryInCustomFs("testMovedPermanently");
Files.createDirectory(root.resolve("aDirectory"));
var expectedBody = openHTML + """
<h1>Directory listing for &#x2F;aDirectory&#x2F;</h1>
<ul>
</ul>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
{
var client = HttpClient.newBuilder().proxy(NO_PROXY)
.followRedirects(HttpClient.Redirect.NEVER).build();
var uri = uri(server, "aDirectory");
var request = HttpRequest.newBuilder(uri).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(301, response.statusCode());
assertEquals("0", response.headers().firstValue("content-length").get());
assertEquals("/aDirectory/", response.headers().firstValue("location").get());
// tests that query component is preserved during redirect
var uri2 = uri(server, "aDirectory", "query");
var req2 = HttpRequest.newBuilder(uri2).build();
var res2 = client.send(req2, BodyHandlers.ofString());
assertEquals(301, res2.statusCode());
assertEquals("0", res2.headers().firstValue("content-length").get());
assertEquals("/aDirectory/?query", res2.headers().firstValue("location").get());
}
{ // tests that redirect to returned relative URI works
var client = HttpClient.newBuilder().proxy(NO_PROXY)
.followRedirects(HttpClient.Redirect.ALWAYS).build();
var uri = uri(server, "aDirectory");
var request = HttpRequest.newBuilder(uri).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(expectedBody, response.body());
assertEquals("text/html; charset=UTF-8", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
}
} finally {
server.stop(0);
}
}
@Test
public void testXss() throws Exception {
var root = createDirectoryInCustomFs("testXss");
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "beginDelim%3C%3EEndDelim")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertTrue(response.body().contains("beginDelim%3C%3EEndDelim"));
assertTrue(response.body().contains("File not found"));
} finally {
server.stop(0);
}
}
static Path createDirectoryInCustomFs(String name) throws Exception {
var defaultFs = FileSystems.getDefault();
var fs = new CustomProvider(defaultFs.provider()).newFileSystem(defaultFs);
var dir = fs.getPath(name);
if (Files.notExists(dir)) {
Files.createDirectory(dir);
}
return dir.toAbsolutePath();
}
static final String openHTML = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
""";
static final String closeHTML = """
</body>
</html>
""";
static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.buildUnchecked();
}
static URI uri(HttpServer server, String path, String query) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.query(query)
.buildUnchecked();
}
static String getLastModified(Path path) throws IOException {
return Files.getLastModifiedTime(path).toInstant().atZone(ZoneId.of("GMT"))
.format(DateTimeFormatter.RFC_1123_DATE_TIME);
}
// --- Custom File System ---
static class CustomProvider extends FileSystemProvider {
private final ConcurrentHashMap<FileSystem, CustomFileSystem> map =
new ConcurrentHashMap<>();
private final FileSystemProvider defaultProvider;
public CustomProvider(FileSystemProvider provider) {
defaultProvider = provider;
}
@Override
public String getScheme() {
return defaultProvider.getScheme();
}
public FileSystem newFileSystem(FileSystem fs) {
return map.computeIfAbsent(fs, (sfs) ->
new CustomFileSystem(this, fs));
}
@Override
public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
FileSystem fs = defaultProvider.newFileSystem(uri, env);
return map.computeIfAbsent(fs, (sfs) ->
new CustomFileSystem(this, fs)
);
}
@Override
public FileSystem getFileSystem(URI uri) {
return map.get(defaultProvider.getFileSystem(uri));
}
@Override
public Path getPath(URI uri) {
Path p = defaultProvider.getPath(uri);
return map.get(defaultProvider.getFileSystem(uri)).wrap(p);
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
Path p = toCustomPath(path).unwrap();
return defaultProvider.newByteChannel(p, options, attrs);
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
throw new RuntimeException("not implemented");
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
Path p = toCustomPath(dir).unwrap();
defaultProvider.createDirectory(p, attrs);
}
@Override
public void delete(Path path) throws IOException {
Path p = toCustomPath(path).unwrap();
defaultProvider.delete(p);
}
@Override
public void copy(Path source, Path target, CopyOption... options) throws IOException {
Path sp = toCustomPath(source).unwrap();
Path tp = toCustomPath(target).unwrap();
defaultProvider.copy(sp, tp, options);
}
@Override
public void move(Path source, Path target, CopyOption... options)
throws IOException {
Path sp = toCustomPath(source).unwrap();
Path tp = toCustomPath(target).unwrap();
defaultProvider.move(sp, tp, options);
}
@Override
public boolean isSameFile(Path path, Path path2)
throws IOException {
Path p = toCustomPath(path).unwrap();
Path p2 = toCustomPath(path2).unwrap();
return defaultProvider.isSameFile(p, p2);
}
@Override
public boolean isHidden(Path path) throws IOException {
Path p = toCustomPath(path).unwrap();
return defaultProvider.isHidden(p);
}
@Override
public FileStore getFileStore(Path path) throws IOException {
Path p = toCustomPath(path).unwrap();
return defaultProvider.getFileStore(p);
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
Path p = toCustomPath(path).unwrap();
defaultProvider.checkAccess(p, modes);
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(Path path,
Class<V> type,
LinkOption... options) {
Path p = toCustomPath(path).unwrap();
return defaultProvider.getFileAttributeView(p, type, options);
}
@Override
public <A extends BasicFileAttributes> A readAttributes(Path path,
Class<A> type,
LinkOption... options)
throws IOException {
Path p = toCustomPath(path).unwrap();
return defaultProvider.readAttributes(p, type, options);
}
@Override
public Map<String, Object> readAttributes(Path path,
String attributes,
LinkOption... options)
throws IOException {
Path p = toCustomPath(path).unwrap();
return defaultProvider.readAttributes(p, attributes, options);
}
@Override
public void setAttribute(Path path, String attribute,
Object value, LinkOption... options)
throws IOException {
Path p = toCustomPath(path).unwrap();
defaultProvider.setAttribute(p, attribute, options);
}
// Checks that the given file is a CustomPath
static CustomPath toCustomPath(Path obj) {
if (obj == null)
throw new NullPointerException();
if (!(obj instanceof CustomPath cp))
throw new ProviderMismatchException();
return cp;
}
}
static class CustomFileSystem extends FileSystem {
private final CustomProvider provider;
private final FileSystem delegate;
public CustomFileSystem(CustomProvider provider, FileSystem delegate) {
this.provider = provider;
this.delegate = delegate;
}
@Override
public FileSystemProvider provider() {
return provider;
}
@Override
public void close() throws IOException { delegate.close(); }
@Override
public boolean isOpen() {
return true;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public String getSeparator() { return delegate.getSeparator(); }
@Override
public Iterable<Path> getRootDirectories() {
return null;
}
@Override
public Iterable<FileStore> getFileStores() {
return null;
}
@Override
public Set<String> supportedFileAttributeViews() {
return null;
}
@Override
public Path getPath(String first, String... more) {
return delegate.getPath(first, more);
}
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern) {
return null;
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
return null;
}
@Override
public WatchService newWatchService() throws IOException {
return null;
}
@Override
public String toString() {
return delegate.toString();
}
Path wrap(Path path) {
return (path != null) ? new CustomPath(this, path) : null;
}
Path unwrap(Path wrapper) {
if (wrapper == null)
throw new NullPointerException();
if (!(wrapper instanceof CustomPath cp))
throw new ProviderMismatchException();
return cp.unwrap();
}
}
static class CustomPath implements Path {
private final CustomFileSystem fs;
private final Path delegate;
CustomPath(CustomFileSystem fs, Path delegate) {
this.fs = fs;
this.delegate = delegate;
}
@Override
public FileSystem getFileSystem() {
return fs;
}
@Override
public boolean isAbsolute() {
return delegate.isAbsolute();
}
@Override
public Path getRoot() {
return fs.wrap(delegate.getRoot());
}
@Override
public Path getFileName() {
return null;
}
@Override
public Path getParent() {
return null;
}
@Override
public int getNameCount() {
return 0;
}
@Override
public Path getName(int index) {
return null;
}
@Override
public Path subpath(int beginIndex, int endIndex) {
return null;
}
@Override
public boolean startsWith(Path other) {
return delegate.startsWith(other);
}
@Override
public boolean endsWith(Path other) {
return false;
}
@Override
public Path normalize() {
return fs.wrap(delegate.normalize());
}
@Override
public Path resolve(Path other) {
return fs.wrap(delegate.resolve(fs.unwrap(other)));
}
@Override
public Path relativize(Path other) {
return null;
}
@Override
public URI toUri() {
return delegate.toUri();
}
@Override
public Path toAbsolutePath() {
return fs.wrap(delegate.toAbsolutePath());
}
@Override
public Path toRealPath(LinkOption... options) throws IOException {
return null;
}
@Override
public WatchKey register(WatchService watcher, WatchEvent.Kind<?>[] events, WatchEvent.Modifier... modifiers) throws IOException {
return null;
}
@Override
public int compareTo(Path other) {
return 0;
}
Path unwrap() {
return delegate;
}
}
}

View file

@ -0,0 +1,292 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Tests file permission checks during the creation of a `FileServerHandler`
* @requires (os.family != "windows")
* @library /test/lib
* @build jdk.test.lib.net.URIBuilder
* @run main/othervm -ea DocRootDirPermissionsTest true
* @run main/othervm -ea DocRootDirPermissionsTest false
*/
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.AclEntry;
import java.nio.file.attribute.AclEntryPermission;
import java.nio.file.attribute.AclEntryType;
import java.nio.file.attribute.AclFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.UserPrincipal;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;
import jdk.test.lib.net.URIBuilder;
import jdk.test.lib.util.FileUtils;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardOpenOption.CREATE;
/**
* Tests file permission checks during the creation of a FileServerHandler.
*
* A FileServerHandler can only be created if its root directory
* is readable. The test consists of 2 runs:
* 1) RootDir is readable
* 2) RootDir is NOT readable
* 2) reuses the test directory created in the previous run, revoking
* read access.
* */
public class DocRootDirPermissionsTest {
private static final Path CWD = Path.of(".").toAbsolutePath().normalize();
private static final Path TEST_DIR = CWD.resolve("RootDir");
private static final InetSocketAddress LOOPBACK_ADDR =
new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
private static final boolean ENABLE_LOGGING = true;
private static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
private static boolean readPermitted;
private static String lastModifiedDir;
private static String lastModifiedFile;
private static Set<PosixFilePermission> posixPermissions;
private static List<AclEntry> acls;
public static void main(String[] args) throws Exception {
new DocRootDirPermissionsTest().run(args);
}
protected void run(String[] args) throws Exception{
setupLogging();
readPermitted = Boolean.parseBoolean(args[0]);
if (readPermitted) {
createTestDir();
testDirectoryGET();
testFileGET();
} else {
revokePermissions();
try {
testCreateHandler();
} finally {
restorePermissions();
}
}
}
private void revokePermissions() throws IOException {
if (!Files.isReadable(TEST_DIR)) {
// good nothing to do:
System.out.println("File is already not readable: nothing to do");
return;
}
System.out.println("FileSystem: " + Files.getFileStore(TEST_DIR).type());
if (Files.getFileStore(TEST_DIR).supportsFileAttributeView("posix")) {
System.out.println("Revoking owner's read access in POSIX permissions for " + TEST_DIR);
posixPermissions = Files.getPosixFilePermissions(TEST_DIR);
Set<PosixFilePermission> newPerms = new HashSet<>(posixPermissions);
newPerms.remove(PosixFilePermission.OWNER_READ);
newPerms.remove(PosixFilePermission.OWNER_EXECUTE);
Files.setPosixFilePermissions(TEST_DIR, newPerms);
} else if (Files.getFileStore(TEST_DIR).supportsFileAttributeView("acl")) {
System.out.println("Revoking owner's read access in ACLs for " + TEST_DIR);
AclFileAttributeView view = Files.getFileAttributeView(TEST_DIR, AclFileAttributeView.class);
acls = view.getAcl();
List<AclEntry> entries = new ArrayList<>();
UserPrincipal owner = view.getOwner();
// Deny owner
entries.add(AclEntry.newBuilder().setType(AclEntryType.DENY)
.setPrincipal(owner).setPermissions(AclEntryPermission.READ_DATA,
AclEntryPermission.READ_ATTRIBUTES, AclEntryPermission.READ_NAMED_ATTRS,
AclEntryPermission.EXECUTE)
.build());
// Revoke read data and execute
for (AclEntry entry : acls) {
Set<AclEntryPermission> perms =
new HashSet<>(entry.permissions());
if (entry.type() == AclEntryType.ALLOW) {
System.out.println("Revoking read access: " + entry);
perms.remove(AclEntryPermission.READ_DATA);
perms.remove(AclEntryPermission.LIST_DIRECTORY);
perms.remove(AclEntryPermission.READ_ATTRIBUTES);
perms.remove(AclEntryPermission.READ_NAMED_ATTRS);
perms.remove(AclEntryPermission.EXECUTE);
entries.add(AclEntry.newBuilder(entry).setPermissions(perms).build());
}
}
view.setAcl(entries);
System.out.println("ACLs: " + view.getAcl());
try {
System.out.println("File is readable: " + Files.isReadable(TEST_DIR));
Thread.sleep(50);
} catch (InterruptedException x) {}
// The above does not always work, skip the test if we can't make TEST_DIR
// read-only.
if (Files.isReadable(TEST_DIR)) {
throw new jtreg.SkippedException("Can't make directory read-only");
}
} else {
throw new RuntimeException("Required attribute view not supported");
}
System.out.println("File is readable: " + Files.isReadable(TEST_DIR));
}
private void restorePermissions() throws IOException {
if (Files.getFileStore(TEST_DIR).supportsFileAttributeView("posix")) {
if (posixPermissions != null) {
System.out.println("Restoring original POSIX permissions");
Files.setPosixFilePermissions(TEST_DIR, posixPermissions);
}
} else if (Files.getFileStore(TEST_DIR).supportsFileAttributeView("acl")) {
if (acls != null) {
System.out.println("Restoring original ACLs");
AclFileAttributeView view = Files.getFileAttributeView(TEST_DIR, AclFileAttributeView.class);
view.setAcl(acls);
}
} else {
throw new RuntimeException("Required attribute view not supported");
}
}
private void setupLogging() {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
}
private void createTestDir() throws IOException {
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
Files.createDirectories(TEST_DIR);
var file = Files.writeString(TEST_DIR.resolve("aFile.txt"), "some text", CREATE);
lastModifiedDir = getLastModified(TEST_DIR);
lastModifiedFile = getLastModified(file);
}
private void testDirectoryGET() throws Exception {
var expectedBody = openHTML + """
<h1>Directory listing for &#x2F;</h1>
<ul>
<li><a href="aFile.txt">aFile.txt</a></li>
</ul>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, TEST_DIR, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assert response.statusCode() == 200;
assert response.body().equals(expectedBody);
assert response.headers().firstValue("content-type").get().equals("text/html; charset=UTF-8");
assert response.headers().firstValue("content-length").get().equals(expectedLength);
assert response.headers().firstValue("last-modified").get().equals(lastModifiedDir);
} finally {
server.stop(0);
}
}
private void testFileGET() throws Exception {
var expectedBody = "some text";
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, TEST_DIR, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assert response.statusCode() == 200;
assert response.body().equals("some text");
assert response.headers().firstValue("content-type").get().equals("text/plain");
assert response.headers().firstValue("content-length").get().equals(expectedLength);
assert response.headers().firstValue("last-modified").get().equals(lastModifiedFile);
} finally {
server.stop(0);
}
}
private void testCreateHandler(){
try {
SimpleFileServer.createFileServer(LOOPBACK_ADDR, TEST_DIR, OutputLevel.NONE);
throw new RuntimeException("Handler creation expected to fail");
} catch (IllegalArgumentException expected) { }
try {
SimpleFileServer.createFileHandler(TEST_DIR);
throw new RuntimeException("Handler creation expected to fail");
} catch (IllegalArgumentException expected) { }
}
private static final String openHTML = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
""";
private static final String closeHTML = """
</body>
</html>
""";
private URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.buildUnchecked();
}
private String getLastModified(Path path) throws IOException {
return Files.getLastModifiedTime(path).toInstant().atZone(ZoneId.of("GMT"))
.format(DateTimeFormatter.RFC_1123_DATE_TIME);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2025, 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.
*/
/*
* @test
* @summary Tests file permission checks during the creation of a `FileServerHandler`
* @requires (os.family == "windows")
* @library /test/lib
* @build jdk.test.lib.net.URIBuilder
* @run main/manual/othervm -ea DocRootDirPermissionsWinTest true
* @run main/manual/othervm -ea DocRootDirPermissionsWinTest false
*/
public class DocRootDirPermissionsWinTest extends DocRootDirPermissionsTest{
public static void main(String[] args) throws Exception {
new DocRootDirPermissionsWinTest().run(args);
}
}

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Tests for FileServerHandler
* @run junit FileServerHandlerTest
*/
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpPrincipal;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class FileServerHandlerTest {
static final Path CWD = Path.of(".").toAbsolutePath();
static final Class<RuntimeException> RE = RuntimeException.class;
public static Object[][] notAllowedMethods() {
var l = List.of("POST", "PUT", "DELETE", "TRACE", "OPTIONS");
return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new);
}
@ParameterizedTest
@MethodSource("notAllowedMethods")
public void testNotAllowedRequestMethod(String requestMethod) throws Exception {
var handler = SimpleFileServer.createFileHandler(CWD);
var exchange = new MethodHttpExchange(requestMethod);
handler.handle(exchange);
assertEquals(405, exchange.rCode);
assertEquals("HEAD, GET", exchange.getResponseHeaders().getFirst("allow"));
}
public static Object[][] notImplementedMethods() {
var l = List.of("GARBAGE", "RUBBISH", "TRASH", "FOO", "BAR");
return l.stream().map(s -> new Object[] { s }).toArray(Object[][]::new);
}
@ParameterizedTest
@MethodSource("notImplementedMethods")
public void testNotImplementedRequestMethod(String requestMethod) throws Exception {
var handler = SimpleFileServer.createFileHandler(CWD);
var exchange = new MethodHttpExchange(requestMethod);
handler.handle(exchange);
assertEquals(501, exchange.rCode);
}
// 301 and 404 response codes tested in SimpleFileServerTest
@Test
public void testThrowingExchange() {
var h = SimpleFileServer.createFileHandler(CWD);
{
var exchange = new ThrowingHttpExchange("GET") {
public InputStream getRequestBody() {
throw new RuntimeException("getRequestBody");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("getRequestBody", t.getMessage());
}
{
var exchange = new ThrowingHttpExchange("GET") {
public Headers getResponseHeaders() {
throw new RuntimeException("getResponseHeaders");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("getResponseHeaders", t.getMessage());
}
{
var exchange = new ThrowingHttpExchange("GET") {
public void sendResponseHeaders(int rCode, long responseLength) {
throw new RuntimeException("sendResponseHeaders");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("sendResponseHeaders", t.getMessage());
}
{
var exchange = new ThrowingHttpExchange("GET") {
public OutputStream getResponseBody() {
throw new RuntimeException("getResponseBody");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("getResponseBody", t.getMessage());
}
{
var exchange = new ThrowingHttpExchange("GET") {
public void close() {
throw new RuntimeException("close");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("close", t.getMessage());
}
}
static class ThrowingHttpExchange extends StubHttpExchange {
private final String method;
volatile int rCode;
volatile long responseLength;
volatile Headers responseHeaders;
volatile Headers requestHeaders;
volatile InputStream requestBody;
ThrowingHttpExchange(String method) {
this.method = method;
responseHeaders = new Headers();
requestHeaders = new Headers();
requestBody = new ByteArrayInputStream(new byte[]{});
}
@Override public String getRequestMethod() { return method; }
@Override public Headers getResponseHeaders() { return responseHeaders; }
@Override public Headers getRequestHeaders() { return requestHeaders; }
@Override public InputStream getRequestBody() { return requestBody; }
@Override public URI getRequestURI() { return URI.create("/"); }
@Override public OutputStream getResponseBody() {
return OutputStream.nullOutputStream();
}
@Override public void sendResponseHeaders(int rCode, long responseLength) {
this.rCode = rCode;
this.responseLength = responseLength;
}
@Override public HttpContext getHttpContext() {
return new HttpContext() {
@Override public HttpHandler getHandler() { return null; }
@Override public void setHandler(HttpHandler handler) { }
@Override public String getPath() {
return "/";
}
@Override public HttpServer getServer() {
return null;
}
@Override public Map<String, Object> getAttributes() {
return null;
}
@Override public List<Filter> getFilters() {
return null;
}
@Override public Authenticator setAuthenticator(Authenticator auth) {
return null;
}
@Override public Authenticator getAuthenticator() {
return null;
}
};
}
}
static class MethodHttpExchange extends StubHttpExchange {
private final String method;
volatile int rCode;
volatile long responseLength;
volatile Headers responseHeaders;
volatile InputStream requestBody;
MethodHttpExchange(String method) {
this.method = method;
responseHeaders = new Headers();
requestBody = InputStream.nullInputStream();
}
@Override public String getRequestMethod() { return method; }
@Override public Headers getResponseHeaders() { return responseHeaders; }
@Override public InputStream getRequestBody() { return requestBody; }
@Override public void sendResponseHeaders(int rCode, long responseLength) {
this.rCode = rCode;
this.responseLength = responseLength;
}
}
static class StubHttpExchange extends HttpExchange {
@Override public Headers getRequestHeaders() { return null; }
@Override public Headers getResponseHeaders() { return null; }
@Override public URI getRequestURI() { return null; }
@Override public String getRequestMethod() { return null; }
@Override public void close() { }
@Override public InputStream getRequestBody() { return null; }
@Override public OutputStream getResponseBody() { return null; }
@Override public HttpContext getHttpContext() { return null; }
@Override public void sendResponseHeaders(int rCode, long responseLength) { }
@Override public InetSocketAddress getRemoteAddress() { return null; }
@Override public int getResponseCode() { return 0; }
@Override public InetSocketAddress getLocalAddress() { return null; }
@Override public String getProtocol() { return null; }
@Override public Object getAttribute(String name) { return null; }
@Override public void setAttribute(String name, Object value) { }
@Override public void setStreams(InputStream i, OutputStream o) { }
@Override public HttpPrincipal getPrincipal() { return null; }
}
}

View file

@ -0,0 +1,364 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Tests for HttpHandlers
* @library /test/lib
* @build jdk.test.lib.net.URIBuilder
* @run junit/othervm HttpHandlersTest
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import jdk.test.lib.net.URIBuilder;
import com.sun.net.httpserver.*;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class HttpHandlersTest {
static final Class<NullPointerException> NPE = NullPointerException.class;
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
static final Class<RuntimeException> RE = RuntimeException.class;
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
@BeforeAll
public static void setup() {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
}
@Test
public void testNull() {
final var handler = new TestHandler();
assertThrows(NPE, () -> HttpHandlers.handleOrElse(null, handler, new TestHandler()));
assertThrows(NPE, () -> HttpHandlers.handleOrElse(p -> true, null, handler));
assertThrows(NPE, () -> HttpHandlers.handleOrElse(p -> true, handler, null));
final var headers = new Headers();
final var body = "";
assertThrows(NPE, () -> HttpHandlers.of(200, null, body));
assertThrows(NPE, () -> HttpHandlers.of(200, headers, null));
}
@Test
public void testOfStatusCode() {
final var headers = new Headers();
final var body = "";
assertThrows(IAE, () -> HttpHandlers.of(99, headers, body));
assertThrows(IAE, () -> HttpHandlers.of(1000, headers, body));
}
@Test
public void testOfNoBody() throws Exception {
var handler = HttpHandlers.of(200, Headers.of("foo", "bar"), "");
var server = HttpServer.create(LOOPBACK_ADDR, 0);
server.createContext("/", handler);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertTrue(response.headers().map().containsKey("date"));
assertEquals("bar", response.headers().firstValue("foo").get());
assertEquals("0", response.headers().firstValue("content-length").get());
assertEquals(3, response.headers().map().size());
assertEquals(200, response.statusCode());
assertEquals("", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testOfWithBody() throws Exception {
var handler = HttpHandlers.of(200, Headers.of("foo", "bar"), "hello world");
var expectedLength = Integer.toString("hello world".getBytes(UTF_8).length);
var server = HttpServer.create(LOOPBACK_ADDR, 0);
server.createContext("/", handler);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertTrue(response.headers().map().containsKey("date"));
assertEquals("bar", response.headers().firstValue("foo").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(3, response.headers().map().size());
assertEquals(200, response.statusCode());
assertEquals("hello world", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testOfHeadRequest() throws Exception {
var handler = HttpHandlers.of(200, Headers.of("content-length", "999"), "hello world");
var expectedLength = Integer.toString("hello world".getBytes(UTF_8).length);
var server = HttpServer.create(LOOPBACK_ADDR, 0);
server.createContext("/", handler);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, ""))
.method("HEAD", BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertTrue(response.headers().map().containsKey("date"));
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(2, response.headers().map().size());
assertEquals(200, response.statusCode());
} finally {
server.stop(0);
}
}
@Test
public void testOfOverwriteHeaders() throws Exception {
var headers = Headers.of("content-length", "1000", "date", "12345");
var handler = HttpHandlers.of(200, headers, "hello world");
var expectedLength = Integer.toString("hello world".getBytes(UTF_8).length);
var server = HttpServer.create(LOOPBACK_ADDR, 0);
server.createContext("/", handler);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertNotEquals("12345", response.headers().firstValue("date").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(2, response.headers().map().size());
assertEquals(200, response.statusCode());
assertEquals("hello world", response.body());
} finally {
server.stop(0);
}
}
public static Object[][] responseBodies() {
return new Object[][] { {"hello world"}, {""} };
}
@ParameterizedTest
@MethodSource("responseBodies")
public void testOfThrowingExchange(String body) {
var h = HttpHandlers.of(200, Headers.of(), body);
{
var exchange = new ThrowingHttpExchange() {
public InputStream getRequestBody() {
throw new RuntimeException("getRequestBody");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("getRequestBody", t.getMessage());
}
{
var exchange = new ThrowingHttpExchange() {
public Headers getResponseHeaders() {
throw new RuntimeException("getResponseHeaders");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("getResponseHeaders", t.getMessage());
}
{
var exchange = new ThrowingHttpExchange() {
public void sendResponseHeaders(int rCode, long responseLength) {
throw new RuntimeException("sendResponseHeaders");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("sendResponseHeaders", t.getMessage());
}
{
var exchange = new ThrowingHttpExchange() {
public OutputStream getResponseBody() {
throw new RuntimeException("getResponseBody");
}
};
if (!body.isEmpty()) { // getResponseBody not called if no responseBody
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("getResponseBody", t.getMessage());
}
}
{
var exchange = new ThrowingHttpExchange() {
public void close() {
throw new RuntimeException("close");
}
};
var t = assertThrows(RE, () -> h.handle(exchange));
assertEquals("close", t.getMessage());
}
}
@Test
public void testHandleOrElseTrue() throws Exception {
var h1 = new TestHandler("TestHandler-1");
var h2 = new TestHandler("TestHandler-2");
var handler = HttpHandlers.handleOrElse(p -> true, h1, h2);
var server = HttpServer.create(LOOPBACK_ADDR, 0);
server.createContext("/", handler);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("TestHandler-1", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testHandleOrElseFalse() throws Exception {
var h1 = new TestHandler("TestHandler-1");
var h2 = new TestHandler("TestHandler-2");
var handler = HttpHandlers.handleOrElse(p -> false, h1, h2);
var server = HttpServer.create(LOOPBACK_ADDR, 0);
server.createContext("/", handler);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("TestHandler-2", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testHandleOrElseNested() throws Exception {
var h1 = new TestHandler("TestHandler-1");
var h2 = new TestHandler("TestHandler-2");
var h3 = new TestHandler("TestHandler-3");
var h4 = HttpHandlers.handleOrElse(p -> false, h1, h2);
var handler = HttpHandlers.handleOrElse(p -> false, h3, h4);
var server = HttpServer.create(LOOPBACK_ADDR, 0);
server.createContext("/", handler);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("TestHandler-2", response.body());
} finally {
server.stop(0);
}
}
/**
* A test handler that discards the request and returns its name
*/
static class TestHandler implements HttpHandler {
final String name;
TestHandler(String name) { this.name = name; }
TestHandler() { this("no name"); }
@Override
public void handle(HttpExchange exchange) throws IOException {
try (InputStream is = exchange.getRequestBody();
OutputStream os = exchange.getResponseBody()) {
is.readAllBytes();
var resp = name.getBytes(UTF_8);
exchange.sendResponseHeaders(200, resp.length);
os.write(resp);
}
}
}
static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.buildUnchecked();
}
static class ThrowingHttpExchange extends HttpExchange {
static final String requestMethod = "GET";
volatile int responseCode;
volatile long responseLength;
volatile Headers responseHeaders;
volatile InputStream requestBody;
ThrowingHttpExchange() {
responseHeaders = new Headers();
this.requestBody = InputStream.nullInputStream();
}
@Override public Headers getResponseHeaders() { return responseHeaders; }
@Override public InputStream getRequestBody() { return requestBody; }
@Override public void sendResponseHeaders(int rCode, long responseLength) {
this.responseCode = rCode;
this.responseLength = responseLength;
}
@Override public OutputStream getResponseBody() {
return OutputStream.nullOutputStream();
}
@Override public String getRequestMethod() { return requestMethod; }
@Override public Headers getRequestHeaders() { return null; }
@Override public URI getRequestURI() { return null; }
@Override public HttpContext getHttpContext() { return null; }
@Override public void close() { }
@Override public InetSocketAddress getRemoteAddress() { return null; }
@Override public int getResponseCode() { return 0; }
@Override public InetSocketAddress getLocalAddress() { return null; }
@Override public String getProtocol() { return null; }
@Override public Object getAttribute(String name) { return null; }
@Override public void setAttribute(String name, Object value) { }
@Override public void setStreams(InputStream i, OutputStream o) { }
@Override public HttpPrincipal getPrincipal() { return null; }
}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2023, 2025, 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.
*/
/*
* @test
* @bug 8315436
* @summary Test if HttpsServer sends the TLS alerts produced
* @library /test/lib
* @build jdk.test.lib.net.SimpleSSLContext
* @run junit/othervm HttpsServerAlertTest
*/
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;
import jdk.test.lib.net.SimpleSSLContext;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.EOFException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class HttpsServerAlertTest {
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
private static final SSLContext sslContext = SimpleSSLContext.findSSLContext();
static {
SSLContext.setDefault(sslContext);
}
@BeforeAll
public static void setup() throws IOException {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
}
@Test
public void testProtocolMismatch() throws Exception {
SSLSocketFactory sf = sslContext.getSocketFactory();
var server = HttpsServer.create(LOOPBACK_ADDR, 0);
server.setHttpsConfigurator(new Configurator(sslContext));
server.start();
try (SSLSocket s = (SSLSocket) sf.createSocket()) {
// server only accepts TLS 1.3
s.setEnabledProtocols(new String[]{"TLSv1.2"});
s.connect(server.getAddress());
s.startHandshake();
fail("Expected a handshake failure");
} catch (SSLHandshakeException e) {
System.out.println("Got exception: " + e);
if (e.getCause() instanceof EOFException ||
!e.getMessage().contains("protocol_version"))
throw e;
} finally {
server.stop(0);
}
}
private static class Configurator extends HttpsConfigurator {
public Configurator(SSLContext sslContext) {
super(sslContext);
}
@Override
public void configure(HttpsParameters params) {
SSLParameters sslParams = getSSLContext().getDefaultSSLParameters();
sslParams.setProtocols(new String[]{"TLSv1.3"});
params.setSSLParameters(sslParams);
}
}
}

View file

@ -0,0 +1,174 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Test for HttpsServer::create
* @library /test/lib
* @build jdk.test.lib.Platform jdk.test.lib.net.URIBuilder
* @run junit/othervm HttpsServerTest
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsServer;
import javax.net.ssl.SSLContext;
import jdk.test.lib.net.SimpleSSLContext;
import jdk.test.lib.net.URIBuilder;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class HttpsServerTest {
static final Class<NullPointerException> NPE = NullPointerException.class;
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
private static final SSLContext sslContext = SimpleSSLContext.findSSLContext();
static {
SSLContext.setDefault(sslContext);
}
@BeforeAll
public static void setup() throws IOException {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
}
@Test
public void testNull() {
assertThrows(NPE, () -> HttpsServer.create(null, 0, null, new Handler()));
assertThrows(NPE, () -> HttpsServer.create(null, 0, "/", null));
assertThrows(NPE, () -> HttpsServer.create(null, 0, "/", new Handler(), (Filter)null));
assertThrows(NPE, () -> HttpsServer.create(null, 0, "/", new Handler(), new Filter[]{null}));
}
@Test
public void testCreate() throws IOException {
assertNull(HttpsServer.create().getAddress());
final var s1 = HttpsServer.create(null, 0);
assertNull(s1.getAddress());
s1.bind((LOOPBACK_ADDR), 0);
assertEquals(LOOPBACK_ADDR.getAddress(), s1.getAddress().getAddress());
final var s2 = HttpsServer.create(null, 0, "/foo/", new Handler());
assertNull(s2.getAddress());
s2.bind(LOOPBACK_ADDR, 0);
assertEquals(LOOPBACK_ADDR.getAddress(), s2.getAddress().getAddress());
s2.removeContext("/foo/"); // throws if context doesn't exist
}
@Test
public void testExchange() throws Exception {
var filter = new Filter();
var server = HttpsServer.create(LOOPBACK_ADDR, 0, "/test", new Handler(), filter);
server.setHttpsConfigurator(new HttpsConfigurator(sslContext));
server.start();
try {
var client = HttpClient.newBuilder()
.proxy(NO_PROXY)
.sslContext(sslContext)
.build();
var request = HttpRequest.newBuilder(uri(server, "/test")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("hello world", response.body());
assertEquals( Integer.toString("hello world".length()), response.headers().firstValue("content-length").get());
assertEquals(filter.responseCode.get().intValue(), response.statusCode());
} finally {
server.stop(0);
}
}
static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.scheme("https")
.host("localhost")
.port(server.getAddress().getPort())
.path(path)
.buildUnchecked();
}
/**
* A test handler that discards the request and sends a response
*/
static class Handler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
try (InputStream is = exchange.getRequestBody();
OutputStream os = exchange.getResponseBody()) {
is.readAllBytes();
var resp = "hello world".getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, resp.length);
os.write(resp);
}
}
}
/**
* A test post-processing filter that captures the response code
*/
static class Filter extends com.sun.net.httpserver.Filter {
final CompletableFuture<Integer> responseCode = new CompletableFuture<>();
@Override
public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
chain.doFilter(exchange);
responseCode.complete(exchange.getResponseCode());
}
@Override
public String description() {
return "HttpsServerTest Filter";
}
}
}

View file

@ -0,0 +1,147 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import jdk.test.lib.net.URIBuilder;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.nio.file.StandardOpenOption.CREATE;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
/*
* @test
* @summary Test idempotency and commutativity of responses with an exhaustive
* set of binary request sequences
* @library /test/lib
* @build jdk.test.lib.net.URIBuilder
* @run junit/othervm IdempotencyAndCommutativityTest
*/
public class IdempotencyAndCommutativityTest {
static final Path CWD = Path.of(".").toAbsolutePath().normalize();
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final String FILE_NAME = "file.txt";
static final String DIR_NAME = "";
static final String MISSING_FILE_NAME = "doesNotExist";
static HttpServer server;
static HttpClient client;
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
@BeforeAll
public static void setup() throws IOException {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
Path root = Files.createDirectories(CWD.resolve("testDirectory"));
Files.writeString(root.resolve(FILE_NAME), "some text", CREATE);
client = HttpClient.newBuilder().proxy(NO_PROXY).build();
server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, SimpleFileServer.OutputLevel.VERBOSE);
server.start();
}
// Container of expected response state for a given request
record ExchangeValues(String method, String resource, int respCode, String contentType) {}
// Creates an exhaustive set of binary exchange sequences
public static Object[][] allBinarySequences() {
final List<ExchangeValues> sequences = List.of(
new ExchangeValues("GET", FILE_NAME, 200, "text/plain"),
new ExchangeValues("GET", DIR_NAME, 200, "text/html; charset=UTF-8"),
new ExchangeValues("GET", MISSING_FILE_NAME, 404, "text/html; charset=UTF-8"),
new ExchangeValues("HEAD", FILE_NAME, 200, "text/plain"),
new ExchangeValues("HEAD", DIR_NAME, 200, "text/html; charset=UTF-8"),
new ExchangeValues("HEAD", MISSING_FILE_NAME, 404, "text/html; charset=UTF-8"),
new ExchangeValues("UNKNOWN", FILE_NAME, 501, null),
new ExchangeValues("UNKNOWN", DIR_NAME, 501, null),
new ExchangeValues("UNKNOWN", MISSING_FILE_NAME, 501, null)
);
return sequences.stream() // cartesian product
.flatMap(s1 -> sequences.stream().map(s2 -> new ExchangeValues[] { s1, s2 }))
.toArray(Object[][]::new);
}
@ParameterizedTest
@MethodSource("allBinarySequences")
public void testBinarySequences(ExchangeValues e1, ExchangeValues e2) throws Exception {
System.out.println("---");
System.out.println(e1);
executeExchange(e1);
System.out.println(e2);
executeExchange(e2);
}
private static void executeExchange(ExchangeValues e) throws Exception {
var request = HttpRequest.newBuilder(uri(server, e.resource()))
.method(e.method(), HttpRequest.BodyPublishers.noBody())
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(e.respCode(), response.statusCode());
if (e.contentType != null) {
assertEquals(e.contentType(), response.headers().firstValue("content-type").get());
} else {
assertTrue(response.headers().firstValue("content-type").isEmpty());
}
}
@AfterAll
public static void teardown() {
server.stop(0);
}
static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.buildUnchecked();
}
}

View file

@ -0,0 +1,391 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Tests the FileServerHandler's mapping of request URI path to file
* system path
* @library /test/lib
* @build jdk.test.lib.Platform jdk.test.lib.net.URIBuilder
* @run junit/othervm MapToPathTest
*/
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import com.sun.net.httpserver.Filter;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpHandlers;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;
import jdk.test.lib.net.URIBuilder;
import jdk.test.lib.util.FileUtils;
import static java.lang.System.out;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.nio.file.StandardOpenOption.CREATE;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class MapToPathTest {
static final Path CWD = Path.of(".").toAbsolutePath();
static final Path TEST_DIR = CWD.resolve("MapToPathTest").normalize();
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final Filter OUTPUT_FILTER = SimpleFileServer.createOutputFilter(out, OutputLevel.VERBOSE);
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
@BeforeAll
public static void setup() throws IOException {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
createDirectories(TEST_DIR);
}
private static void createDirectories(Path testDir) throws IOException {
// Create directory tree:
//
// |-- TEST_DIR
// |-- foo
// |-- bar
// |-- baz
// |-- file.txt
// |-- file.txt
// |-- foobar
// |-- file.txt
// |-- file.txt
Files.createDirectories(TEST_DIR);
Stream.of("foo", "foobar", "foo/bar/baz").forEach(s -> {
try {
Path p = testDir.resolve(s);
Files.createDirectories(p);
Files.writeString(p.resolve("file.txt"), s, CREATE);
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
});
Files.writeString(testDir.resolve("file.txt"), "testdir", CREATE);
}
@Test
public void test() throws Exception {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
{
var handler = SimpleFileServer.createFileHandler(TEST_DIR);
var server = HttpServer.create(LOOPBACK_ADDR, 10, "/", handler, OUTPUT_FILTER);
server.start();
try {
var req1 = HttpRequest.newBuilder(uri(server, "/")).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(200, res1.statusCode());
assertEquals("text/html; charset=UTF-8", res1.headers().firstValue("content-type").get());
assertEquals(Long.toString(257L), res1.headers().firstValue("content-length").get());
assertEquals(getLastModified(TEST_DIR), res1.headers().firstValue("last-modified").get());
var req2 = HttpRequest.newBuilder(uri(server, "/../")).build();
var res2 = client.send(req2, BodyHandlers.ofString());
assertEquals(404, res2.statusCode()); // cannot escape root
var req3 = HttpRequest.newBuilder(uri(server, "/foo/bar/baz/c://")).build();
var res3 = client.send(req3, BodyHandlers.ofString());
assertEquals(404, res3.statusCode()); // not found
var req4 = HttpRequest.newBuilder(uri(server, "/foo/bar/baz/c:.//")).build();
var res4 = client.send(req4, BodyHandlers.ofString());
assertEquals(404, res4.statusCode()); // not found
var req5 = HttpRequest.newBuilder(uri(server, "/foo/bar/baz/c:..//")).build();
var res5 = client.send(req5, BodyHandlers.ofString());
assertEquals(404, res5.statusCode()); // not found
var req6 = HttpRequest.newBuilder(uri(server, "/foo/file:" + TEST_DIR.getParent())).build();
var res6 = client.send(req6, BodyHandlers.ofString());
assertEquals(404, res6.statusCode()); // not found
var req7 = HttpRequest.newBuilder(uri(server, "/foo/bar/\\..\\../")).build();
var res7 = client.send(req7, BodyHandlers.ofString());
assertEquals(404, res7.statusCode()); // not found
var req8 = HttpRequest.newBuilder(uri(server, "/foo")).build();
var res8 = client.send(req8, BodyHandlers.ofString());
assertEquals(301, res8.statusCode()); // redirect
assertEquals("0", res8.headers().firstValue("content-length").get());
assertEquals("/foo/", res8.headers().firstValue("location").get());
} finally {
server.stop(0);
}
}
{
var handler = SimpleFileServer.createFileHandler(TEST_DIR);
var server = HttpServer.create(LOOPBACK_ADDR, 10, "/browse/", handler, OUTPUT_FILTER);
server.start();
try {
var req1 = HttpRequest.newBuilder(uri(server, "/browse/file.txt")).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(200, res1.statusCode());
assertEquals("testdir", res1.body());
assertEquals("text/plain", res1.headers().firstValue("content-type").get());
assertEquals(Long.toString(7L), res1.headers().firstValue("content-length").get());
assertEquals(getLastModified(TEST_DIR.resolve("file.txt")), res1.headers().firstValue("last-modified").get());
var req2 = HttpRequest.newBuilder(uri(server, "/store/file.txt")).build();
var res2 = client.send(req2, BodyHandlers.ofString());
assertEquals(404, res2.statusCode()); // no context found
} finally {
server.stop(0);
}
}
{
// Test "/foo/" context (with trailing slash)
var handler = SimpleFileServer.createFileHandler(TEST_DIR.resolve("foo"));
var server = HttpServer.create(LOOPBACK_ADDR, 10, "/foo/", handler, OUTPUT_FILTER);
server.start();
try {
var req1 = HttpRequest.newBuilder(uri(server, "/foo/file.txt")).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(200, res1.statusCode());
assertEquals("foo", res1.body());
assertEquals("text/plain", res1.headers().firstValue("content-type").get());
assertEquals(Long.toString(3L), res1.headers().firstValue("content-length").get());
assertEquals(getLastModified(TEST_DIR.resolve("foo").resolve("file.txt")), res1.headers().firstValue("last-modified").get());
var req2 = HttpRequest.newBuilder(uri(server, "/foobar/file.txt")).build();
var res2 = client.send(req2, BodyHandlers.ofString());
assertEquals(404, res2.statusCode()); // no context found
var req3 = HttpRequest.newBuilder(uri(server, "/foo/../foobar/file.txt")).build();
var res3 = client.send(req3, BodyHandlers.ofString());
assertEquals(404, res3.statusCode()); // cannot escape context
var req4 = HttpRequest.newBuilder(uri(server, "/foo/../..")).build();
var res4 = client.send(req4, BodyHandlers.ofString());
assertEquals(404, res4.statusCode()); // cannot escape root
var req5 = HttpRequest.newBuilder(uri(server, "/foo/bar")).build();
var res5 = client.send(req5, BodyHandlers.ofString());
assertEquals(301, res5.statusCode()); // redirect
assertEquals("0", res5.headers().firstValue("content-length").get());
assertEquals("/foo/bar/", res5.headers().firstValue("location").get());
} finally {
server.stop(0);
}
}
{
// Test "/foo" context (without trailing slash)
var handler = SimpleFileServer.createFileHandler(TEST_DIR.resolve("foo"));
var server = HttpServer.create(LOOPBACK_ADDR, 10, "/foo", handler, OUTPUT_FILTER);
server.start();
try {
var req1 = HttpRequest.newBuilder(uri(server, "/foo/file.txt")).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(200, res1.statusCode());
assertEquals("foo", res1.body());
assertEquals("text/plain", res1.headers().firstValue("content-type").get());
assertEquals(Long.toString(3L), res1.headers().firstValue("content-length").get());
assertEquals(getLastModified(TEST_DIR.resolve("foo").resolve("file.txt")), res1.headers().firstValue("last-modified").get());
var req2 = HttpRequest.newBuilder(uri(server, "/foobar/")).build();
var res2 = client.send(req2, BodyHandlers.ofString());
assertEquals(404, res2.statusCode()); // handler prevents mapping to /foo/bar
var req3 = HttpRequest.newBuilder(uri(server, "/foobar/file.txt")).build();
var res3 = client.send(req3, BodyHandlers.ofString());
assertEquals(404, res3.statusCode()); // handler prevents mapping to /foo/bar/file.txt
var req4 = HttpRequest.newBuilder(uri(server, "/file.txt")).build();
var res4 = client.send(req4, BodyHandlers.ofString());
assertEquals(404, res4.statusCode());
var req5 = HttpRequest.newBuilder(uri(server, "/foo/bar")).build();
var res5 = client.send(req5, BodyHandlers.ofString());
assertEquals(301, res5.statusCode()); // redirect
assertEquals("0", res5.headers().firstValue("content-length").get());
assertEquals("/foo/bar/", res5.headers().firstValue("location").get());
var req6 = HttpRequest.newBuilder(uri(server, "/foo")).build();
var res6 = client.send(req6, BodyHandlers.ofString());
assertEquals(301, res6.statusCode()); // redirect
assertEquals("0", res6.headers().firstValue("content-length").get());
assertEquals("/foo/", res6.headers().firstValue("location").get());
} finally {
server.stop(0);
}
}
{
// Test that a request path segment that is a Windows root drive
// does not circumvent access restrictions.
//
// For example, given the test directory tree:
//
// |-- TEST_DIR
// |-- foo
// |-- bar ----->>> if hidden, itself and any of its subdirectories are not accessible
// |-- baz
// |-- file.txt
// ...
var handler = SimpleFileServer.createFileHandler(TEST_DIR);
var server = HttpServer.create(LOOPBACK_ADDR, 10, "/", handler, OUTPUT_FILTER);
server.start();
try {
var req1 = HttpRequest.newBuilder(uri(server, "/foo/bar/c:/baz/")).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(404, res1.statusCode()); // not found
} finally {
server.stop(0);
}
}
}
// Tests with a mixture of in-memory and file handlers.
@Test
public void multipleContexts() throws Exception {
var rootHandler = HttpHandlers.of(200, Headers.of(), "root response body");
var fooHandler = SimpleFileServer.createFileHandler(TEST_DIR.resolve("foo"));
var foobarHandler = SimpleFileServer.createFileHandler(TEST_DIR.resolve("foobar"));
var barHandler = HttpHandlers.of(200, Headers.of(), "bar response body");
var server = HttpServer.create(LOOPBACK_ADDR, 0);
server.createContext("/", rootHandler);
server.createContext("/foo/", fooHandler);
server.createContext("/bar/", barHandler);
server.createContext("/foobar/", foobarHandler);
server.start();
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
try {
for (String uriPath : List.of("/", "/blah", "/xyz/t/z", "/txt") ) {
out.println("uri.Path=" + uriPath);
var req1 = HttpRequest.newBuilder(uri(server, uriPath)).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(200, res1.statusCode());
assertEquals("root response body", res1.body());
}
{
var req1 = HttpRequest.newBuilder(uri(server, "/foo/file.txt")).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(200, res1.statusCode());
assertEquals("foo", res1.body());
var req2 = HttpRequest.newBuilder(uri(server, "/foo/bar/baz/file.txt")).build();
var res2 = client.send(req2, BodyHandlers.ofString());
assertEquals(200, res2.statusCode());
assertEquals("foo/bar/baz", res2.body());
}
{
var req1 = HttpRequest.newBuilder(uri(server, "/foobar/file.txt")).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(200, res1.statusCode());
assertEquals("foobar", res1.body());
}
for (String uriPath : List.of("/bar/", "/bar/t", "/bar/t/z", "/bar/index.html") ) {
out.println("uri.Path=" + uriPath);
var req1 = HttpRequest.newBuilder(uri(server, uriPath)).build();
var res1 = client.send(req1, BodyHandlers.ofString());
assertEquals(200, res1.statusCode());
assertEquals("bar response body", res1.body());
}
} finally {
server.stop(0);
}
}
// Tests requests with queries, which are simply ignored by the handler
@Test
public void requestWithQuery() throws Exception {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var handler = SimpleFileServer.createFileHandler(TEST_DIR);
var server = HttpServer.create(LOOPBACK_ADDR, 10, "/", handler, OUTPUT_FILTER);
server.start();
try {
for (String query : List.of("x=y", "x=", "xxx", "#:?") ) {
out.println("uri.Query=" + query);
var req = HttpRequest.newBuilder(uri(server, "", query)).build();
var res = client.send(req, BodyHandlers.ofString());
assertEquals(200, res.statusCode());
assertEquals("text/html; charset=UTF-8", res.headers().firstValue("content-type").get());
assertEquals(Long.toString(257L), res.headers().firstValue("content-length").get());
assertEquals(getLastModified(TEST_DIR), res.headers().firstValue("last-modified").get());
}
} finally {
server.stop(0);
}
}
@AfterAll
public static void teardown() throws IOException {
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
}
static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path(path)
.buildUnchecked();
}
static URI uri(HttpServer server, String path, String query) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path(path)
.query(query)
.buildUnchecked();
}
static String getLastModified(Path path) throws IOException {
return Files.getLastModifiedTime(path).toInstant().atZone(ZoneId.of("GMT"))
.format(DateTimeFormatter.RFC_1123_DATE_TIME);
}
}

View file

@ -0,0 +1,344 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Tests for OutputFilter
* @modules java.base/sun.net.www:+open
* @library /test/lib
* @build jdk.test.lib.net.URIBuilder
* @run junit/othervm -Djdk.httpclient.redirects.retrylimit=1 OutputFilterTest
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;
import jdk.test.lib.net.URIBuilder;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.nio.charset.StandardCharsets.*;
import static com.sun.net.httpserver.SimpleFileServer.OutputLevel.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class OutputFilterTest {
static final Class<NullPointerException> NPE = NullPointerException.class;
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
static final Class<IOException> IOE = IOException.class;
static final OutputStream OUT = new ByteArrayOutputStream();
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final boolean ENABLE_LOGGING = true;
static final Logger logger = Logger.getLogger("com.sun.net.httpserver");
@BeforeAll
public static void setup() {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
logger.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
logger.addHandler(ch);
}
}
@Test
public void testNull() {
assertThrows(NPE, () -> SimpleFileServer.createOutputFilter(null, null));
assertThrows(NPE, () -> SimpleFileServer.createOutputFilter(null, VERBOSE));
assertThrows(NPE, () -> SimpleFileServer.createOutputFilter(OUT, null));
}
@Test
public void testDescription() {
var filter = SimpleFileServer.createOutputFilter(OUT, VERBOSE);
assertEquals("HttpExchange OutputFilter (outputLevel: VERBOSE)", filter.description());
filter = SimpleFileServer.createOutputFilter(OUT, INFO);
assertEquals("HttpExchange OutputFilter (outputLevel: INFO)", filter.description());
}
@Test
public void testNONE() {
assertThrows(IAE, () -> SimpleFileServer.createOutputFilter(OUT, NONE));
}
/**
* Confirms that the output filter produces the expected output for a
* successful exchange (with the request-path attribute set.)
*/
@Test
public void testExchange() throws Exception {
var baos = new ByteArrayOutputStream();
var handler = new RequestPathHandler();
var filter = SimpleFileServer.createOutputFilter(baos, VERBOSE);
var server = HttpServer.create(LOOPBACK_ADDR, 10, "/", handler, filter);
server.start();
try (baos) {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(3, response.headers().map().size());
assertEquals("hello world", response.body());
} finally {
server.stop(0);
baos.flush();
var filterOutput = baos.toString(UTF_8);
var pattern = Pattern.compile("""
127\\.0\\.0\\.1 - - \\[[\\s\\S]+] "GET / HTTP/1\\.1" 200 -
Resource requested: /foo/bar
(>[\\s\\S]+:[\\s\\S]+)+
>
(<[\\s\\S]+:[\\s\\S]+)+
<
""".replaceAll("\n", System.lineSeparator()));
assertTrue(pattern.matcher(filterOutput).matches());
/*
* Expected output format:
* """
* 127.0.0.1 - - [06/Jul/2021:12:56:47 +0100] "GET / HTTP/1.1" 200 -
* Resource requested: /foo/bar
* > Connection: Upgrade, HTTP2-Settings
* > Http2-settings: AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA
* > Host: localhost:59146
* > Upgrade: h2c
* > User-agent: Java-http-client/18-internal
* > Content-length: 0
* >
* < Date: Tue, 06 Jul 2021 11:56:47 GMT
* < Content-length: 11
* <
* """;
*/
}
}
/**
* Confirms that the output filter produces the expected output for
* a successful exchange (without the request-path attribute set.)
*/
@Test
public void testExchangeWithoutRequestPath() throws Exception {
var baos = new ByteArrayOutputStream();
var handler = new NoRequestPathHandler();
var filter = SimpleFileServer.createOutputFilter(baos, VERBOSE);
var server = HttpServer.create(LOOPBACK_ADDR, 10, "/", handler, filter);
server.start();
try (baos) {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(2, response.headers().map().size());
assertEquals("hello world", response.body());
} finally {
server.stop(0);
baos.flush();
var filterOutput = baos.toString(UTF_8);
var pattern = Pattern.compile("""
127\\.0\\.0\\.1 - - \\[[\\s\\S]+] "GET / HTTP/1\\.1" 200 -
(>[\\s\\S]+:[\\s\\S]+)+
>
(<[\\s\\S]+:[\\s\\S]+)+
<
""".replaceAll("\n", System.lineSeparator()));
assertTrue(pattern.matcher(filterOutput).matches());
/*
* Expected output:
* """
* 127.0.0.1 - - [12/Jul/2021:10:05:10 +0000] "GET / HTTP/1.1" 200 -
* > Connection: Upgrade, HTTP2-Settings
* > Http2-settings: AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA
* > Host: localhost:57931
* > Upgrade: h2c
* > User-agent: Java-http-client/18-internal
* > Content-length: 0
* >
* < Date: Mon, 12 Jul 2021 10:05:10 GMT
* < Content-length: 11
* <
* """;
*/
}
}
public static Object[][] throwingHandler() {
return new Object[][] {
{VERBOSE, "Error: server exchange handling failed: IOE ThrowingHandler" + System.lineSeparator()},
{INFO, "Error: server exchange handling failed: IOE ThrowingHandler" + System.lineSeparator()},
{NONE, ""}
};
}
/**
* Confirms that the output filter captures a throwable that is thrown
* during the exchange handling and prints the expected error message.
* The "httpclient.redirects.retrylimit" system property is set to 1 to
* prevent retries on the client side, which would result in more than one
* error message.
*/
@ParameterizedTest
@MethodSource("throwingHandler")
public void testExchangeThrowingHandler(OutputLevel level,
String expectedOutput) throws Exception {
var baos = new ByteArrayOutputStream();
var handler = new ThrowingHandler();
HttpServer server;
if (level.equals(NONE)) {
server = HttpServer.create(LOOPBACK_ADDR, 10, "/", handler);
} else {
var filter = SimpleFileServer.createOutputFilter(baos, level);
server = HttpServer.create(LOOPBACK_ADDR, 10, "/", handler, filter);
}
server.start();
try (baos) {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
assertThrows(IOE, () -> client.send(request, HttpResponse.BodyHandlers.ofString()));
} finally {
server.stop(0);
baos.flush();
assertEquals(expectedOutput, baos.toString(UTF_8));
}
}
/**
* Confirms that the output filter prints the expected message if the request
* URI cannot be resolved. This only applies if the filter is used in
* combination with the SimpleFileServer file-handler, which sets the
* necessary request-path attribute.
*/
@Test
public void testCannotResolveRequestURI() throws Exception {
var baos = new ByteArrayOutputStream();
var handler = SimpleFileServer.createFileHandler(Path.of(".").toAbsolutePath());
var filter = SimpleFileServer.createOutputFilter(baos, VERBOSE);
var server = HttpServer.create(LOOPBACK_ADDR, 0, "/", handler, filter);
server.start();
try (baos) {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile\u0000.txt")).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(3, response.headers().map().size());
} finally {
server.stop(0);
baos.flush();
var filterOutput = baos.toString(UTF_8);
var pattern = Pattern.compile("""
127\\.0\\.0\\.1 - - \\[[\\s\\S]+] "GET /aFile%00\\.txt HTTP/1\\.1" 404 -
Resource requested: could not resolve request URI path
(>[\\s\\S]+:[\\s\\S]+)+
>
(<[\\s\\S]+:[\\s\\S]+)+
<
""".replaceAll("\n", System.lineSeparator()));
assertTrue(pattern.matcher(filterOutput).matches());
}
}
// --- infra ---
static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.buildUnchecked();
}
/**
* A handler that sets the request-path attribute and a custom header
* and sends a response.
*/
static class RequestPathHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
try (InputStream is = exchange.getRequestBody();
OutputStream os = exchange.getResponseBody()) {
is.readAllBytes();
exchange.setAttribute("request-path", "/foo/bar");
exchange.getResponseHeaders().put("Foo", List.of("bar", "bar"));
var resp = "hello world".getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, resp.length);
os.write(resp);
}
}
}
/**
* A handler that sets no request-path attribute and sends a response.
*/
static class NoRequestPathHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
try (InputStream is = exchange.getRequestBody();
OutputStream os = exchange.getResponseBody()) {
is.readAllBytes();
var resp = "hello world".getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, resp.length);
os.write(resp);
}
}
}
/**
* A handler that throws an IOException.
*/
static class ThrowingHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
try (exchange) {
throw new IOException("IOE ThrowingHandler");
}
}
}
}

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Tests for Request
* @run junit RequestTest
*/
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import com.sun.net.httpserver.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
public class RequestTest {
@Test
public void testAddToEmpty() {
var headers = new Headers();
Request request = new TestHttpExchange(headers);
request = request.with("Foo", List.of("Bar"));
assertEquals(1, request.getRequestHeaders().size());
assertEquals(List.of("Bar"), request.getRequestHeaders().get("Foo"));
assertReadOnly(request.getRequestHeaders());
}
@Test
public void testAddition() {
var headers = new Headers();
headers.add("Foo", "Bar");
Request request = new TestHttpExchange(headers);
request = request.with("X-Foo", List.of("Bar"));
assertEquals(2, request.getRequestHeaders().size());
assertEquals(List.of("Bar"), request.getRequestHeaders().get("Foo"));
assertEquals(List.of("Bar"), request.getRequestHeaders().get("X-Foo"));
assertReadOnly(request.getRequestHeaders());
}
@Test
public void testAddWithExisting() {
final String headerName = "Foo";
var headers = new Headers();
headers.add(headerName, "Bar");
Request request = new TestHttpExchange(headers);
request = request.with(headerName, List.of("blahblahblah"));
assertEquals(1, request.getRequestHeaders().size());
assertEquals(List.of("Bar"), request.getRequestHeaders().get(headerName));
assertReadOnly(request.getRequestHeaders());
}
@Test
public void testAddSeveral() {
var headers = new Headers();
headers.add("Foo", "Bar");
Request request = new TestHttpExchange(headers);
request = request.with("Larry", List.of("a"))
.with("Curly", List.of("b"))
.with("Moe", List.of("c"));
assertEquals(4, request.getRequestHeaders().size());
assertEquals("Bar", request.getRequestHeaders().getFirst("Foo"));
assertEquals("a", request.getRequestHeaders().getFirst("Larry"));
assertEquals("b", request.getRequestHeaders().getFirst("Curly"));
assertEquals("c", request.getRequestHeaders().getFirst("Moe" ));
assertReadOnly(request.getRequestHeaders());
}
static final Class<UnsupportedOperationException> UOP = UnsupportedOperationException.class;
static void assertReadOnly(Headers headers) {
assertUnsupportedOperation(headers);
assertUnmodifiableCollection(headers);
assertUnmodifiableList(headers);
}
static void assertUnsupportedOperation(Headers headers) {
assertThrows(UOP, () -> headers.add("a", "b"));
assertThrows(UOP, () -> headers.compute("c", (k, v) -> List.of("c")));
assertThrows(UOP, () -> headers.computeIfAbsent("d", k -> List.of("d")));
assertThrows(UOP, () -> headers.computeIfPresent("Foo", (k, v) -> null));
assertThrows(UOP, () -> headers.merge("e", List.of("e"), (k, v) -> List.of("e")));
assertThrows(UOP, () -> headers.put("f", List.of("f")));
assertThrows(UOP, () -> headers.putAll(Map.of()));
assertThrows(UOP, () -> headers.putIfAbsent("g", List.of("g")));
assertThrows(UOP, () -> headers.remove("h"));
assertThrows(UOP, () -> headers.replace("i", List.of("i")));
assertThrows(UOP, () -> headers.replace("j", List.of("j"), List.of("j")));
assertThrows(UOP, () -> headers.replaceAll((k, v) -> List.of("k")));
assertThrows(UOP, () -> headers.set("l", "m"));
assertThrows(UOP, () -> headers.clear());
}
static void assertUnmodifiableCollection(Headers headers) {
var entry = new AbstractMap.SimpleEntry<>("n", List.of("n"));
assertThrows(UOP, () -> headers.values().remove(List.of("Bar")));
assertThrows(UOP, () -> headers.values().removeAll(List.of("Bar")));
assertThrows(UOP, () -> headers.keySet().remove("Foo"));
assertThrows(UOP, () -> headers.keySet().removeAll(List.of("Foo")));
assertThrows(UOP, () -> headers.entrySet().remove(entry));
assertThrows(UOP, () -> headers.entrySet().removeAll(List.of(entry)));
}
static void assertUnmodifiableList(Headers headers) {
assertThrows(UOP, () -> headers.get("Foo").remove(0));
assertThrows(UOP, () -> headers.get("foo").remove(0));
assertThrows(UOP, () -> headers.values().stream().findFirst().orElseThrow().remove(0));
assertThrows(UOP, () -> headers.entrySet().stream().findFirst().orElseThrow().getValue().remove(0));
}
static class TestHttpExchange extends StubHttpExchange {
final Headers headers;
TestHttpExchange(Headers headers) {
this.headers = headers;
}
@Override
public Headers getRequestHeaders() {
return headers;
}
}
static class StubHttpExchange extends HttpExchange {
@Override public Headers getRequestHeaders() { return null; }
@Override public Headers getResponseHeaders() { return null; }
@Override public URI getRequestURI() { return null; }
@Override public String getRequestMethod() { return null; }
@Override public HttpContext getHttpContext() { return null; }
@Override public void close() { }
@Override public InputStream getRequestBody() { return null; }
@Override public OutputStream getResponseBody() { return null; }
@Override public void sendResponseHeaders(int rCode, long responseLength) { }
@Override public InetSocketAddress getRemoteAddress() { return null; }
@Override public int getResponseCode() { return 0; }
@Override public InetSocketAddress getLocalAddress() { return null; }
@Override public String getProtocol() { return null; }
@Override public Object getAttribute(String name) { return null; }
@Override public void setAttribute(String name, Object value) { }
@Override public void setStreams(InputStream i, OutputStream o) { }
@Override public HttpPrincipal getPrincipal() { return null; }
}
}

View file

@ -0,0 +1,210 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
import java.io.IOException;
import java.io.StringReader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import jdk.test.lib.net.URIBuilder;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import sun.net.www.MimeTable;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/*
* @test
* @summary Tests for MIME types in response headers
* @modules java.base/sun.net.www:+open
* @library /test/lib
* @build jdk.test.lib.net.URIBuilder
* @run junit/othervm ServerMimeTypesResolutionTest
*/
public class ServerMimeTypesResolutionTest {
static final Path CWD = Path.of(".").toAbsolutePath();
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final String FILE_NAME = "empty-file-of-type";
static final String UNKNOWN_FILE_EXTENSION = ".unknown-file-extension";
static final Properties SUPPORTED_MIME_TYPES = new Properties();
static final Set<String> UNSUPPORTED_FILE_EXTENSIONS = new HashSet<>();
static List<String> supportedFileExtensions;
static Path root;
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
@BeforeAll
public static void setup() throws Exception {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
getSupportedMimeTypes(SUPPORTED_MIME_TYPES);
supportedFileExtensions = getFileExtensions(SUPPORTED_MIME_TYPES);
root = createFileTreeFromMimeTypes(SUPPORTED_MIME_TYPES);
}
public static Properties getSupportedMimeTypes(Properties properties) throws IOException {
properties.load(MimeTable.class.getResourceAsStream("content-types.properties"));
return properties;
}
private static List<String> getFileExtensions(Properties input) {
return new ArrayList<>(getMimeTypesPerFileExtension(input).keySet());
}
private static Map<String,String> getMimeTypesPerFileExtension(Properties input) {
return input
.entrySet()
.stream()
.filter(entry -> ((String)entry.getValue()).contains("file_extensions"))
.flatMap(entry ->
Arrays.stream(
((String)deserialize((String) entry.getValue(), ";")
.get("file_extensions")).split(","))
.map(extension ->
Map.entry(extension, entry.getKey().toString())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
private static Path createFileTreeFromMimeTypes(Properties properties)
throws IOException {
final Path root = Files.createDirectory(CWD.resolve(ServerMimeTypesResolutionTest.class.getSimpleName()));
for (String extension : supportedFileExtensions) {
Files.createFile(root.resolve(toFileName(extension)));
}
Files.createFile(root.resolve(toFileName(UNKNOWN_FILE_EXTENSION)));
return root;
}
private static String toFileName(String extension) {
return "%s%s".formatted(FILE_NAME, extension);
}
protected static Properties deserialize(String serialized, String delimiter) {
try {
Properties properties = new Properties();
properties.load(
new StringReader(
Optional.ofNullable(delimiter)
.map(d -> serialized.replaceAll(delimiter, System.lineSeparator()))
.orElse(serialized)
)
);
return properties;
} catch (IOException exception) {
exception.printStackTrace();
throw new RuntimeException(("error while deserializing string %s " +
"to properties").formatted(serialized), exception);
}
}
@Test
public void testMimeTypeHeaders() throws Exception {
final var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, SimpleFileServer.OutputLevel.VERBOSE);
server.start();
try {
final var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
final Map<String, String> mimeTypesPerFileExtension = getMimeTypesPerFileExtension(SUPPORTED_MIME_TYPES);
for (String extension : supportedFileExtensions) {
final String expectedMimeType = mimeTypesPerFileExtension.get(extension);
execute(server, client, extension, expectedMimeType);
}
execute(server, client, UNKNOWN_FILE_EXTENSION,"application/octet-stream");
} finally {
server.stop(0);
}
}
private static void execute(HttpServer server,
HttpClient client,
String extension,
String expectedMimeType)
throws IOException, InterruptedException {
final var uri = uri(server, toFileName(extension));
final var request = HttpRequest.newBuilder(uri).build();
final var response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(expectedMimeType, response.headers().firstValue("content-type").get());
}
static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.buildUnchecked();
}
public static Object[][] commonExtensions() {
Set<String> extensions = Set.of(".aac", ".abw", ".arc", ".avi", ".azw", ".bin", ".bmp", ".bz",
".bz2", ".csh", ".css", ".csv", ".doc", ".docx",".eot", ".epub", ".gz", ".gif", ".htm", ".html", ".ico",
".ics", ".jar", ".jpeg", ".jpg", ".js", ".json", ".jsonld", ".mid", ".midi", ".mjs", ".mp3", ".cda",
".mp4", ".mpeg", ".mpkg", ".odp", ".ods", ".odt", ".oga", ".ogv", ".ogx", ".opus", ".otf", ".png",
".pdf", ".php", ".ppt", ".pptx", ".rar", ".rtf", ".sh", ".svg", ".swf", ".tar", ".tif", ".tiff", ".ts",
".ttf", ".txt", ".vsd", ".wav", ".weba", ".webm", ".webp", ".woff", ".woff2", ".xhtml", ".xls", ".xlsx",
".xml", ".xul", ".zip", ".3gp", ".3g2", ".7z");
return extensions.stream().map(e -> new Object[]{e}).toArray(Object[][]::new);
}
/**
* This is a one-off test to check which common file extensions are
* currently supported by the system-wide mime table returned by
* {@linkplain java.net.FileNameMap#getContentTypeFor(String) getContentTypeFor}.
*
* Source common mime types:
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
*/
// @Test(dataProvider = "commonExtensions")
public static void testCommonExtensions(String extension) {
var contains = supportedFileExtensions.contains(extension);
if (!contains) UNSUPPORTED_FILE_EXTENSIONS.add(extension);
assertTrue(contains, "expecting %s to be present".formatted(extension));
}
// @AfterTest
public static void printUnsupportedFileExtensions() {
System.out.println("Unsupported file extensions: " + UNSUPPORTED_FILE_EXTENSIONS.size());
UNSUPPORTED_FILE_EXTENSIONS.forEach(System.out::println);
}
}

View file

@ -0,0 +1,769 @@
/*
* Copyright (c) 2021, 2025, 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.
*/
/*
* @test
* @summary Tests for SimpleFileServer
* @library /test/lib
* @build jdk.test.lib.Platform jdk.test.lib.net.URIBuilder
* @run junit/othervm SimpleFileServerTest
*/
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;
import jdk.test.lib.Platform;
import jdk.test.lib.net.URIBuilder;
import jdk.test.lib.util.FileUtils;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardOpenOption.CREATE;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
public class SimpleFileServerTest {
static final Class<NullPointerException> NPE = NullPointerException.class;
static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
static final Class<UncheckedIOException> UIOE = UncheckedIOException.class;
static final Path CWD = Path.of(".").toAbsolutePath();
static final Path TEST_DIR = CWD.resolve("SimpleFileServerTest");
static final InetSocketAddress LOOPBACK_ADDR = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");
static final String EXPECTED_LAST_MODIFIED_OF_FAVICON = "Mon, 23 May 1995 11:11:11 GMT";
@BeforeAll
public static void setup() throws IOException {
if (ENABLE_LOGGING) {
ConsoleHandler ch = new ConsoleHandler();
LOGGER.setLevel(Level.ALL);
ch.setLevel(Level.ALL);
LOGGER.addHandler(ch);
}
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
Files.createDirectories(TEST_DIR);
}
@Test
public void testFileGET() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testFileGET"));
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
var lastModified = getLastModified(file);
var expectedLength = Long.toString(Files.size(file));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("some text", response.body());
assertEquals("text/plain", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
} finally {
server.stop(0);
}
}
@Test
public void testDirectoryGET() throws Exception {
var expectedBody = openHTML + """
<h1>Directory listing for &#x2F;</h1>
<ul>
<li><a href="aFile.txt">aFile.txt</a></li>
</ul>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testDirectoryGET"));
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
var lastModified = getLastModified(root);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("text/html; charset=UTF-8", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testFavIconGET() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testFavIconGET"));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
// expect built-in icon
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "favicon.ico")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("image/x-icon", response.headers().firstValue("content-type").get());
assertEquals(EXPECTED_LAST_MODIFIED_OF_FAVICON, response.headers().firstValue("last-modified").get());
// expect custom (and broken) icon
var file = Files.writeString(root.resolve("favicon.ico"), "broken icon", CREATE);
try {
var lastModified = getLastModified(file);
var expectedLength = Long.toString(Files.size(file));
response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("application/octet-stream", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
} finally {
Files.delete(file);
}
// expect built-in icon
response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("image/x-icon", response.headers().firstValue("content-type").get());
assertEquals(EXPECTED_LAST_MODIFIED_OF_FAVICON, response.headers().firstValue("last-modified").get());
} finally {
server.stop(0);
}
}
@Test
public void testFavIconHEAD() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testFavIconHEAD"));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "favicon.ico"))
.method("HEAD", BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("image/x-icon", response.headers().firstValue("content-type").get());
assertEquals(EXPECTED_LAST_MODIFIED_OF_FAVICON, response.headers().firstValue("last-modified").get());
assertEquals("", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testFileHEAD() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testFileHEAD"));
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
var lastModified = getLastModified(file);
var expectedLength = Long.toString(Files.size(file));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile.txt"))
.method("HEAD", BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("text/plain", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
assertEquals("", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testDirectoryHEAD() throws Exception {
var expectedLength = Integer.toString(
(openHTML + """
<h1>Directory listing for &#x2F;</h1>
<ul>
<li><a href="aFile.txt">aFile.txt</a></li>
</ul>
""" + closeHTML).getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testDirectoryHEAD"));
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
var lastModified = getLastModified(root);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, ""))
.method("HEAD", BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals("text/html; charset=UTF-8", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
assertEquals("", response.body());
} finally {
server.stop(0);
}
}
public static Object[][] indexFiles() {
var fileContent = openHTML + """
<h1>This is an index file</h1>
""" + closeHTML;
var dirListing = openHTML + """
<h1>Directory listing for &#x2F;</h1>
<ul>
</ul>
""" + closeHTML;
return new Object[][] {
{"1", "index.html", "text/html", "116", fileContent, true},
{"2", "index.htm", "text/html", "116", fileContent, true},
{"3", "index.txt", "text/html; charset=UTF-8", "134", dirListing, false}
};
}
@ParameterizedTest
@MethodSource("indexFiles")
public void testDirectoryWithIndexGET(String id,
String filename,
String contentType,
String contentLength,
String expectedBody,
boolean serveIndexFile) throws Exception {
var root = Files.createDirectories(TEST_DIR.resolve("testDirectoryWithIndexGET"+id));
var lastModified = getLastModified(root);
if (serveIndexFile) {
var file = Files.writeString(root.resolve(filename), expectedBody, CREATE);
lastModified = getLastModified(file);
}
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(contentType, response.headers().firstValue("content-type").get());
assertEquals(contentLength, response.headers().firstValue("content-length").get());
assertEquals(lastModified, response.headers().firstValue("last-modified").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
if (serveIndexFile) {
Files.delete(root.resolve(filename));
}
}
}
@Test
public void testNotReadableFileGET() throws Exception {
Assumptions.assumeFalse(Platform.isWindows(), "Not applicable on Windows");
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;aFile.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testNotReadableFileGET"));
var file = Files.writeString(root.resolve("aFile.txt"), "some text", CREATE);
file.toFile().setReadable(false, false);
assert !Files.isReadable(file);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
file.toFile().setReadable(true, false);
}
}
@Test
public void testNotReadableSegmentGET() throws Exception {
Assumptions.assumeFalse(Platform.isWindows(), "Not applicable on Windows");
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;dir&#x2F;aFile.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testNotReadableSegmentGET"));
var dir = Files.createDirectory(root.resolve("dir"));
var file = Files.writeString(dir.resolve("aFile.txt"), "some text", CREATE);
dir.toFile().setReadable(false, false);
assert !Files.isReadable(dir);
assert Files.isReadable(file);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "dir/aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
dir.toFile().setReadable(true, false);
}
}
@Test
public void testInvalidRequestURIGET() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;aFile?#.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testInvalidRequestURIGET"));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "aFile?#.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testNotFoundGET() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;doesNotExist.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testNotFoundGET"));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "doesNotExist.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testNotFoundHEAD() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;doesNotExist.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testNotFoundHEAD"));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "doesNotExist.txt"))
.method("HEAD", BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals("", response.body());
} finally {
server.stop(0);
}
}
@Test
public void testSymlinkGET() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;symlink</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testSymlinkGET"));
var symlink = root.resolve("symlink");
var target = Files.writeString(root.resolve("target.txt"), "some text", CREATE);
createSymLink(symlink, target);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "symlink")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testSymlinkSegmentGET() throws Exception {
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;symlink&#x2F;aFile.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var root = Files.createDirectory(TEST_DIR.resolve("testSymlinkSegmentGET"));
var symlink = root.resolve("symlink");
var target = Files.createDirectory(root.resolve("target"));
Files.writeString(target.resolve("aFile.txt"), "some text", CREATE);
createSymLink(symlink, target);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "symlink/aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
private static void createSymLink(Path symlink, Path target) {
try {
Files.createSymbolicLink(symlink, target);
} catch (UnsupportedOperationException uoe) {
Assumptions.abort("sym link creation not supported");
} catch (IOException ioe) {
Assumptions.abort("probably insufficient privileges to create sym links (Windows)");
}
}
@Test
public void testHiddenFileGET() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testHiddenFileGET"));
var file = createHiddenFile(root);
var fileName = file.getFileName().toString();
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;""" + fileName +
"""
</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, fileName)).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
@Test
public void testHiddenSegmentGET() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testHiddenSegmentGET"));
var file = createFileInHiddenDirectory(root);
var expectedBody = openHTML + """
<h1>File not found</h1>
<p>&#x2F;.hiddenDirectory&#x2F;aFile.txt</p>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, ".hiddenDirectory/aFile.txt")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
assertEquals(expectedBody, response.body());
} finally {
server.stop(0);
}
}
private static Path createHiddenFile(Path root) throws IOException {
Path file;
if (Platform.isWindows()) {
file = Files.createFile(root.resolve("aFile.txt"));
Files.setAttribute(file, "dos:hidden", true, LinkOption.NOFOLLOW_LINKS);
} else {
file = Files.writeString(root.resolve(".aFile.txt"), "some text", CREATE);
}
assertTrue(Files.isHidden(file));
return file;
}
private static Path createFileInHiddenDirectory(Path root) throws IOException {
Path dir;
Path file;
if (Platform.isWindows()) {
dir = Files.createDirectory(root.resolve("hiddenDirectory"));
Files.setAttribute(dir, "dos:hidden", true, LinkOption.NOFOLLOW_LINKS);
} else {
dir = Files.createDirectory(root.resolve(".hiddenDirectory"));
}
file = Files.writeString(dir.resolve("aFile.txt"), "some text", CREATE);
assertTrue(Files.isHidden(dir));
assertFalse(Files.isHidden(file));
return file;
}
@Test
public void testMovedPermanently() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testMovedPermanently"));
Files.createDirectory(root.resolve("aDirectory"));
var expectedBody = openHTML + """
<h1>Directory listing for &#x2F;aDirectory&#x2F;</h1>
<ul>
</ul>
""" + closeHTML;
var expectedLength = Integer.toString(expectedBody.getBytes(UTF_8).length);
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
{
var client = HttpClient.newBuilder().proxy(NO_PROXY)
.followRedirects(HttpClient.Redirect.NEVER).build();
var uri = uri(server, "aDirectory");
var request = HttpRequest.newBuilder(uri).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(301, response.statusCode());
assertEquals("0", response.headers().firstValue("content-length").get());
assertEquals("/aDirectory/", response.headers().firstValue("location").get());
// tests that query component is preserved during redirect
var uri2 = uri(server, "aDirectory", "query");
var req2 = HttpRequest.newBuilder(uri2).build();
var res2 = client.send(req2, BodyHandlers.ofString());
assertEquals(301, res2.statusCode());
assertEquals("0", res2.headers().firstValue("content-length").get());
assertEquals("/aDirectory/?query", res2.headers().firstValue("location").get());
}
{ // tests that redirect to returned relative URI works
var client = HttpClient.newBuilder().proxy(NO_PROXY)
.followRedirects(HttpClient.Redirect.ALWAYS).build();
var uri = uri(server, "aDirectory");
var request = HttpRequest.newBuilder(uri).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(200, response.statusCode());
assertEquals(expectedBody, response.body());
assertEquals("text/html; charset=UTF-8", response.headers().firstValue("content-type").get());
assertEquals(expectedLength, response.headers().firstValue("content-length").get());
}
} finally {
server.stop(0);
}
}
@Test
public void testNull() {
final var addr = InetSocketAddress.createUnresolved("foo", 8080);
final var path = Path.of("/tmp");
final var levl = OutputLevel.INFO;
assertThrows(NPE, () -> SimpleFileServer.createFileServer(null, null, null));
assertThrows(NPE, () -> SimpleFileServer.createFileServer(null, null, levl));
assertThrows(NPE, () -> SimpleFileServer.createFileServer(null, path, null));
assertThrows(NPE, () -> SimpleFileServer.createFileServer(null, path, levl));
assertThrows(NPE, () -> SimpleFileServer.createFileServer(addr, null, null));
assertThrows(NPE, () -> SimpleFileServer.createFileServer(addr, null, levl));
assertThrows(NPE, () -> SimpleFileServer.createFileServer(addr, path, null));
assertThrows(NPE, () -> SimpleFileServer.createFileHandler(null));
assertThrows(NPE, () -> SimpleFileServer.createOutputFilter(null, null));
assertThrows(NPE, () -> SimpleFileServer.createOutputFilter(null, levl));
assertThrows(NPE, () -> SimpleFileServer.createOutputFilter(System.out, null));
}
@Test
public void testInitialSlashContext() {
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, TEST_DIR, OutputLevel.INFO);
server.removeContext("/"); // throws if no context
server.stop(0);
}
@Test
public void testBound() {
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, TEST_DIR, OutputLevel.INFO);
var boundAddr = server.getAddress();
server.stop(0);
assertTrue(boundAddr.getAddress() != null);
assertTrue(boundAddr.getPort() > 0);
}
@Test
public void testIllegalPath() throws Exception {
var addr = LOOPBACK_ADDR;
{ // not a directory
Path p = Files.createFile(TEST_DIR.resolve("aFile"));
assert !Files.isDirectory(p);
var iae = assertThrows(IAE, () -> SimpleFileServer.createFileServer(addr, p, OutputLevel.INFO));
assertTrue(iae.getMessage().contains("not a directory"));
}
{ // does not exist
Path p = TEST_DIR.resolve("doesNotExist");
assert !Files.exists(p);
var iae = assertThrows(IAE, () -> SimpleFileServer.createFileServer(addr, p, OutputLevel.INFO));
assertTrue(iae.getMessage().contains("does not exist"));
}
}
@Test
public void testNonReadablePath() throws Exception {
Assumptions.assumeFalse(Platform.isWindows(), "Not applicable on Windows");
var addr = LOOPBACK_ADDR;
Path p = Files.createDirectory(TEST_DIR.resolve("aDir"));
p.toFile().setReadable(false, false);
assert !Files.isReadable(p);
try {
var iae = assertThrows(IAE, () -> SimpleFileServer.createFileServer(addr, p, OutputLevel.INFO));
assertTrue(iae.getMessage().contains("not readable"));
} finally {
p.toFile().setReadable(true, false);
}
}
@Test
public void testUncheckedIOException() {
var addr = InetSocketAddress.createUnresolved("foo", 8080);
assertThrows(UIOE, () -> SimpleFileServer.createFileServer(addr, TEST_DIR, OutputLevel.INFO));
assertThrows(UIOE, () -> SimpleFileServer.createFileServer(addr, TEST_DIR, OutputLevel.VERBOSE));
}
@Test
public void testXss() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testXss"));
var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "beginDelim%3C%3EEndDelim")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(404, response.statusCode());
assertTrue(response.body().contains("beginDelim%3C%3EEndDelim"));
assertTrue(response.body().contains("File not found"));
} finally {
server.stop(0);
}
}
@AfterAll
public static void teardown() throws IOException {
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
}
static final String openHTML = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
""";
static final String closeHTML = """
</body>
</html>
""";
static URI uri(HttpServer server, String path) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.buildUnchecked();
}
static URI uri(HttpServer server, String path, String query) {
return URIBuilder.newBuilder()
.host("localhost")
.port(server.getAddress().getPort())
.scheme("http")
.path("/" + path)
.query(query)
.buildUnchecked();
}
static String getLastModified(Path path) throws IOException {
return Files.getLastModifiedTime(path).toInstant().atZone(ZoneId.of("GMT"))
.format(DateTimeFormatter.RFC_1123_DATE_TIME);
}
}

Some files were not shown because too many files have changed in this diff Show more