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
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* 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 the jwebserver command-line tool
|
||||
* @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 JWEBSERVER = getJwebserver(JAVA_HOME);
|
||||
static final String LOCALE_OPT = "-J-Duser.language=en -J-Duser.country=US";
|
||||
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(JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, 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 getJwebserver(Path image) {
|
||||
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
|
||||
Path jwebserver = image.resolve("bin").resolve(isWindows ? "jwebserver.exe" : "jwebserver");
|
||||
if (Files.notExists(jwebserver))
|
||||
throw new RuntimeException(jwebserver + " not found");
|
||||
return jwebserver.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
static OutputAnalyzer simpleserver(String... args) throws Throwable {
|
||||
var pb = new ProcessBuilder(args)
|
||||
.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 jwebserver tool 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 = "-J-Duser.language=en -J-Duser.country=US";
|
||||
static final String JWEBSERVER = getJwebserver(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(JWEBSERVER, LOCALE_OPT)
|
||||
.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 getJwebserver(Path image) {
|
||||
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
|
||||
Path jwebserver = image.resolve("bin").resolve(isWindows ? "jwebserver.exe" : "jwebserver");
|
||||
if (Files.notExists(jwebserver))
|
||||
throw new RuntimeException(jwebserver + " not found");
|
||||
return jwebserver.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 the jwebserver command-line tool
|
||||
* @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 = "-J-Duser.language=en -J-Duser.country=US";
|
||||
static final String JWEBSERVER = getJwebserver(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(JWEBSERVER, LOCALE_OPT, "-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(JWEBSERVER, LOCALE_OPT, 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: jwebserver [-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
|
||||
JWEBSERVER, LOCALE_OPT, 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
|
||||
JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, "-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(JWEBSERVER, LOCALE_OPT, 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(JWEBSERVER, LOCALE_OPT, "-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(JWEBSERVER, LOCALE_OPT, "-p", "0", opt, CWD_STR, opt, CWD_STR)
|
||||
.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[][] 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(JWEBSERVER, LOCALE_OPT, "-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(JWEBSERVER, LOCALE_OPT, 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 getJwebserver(Path image) {
|
||||
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
|
||||
Path jwebserver = image.resolve("bin").resolve(isWindows ? "jwebserver.exe" : "jwebserver");
|
||||
if (Files.notExists(jwebserver))
|
||||
throw new RuntimeException(jwebserver + " not found");
|
||||
return jwebserver.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 = "jwebserver " + 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,100 @@
|
|||
/*
|
||||
* 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.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.test.lib.net.IPSupport;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jtreg.SkippedException;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8332020
|
||||
* @summary verifies that when jwebserver is launched with a IPv6 bind address
|
||||
* then the URL printed contains the correct host literal
|
||||
* @modules jdk.httpserver java.base/jdk.internal.util
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.net.IPSupport
|
||||
* @run driver IPv6BoundHost
|
||||
*/
|
||||
public class IPv6BoundHost {
|
||||
|
||||
private static final Path JDK_BIN_DIR = Path.of(System.getProperty("java.home")).resolve("bin");
|
||||
private static final Path JWEBSERVER_BINARY = OperatingSystem.isWindows()
|
||||
? JDK_BIN_DIR.resolve("jwebserver.exe") : JDK_BIN_DIR.resolve("jwebserver");
|
||||
private static final String LOCALE_OPT = "-J-Duser.language=en -J-Duser.country=US";
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
IPSupport.printPlatformSupport(System.err); // for debug purposes
|
||||
if (!IPSupport.hasIPv6()) {
|
||||
throw new SkippedException("Skipping test - IPv6 is not supported");
|
||||
}
|
||||
final String output = launchJwebserverAndExit(List.of("-b", "::1", "-p", "0"));
|
||||
if (output.contains("URL http://[::1]:")
|
||||
|| output.contains("URL http://[0:0:0:0:0:0:0:1]:")) {
|
||||
// found expected content
|
||||
System.out.println("found expected URL in jwebserver output");
|
||||
} else {
|
||||
throw new AssertionError("missing IPv6 address in jwebserver process output");
|
||||
}
|
||||
}
|
||||
|
||||
private static String launchJwebserverAndExit(final List<String> args) throws Exception {
|
||||
final Predicate<String> waitForLine = (s) -> s.startsWith("URL http://");
|
||||
final StringBuilder sb = new StringBuilder(); // stdout & stderr
|
||||
final List<String> cmd = new ArrayList<>();
|
||||
cmd.add(JWEBSERVER_BINARY.toString());
|
||||
cmd.add(LOCALE_OPT);
|
||||
cmd.addAll(args);
|
||||
// start the process and await the waitForLine before returning
|
||||
final Process p = ProcessTools.startProcess("8332020-test", new ProcessBuilder(cmd),
|
||||
line -> sb.append(line).append("\n"),
|
||||
waitForLine,
|
||||
30, // suitably high default timeout, not expected to timeout
|
||||
TimeUnit.SECONDS);
|
||||
System.out.println(sb.toString()); // print the process' stdout/stderr
|
||||
// the process has started and it is confirmed that the process output has the line
|
||||
// we were waiting for. now kill the process.
|
||||
p.destroy();
|
||||
final int exitCode = p.waitFor();
|
||||
if (!isNormalExitCode(exitCode)) {
|
||||
throw new AssertionError("jwebserver exited with unexpected exit code: " + exitCode);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean isNormalExitCode(final int exitCode) {
|
||||
final int SIGTERM = 15;
|
||||
if (OperatingSystem.isWindows()) {
|
||||
return exitCode == 1; // we expect exit code == 1 on Windows for Process.destroy()
|
||||
} else {
|
||||
// signal terminated exit code on Unix is 128 + signal value
|
||||
return exitCode == (128 + SIGTERM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8278398
|
||||
* @summary Tests the jwebserver's maximum request time
|
||||
* @modules jdk.httpserver
|
||||
* @library /test/lib
|
||||
* @run junit/othervm MaxRequestTimeTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
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.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import jdk.test.lib.Platform;
|
||||
import jdk.test.lib.net.SimpleSSLContext;
|
||||
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 static java.net.http.HttpClient.Builder.NO_PROXY;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* This test confirms that the jwebserver does not wait indefinitely for
|
||||
* a request to arrive.
|
||||
*
|
||||
* The jwebserver has a maximum request time of 5 seconds, which is set with the
|
||||
* "sun.net.httpserver.maxReqTime" system property. If this threshold is
|
||||
* reached, for example in the case of an HTTPS request where the server keeps
|
||||
* waiting for a plaintext request, the server closes the connection. Subsequent
|
||||
* requests are expected to be handled as normal.
|
||||
*
|
||||
* The test checks in the following order that:
|
||||
* 1. an HTTP request is handled successfully,
|
||||
* 2. an HTTPS request fails due to the server closing the connection
|
||||
* 3. another HTTP request is handled successfully.
|
||||
*/
|
||||
public class MaxRequestTimeTest {
|
||||
static final Path JAVA_HOME = Path.of(System.getProperty("java.home"));
|
||||
static final String LOCALE_OPT = "-J-Duser.language=en -J-Duser.country=US";
|
||||
static final String JWEBSERVER = getJwebserver(JAVA_HOME);
|
||||
static final Path CWD = Path.of(".").toAbsolutePath().normalize();
|
||||
static final Path TEST_DIR = CWD.resolve("MaxRequestTimeTest");
|
||||
static final String LOOPBACK_ADDR = InetAddress.getLoopbackAddress().getHostAddress();
|
||||
static final AtomicInteger PORT = new AtomicInteger();
|
||||
|
||||
private static final SSLContext sslContext = SimpleSSLContext.findSSLContext();
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() throws IOException {
|
||||
if (Files.exists(TEST_DIR)) {
|
||||
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
|
||||
}
|
||||
Files.createDirectories(TEST_DIR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxRequestTime() throws Throwable {
|
||||
final var sb = new StringBuffer(); // stdout & stderr
|
||||
final var p = startProcess("jwebserver", sb);
|
||||
try {
|
||||
sendHTTPSRequest(); // server expected to terminate connection
|
||||
sendHTTPRequest(); // server expected to respond successfully
|
||||
sendHTTPSRequest(); // server expected to terminate connection
|
||||
sendHTTPRequest(); // server expected to respond successfully
|
||||
} finally {
|
||||
p.destroy();
|
||||
int exitCode = p.waitFor();
|
||||
checkOutput(sb, exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
static String expectedBody = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Directory listing for /</h1>
|
||||
<ul>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
static void sendHTTPRequest() throws IOException, InterruptedException {
|
||||
out.println("\n--- sendHTTPRequest");
|
||||
var client = HttpClient.newBuilder()
|
||||
.proxy(NO_PROXY)
|
||||
.build();
|
||||
var request = HttpRequest.newBuilder(URI.create("http://localhost:" + PORT.get() + "/")).build();
|
||||
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
assertEquals(expectedBody, response.body());
|
||||
}
|
||||
|
||||
static void sendHTTPSRequest() throws IOException, InterruptedException {
|
||||
out.println("\n--- sendHTTPSRequest");
|
||||
var client = HttpClient.newBuilder()
|
||||
.sslContext(sslContext)
|
||||
.proxy(NO_PROXY)
|
||||
.build();
|
||||
var request = HttpRequest.newBuilder(URI.create("https://localhost:" + PORT.get() + "/")).build();
|
||||
try {
|
||||
client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
throw new RuntimeException("Expected SSLException not thrown");
|
||||
} catch (SSLException expected) { // server closes connection when max request time is reached
|
||||
expected.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void teardown() throws IOException {
|
||||
if (Files.exists(TEST_DIR)) {
|
||||
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
|
||||
}
|
||||
}
|
||||
|
||||
// --- infra ---
|
||||
|
||||
static String getJwebserver(Path image) {
|
||||
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
|
||||
Path jwebserver = image.resolve("bin").resolve(isWindows ? "jwebserver.exe" : "jwebserver");
|
||||
if (Files.notExists(jwebserver))
|
||||
throw new RuntimeException(jwebserver + " not found");
|
||||
return jwebserver.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
// The stdout/stderr output line to wait for when starting the jwebserver
|
||||
static final String REGULAR_STARTUP_LINE_STRING_1 = "URL http://";
|
||||
static final String REGULAR_STARTUP_LINE_STRING_2 = "Serving ";
|
||||
|
||||
static void parseAndSetPort(String line) {
|
||||
PORT.set(Integer.parseInt(line.split(" port ")[1]));
|
||||
}
|
||||
|
||||
static Process startProcess(String name, StringBuffer sb) throws Throwable {
|
||||
// starts the process, parses the port and awaits startup line before sending requests
|
||||
return ProcessTools.startProcess(name,
|
||||
new ProcessBuilder(JWEBSERVER, LOCALE_OPT, "-p", "0").directory(TEST_DIR.toFile()),
|
||||
line -> {
|
||||
if (line.startsWith(REGULAR_STARTUP_LINE_STRING_2)) { parseAndSetPort(line); }
|
||||
sb.append(line + "\n");
|
||||
},
|
||||
line -> line.startsWith(REGULAR_STARTUP_LINE_STRING_1),
|
||||
30, // suitably high default timeout, not expected to timeout
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void checkOutput(StringBuffer sb, int exitCode) {
|
||||
out.println("\n--- server output: \n" + sb);
|
||||
var outputAnalyser = new OutputAnalyzer(sb.toString(), "", exitCode);
|
||||
outputAnalyser.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 + " and subdirectories on " + LOOPBACK_ADDR + " port " + PORT)
|
||||
.shouldContain("URL http://" + LOOPBACK_ADDR);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue