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:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
60
test/jdk/com/sun/net/httpserver/AuthenticatorTest.java
Normal file
60
test/jdk/com/sun/net/httpserver/AuthenticatorTest.java
Normal 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());
|
||||
}
|
||||
}
|
||||
119
test/jdk/com/sun/net/httpserver/BasicAuthToken.java
Normal file
119
test/jdk/com/sun/net/httpserver/BasicAuthToken.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
193
test/jdk/com/sun/net/httpserver/BasicAuthenticatorRealm.java
Normal file
193
test/jdk/com/sun/net/httpserver/BasicAuthenticatorRealm.java
Normal 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();
|
||||
}
|
||||
}
|
||||
159
test/jdk/com/sun/net/httpserver/ClearTextServerSSL.java
Normal file
159
test/jdk/com/sun/net/httpserver/ClearTextServerSSL.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
52
test/jdk/com/sun/net/httpserver/CreateHttpServerTest.java
Normal file
52
test/jdk/com/sun/net/httpserver/CreateHttpServerTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
123
test/jdk/com/sun/net/httpserver/DateFormatterTest.java
Normal file
123
test/jdk/com/sun/net/httpserver/DateFormatterTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
test/jdk/com/sun/net/httpserver/DummyVerifier.java
Normal file
32
test/jdk/com/sun/net/httpserver/DummyVerifier.java
Normal 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;
|
||||
}
|
||||
}
|
||||
76
test/jdk/com/sun/net/httpserver/EchoHandler.java
Normal file
76
test/jdk/com/sun/net/httpserver/EchoHandler.java
Normal 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);
|
||||
}
|
||||
}
|
||||
128
test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java
Normal file
128
test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
306
test/jdk/com/sun/net/httpserver/FailAndStopTest.java
Normal file
306
test/jdk/com/sun/net/httpserver/FailAndStopTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
146
test/jdk/com/sun/net/httpserver/FileServerHandler.java
Normal file
146
test/jdk/com/sun/net/httpserver/FileServerHandler.java
Normal 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();
|
||||
}
|
||||
}
|
||||
439
test/jdk/com/sun/net/httpserver/FilterTest.java
Normal file
439
test/jdk/com/sun/net/httpserver/FilterTest.java
Normal 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 { }
|
||||
}
|
||||
}
|
||||
536
test/jdk/com/sun/net/httpserver/HeadersTest.java
Normal file
536
test/jdk/com/sun/net/httpserver/HeadersTest.java
Normal 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
|
||||
}
|
||||
105
test/jdk/com/sun/net/httpserver/Http10KeepAliveMaxParamTest.java
Normal file
105
test/jdk/com/sun/net/httpserver/Http10KeepAliveMaxParamTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
92
test/jdk/com/sun/net/httpserver/HttpContextTest.java
Normal file
92
test/jdk/com/sun/net/httpserver/HttpContextTest.java
Normal 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 { }
|
||||
}
|
||||
}
|
||||
48
test/jdk/com/sun/net/httpserver/HttpPrincipalTest.java
Normal file
48
test/jdk/com/sun/net/httpserver/HttpPrincipalTest.java
Normal 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());
|
||||
}
|
||||
}
|
||||
161
test/jdk/com/sun/net/httpserver/HttpServerProviderTest.java
Normal file
161
test/jdk/com/sun/net/httpserver/HttpServerProviderTest.java
Normal 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; }
|
||||
}
|
||||
}
|
||||
102
test/jdk/com/sun/net/httpserver/HttpServerTest.java
Normal file
102
test/jdk/com/sun/net/httpserver/HttpServerTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
234
test/jdk/com/sun/net/httpserver/InputNotRead.java
Normal file
234
test/jdk/com/sun/net/httpserver/InputNotRead.java
Normal 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." );
|
||||
}
|
||||
|
||||
}
|
||||
61
test/jdk/com/sun/net/httpserver/LogFilter.java
Normal file
61
test/jdk/com/sun/net/httpserver/LogFilter.java
Normal 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){}
|
||||
}
|
||||
147
test/jdk/com/sun/net/httpserver/MissingTrailingSpace.java
Normal file
147
test/jdk/com/sun/net/httpserver/MissingTrailingSpace.java
Normal 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." );
|
||||
}
|
||||
}
|
||||
215
test/jdk/com/sun/net/httpserver/SANTest.java
Normal file
215
test/jdk/com/sun/net/httpserver/SANTest.java
Normal 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();
|
||||
}
|
||||
}
|
||||
152
test/jdk/com/sun/net/httpserver/SelCacheTest.java
Normal file
152
test/jdk/com/sun/net/httpserver/SelCacheTest.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
347
test/jdk/com/sun/net/httpserver/ServerStopTerminationTest.java
Normal file
347
test/jdk/com/sun/net/httpserver/ServerStopTerminationTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
test/jdk/com/sun/net/httpserver/SimpleHttpServerTest.java
Normal file
84
test/jdk/com/sun/net/httpserver/SimpleHttpServerTest.java
Normal 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
42
test/jdk/com/sun/net/httpserver/StopNoStartTest.java
Normal file
42
test/jdk/com/sun/net/httpserver/StopNoStartTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
1
test/jdk/com/sun/net/httpserver/TEST.properties
Normal file
1
test/jdk/com/sun/net/httpserver/TEST.properties
Normal file
|
|
@ -0,0 +1 @@
|
|||
modules = jdk.httpserver java.logging
|
||||
113
test/jdk/com/sun/net/httpserver/TaskRejectedTest.java
Normal file
113
test/jdk/com/sun/net/httpserver/TaskRejectedTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
test/jdk/com/sun/net/httpserver/Test.java
Normal file
38
test/jdk/com/sun/net/httpserver/Test.java
Normal 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);
|
||||
}
|
||||
}
|
||||
163
test/jdk/com/sun/net/httpserver/Test1.java
Normal file
163
test/jdk/com/sun/net/httpserver/Test1.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
95
test/jdk/com/sun/net/httpserver/Test10.java
Normal file
95
test/jdk/com/sun/net/httpserver/Test10.java
Normal 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();
|
||||
}
|
||||
}
|
||||
84
test/jdk/com/sun/net/httpserver/Test11.java
Normal file
84
test/jdk/com/sun/net/httpserver/Test11.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
174
test/jdk/com/sun/net/httpserver/Test12.java
Normal file
174
test/jdk/com/sun/net/httpserver/Test12.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
199
test/jdk/com/sun/net/httpserver/Test13.java
Normal file
199
test/jdk/com/sun/net/httpserver/Test13.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
142
test/jdk/com/sun/net/httpserver/Test14.java
Normal file
142
test/jdk/com/sun/net/httpserver/Test14.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
135
test/jdk/com/sun/net/httpserver/Test2.java
Normal file
135
test/jdk/com/sun/net/httpserver/Test2.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
161
test/jdk/com/sun/net/httpserver/Test3.java
Normal file
161
test/jdk/com/sun/net/httpserver/Test3.java
Normal 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();
|
||||
}
|
||||
}
|
||||
161
test/jdk/com/sun/net/httpserver/Test4.java
Normal file
161
test/jdk/com/sun/net/httpserver/Test4.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
160
test/jdk/com/sun/net/httpserver/Test5.java
Normal file
160
test/jdk/com/sun/net/httpserver/Test5.java
Normal 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();
|
||||
}
|
||||
}
|
||||
115
test/jdk/com/sun/net/httpserver/Test6.java
Normal file
115
test/jdk/com/sun/net/httpserver/Test6.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
123
test/jdk/com/sun/net/httpserver/Test6a.java
Normal file
123
test/jdk/com/sun/net/httpserver/Test6a.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
114
test/jdk/com/sun/net/httpserver/Test7.java
Normal file
114
test/jdk/com/sun/net/httpserver/Test7.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
119
test/jdk/com/sun/net/httpserver/Test7a.java
Normal file
119
test/jdk/com/sun/net/httpserver/Test7a.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
117
test/jdk/com/sun/net/httpserver/Test8.java
Normal file
117
test/jdk/com/sun/net/httpserver/Test8.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
143
test/jdk/com/sun/net/httpserver/Test8a.java
Normal file
143
test/jdk/com/sun/net/httpserver/Test8a.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
210
test/jdk/com/sun/net/httpserver/Test9.java
Normal file
210
test/jdk/com/sun/net/httpserver/Test9.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
204
test/jdk/com/sun/net/httpserver/Test9a.java
Normal file
204
test/jdk/com/sun/net/httpserver/Test9a.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
128
test/jdk/com/sun/net/httpserver/TestLogging.java
Normal file
128
test/jdk/com/sun/net/httpserver/TestLogging.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
197
test/jdk/com/sun/net/httpserver/UnmodifiableHeadersTest.java
Normal file
197
test/jdk/com/sun/net/httpserver/UnmodifiableHeadersTest.java
Normal 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; }
|
||||
}
|
||||
}
|
||||
277
test/jdk/com/sun/net/httpserver/bugs/6725892/Test.java
Normal file
277
test/jdk/com/sun/net/httpserver/bugs/6725892/Test.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
230
test/jdk/com/sun/net/httpserver/bugs/8199849/ParamTest.java
Normal file
230
test/jdk/com/sun/net/httpserver/bugs/8199849/ParamTest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
71
test/jdk/com/sun/net/httpserver/bugs/B6339483.java
Normal file
71
test/jdk/com/sun/net/httpserver/bugs/B6339483.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
109
test/jdk/com/sun/net/httpserver/bugs/B6341616.java
Normal file
109
test/jdk/com/sun/net/httpserver/bugs/B6341616.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
157
test/jdk/com/sun/net/httpserver/bugs/B6361557.java
Normal file
157
test/jdk/com/sun/net/httpserver/bugs/B6361557.java
Normal 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;
|
||||
}
|
||||
}
|
||||
193
test/jdk/com/sun/net/httpserver/bugs/B6373555.java
Normal file
193
test/jdk/com/sun/net/httpserver/bugs/B6373555.java
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
159
test/jdk/com/sun/net/httpserver/bugs/B6393710.java
Normal file
159
test/jdk/com/sun/net/httpserver/bugs/B6393710.java
Normal 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 ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
158
test/jdk/com/sun/net/httpserver/bugs/B6401598.java
Normal file
158
test/jdk/com/sun/net/httpserver/bugs/B6401598.java
Normal 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;
|
||||
}
|
||||
}
|
||||
118
test/jdk/com/sun/net/httpserver/bugs/B6421581.java
Normal file
118
test/jdk/com/sun/net/httpserver/bugs/B6421581.java
Normal 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();
|
||||
}
|
||||
}
|
||||
39
test/jdk/com/sun/net/httpserver/bugs/B6424196.java
Normal file
39
test/jdk/com/sun/net/httpserver/bugs/B6424196.java
Normal 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();
|
||||
}
|
||||
}
|
||||
87
test/jdk/com/sun/net/httpserver/bugs/B6431193.java
Normal file
87
test/jdk/com/sun/net/httpserver/bugs/B6431193.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
109
test/jdk/com/sun/net/httpserver/bugs/B6433018.java
Normal file
109
test/jdk/com/sun/net/httpserver/bugs/B6433018.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
105
test/jdk/com/sun/net/httpserver/bugs/B6526158.java
Normal file
105
test/jdk/com/sun/net/httpserver/bugs/B6526158.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
114
test/jdk/com/sun/net/httpserver/bugs/B6526913.java
Normal file
114
test/jdk/com/sun/net/httpserver/bugs/B6526913.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
139
test/jdk/com/sun/net/httpserver/bugs/B6529200.java
Normal file
139
test/jdk/com/sun/net/httpserver/bugs/B6529200.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
test/jdk/com/sun/net/httpserver/bugs/B6744329.java
Normal file
115
test/jdk/com/sun/net/httpserver/bugs/B6744329.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
110
test/jdk/com/sun/net/httpserver/bugs/B6886436.java
Normal file
110
test/jdk/com/sun/net/httpserver/bugs/B6886436.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
125
test/jdk/com/sun/net/httpserver/bugs/B8211420.java
Normal file
125
test/jdk/com/sun/net/httpserver/bugs/B8211420.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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\\\"");
|
||||
}
|
||||
}
|
||||
134
test/jdk/com/sun/net/httpserver/bugs/ExceptionKeepAlive.java
Normal file
134
test/jdk/com/sun/net/httpserver/bugs/ExceptionKeepAlive.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
162
test/jdk/com/sun/net/httpserver/bugs/FixedLengthInputStream.java
Normal file
162
test/jdk/com/sun/net/httpserver/bugs/FixedLengthInputStream.java
Normal 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");}
|
||||
|
||||
}
|
||||
438
test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java
Normal file
438
test/jdk/com/sun/net/httpserver/bugs/HandlerConnectionClose.java
Normal 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");}
|
||||
|
||||
}
|
||||
131
test/jdk/com/sun/net/httpserver/bugs/HeadKeepAlive.java
Normal file
131
test/jdk/com/sun/net/httpserver/bugs/HeadKeepAlive.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
131
test/jdk/com/sun/net/httpserver/bugs/HeadTest.java
Normal file
131
test/jdk/com/sun/net/httpserver/bugs/HeadTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
139
test/jdk/com/sun/net/httpserver/bugs/TruncatedRequestBody.java
Normal file
139
test/jdk/com/sun/net/httpserver/bugs/TruncatedRequestBody.java
Normal 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");
|
||||
}
|
||||
}
|
||||
118
test/jdk/com/sun/net/httpserver/bugs/ZeroLengthOutputStream.java
Normal file
118
test/jdk/com/sun/net/httpserver/bugs/ZeroLengthOutputStream.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 /</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 /</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 /</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>/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>/dir/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>/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>/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>/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>/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>/symlink/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>/""" + 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>/.hiddenDirectory/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 /aDirectory/</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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 /</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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
391
test/jdk/com/sun/net/httpserver/simpleserver/MapToPathTest.java
Normal file
391
test/jdk/com/sun/net/httpserver/simpleserver/MapToPathTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
168
test/jdk/com/sun/net/httpserver/simpleserver/RequestTest.java
Normal file
168
test/jdk/com/sun/net/httpserver/simpleserver/RequestTest.java
Normal 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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 /</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 /</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 /</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>/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>/dir/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>/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>/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>/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>/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>/symlink/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>/""" + 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>/.hiddenDirectory/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 /aDirectory/</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
Loading…
Add table
Add a link
Reference in a new issue