8277459: Add jwebserver tool

Reviewed-by: michaelm, dfuchs, ihse
This commit is contained in:
Julia Boes 2021-12-01 10:37:09 +00:00
parent 84aa0a191b
commit f505396ccc
17 changed files with 1028 additions and 70 deletions

View File

@ -0,0 +1,30 @@
#
# Copyright (c) 2021, 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. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# 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.
#
include LauncherCommon.gmk
$(eval $(call SetupBuildLauncher, jwebserver, \
MAIN_CLASS := sun.net.httpserver.simpleserver.JWebServer, \
))

View File

@ -104,12 +104,12 @@ import sun.net.httpserver.simpleserver.OutputFilter;
* server.start();
* }</pre>
*
* <h2>Main entry point</h2>
* <h2>jwebserver Tool</h2>
*
* <p>A <a id="server-impl">simple HTTP file server implementation</a> is
* provided via the
* <a href="{@docRoot}/jdk.httpserver/module-summary.html#entry-point">main entry point</a>
* of the {@code jdk.httpserver} module.
* <p>A simple HTTP file server implementation is provided via the
* {@code jwebserver} tool.
*
* @toolGuide jwebserver
*
* @since 18
*/

View File

@ -31,6 +31,24 @@
Any HTTP functionality not provided by this API can be implemented by application code
using the API.
<p>
* The main components are:
* <ul>
* <li>the {@link com.sun.net.httpserver.HttpExchange} class that describes a
* request and response pair,</li>
* <li>the {@link com.sun.net.httpserver.HttpHandler} interface to handle
* incoming requests, plus the {@link com.sun.net.httpserver.HttpHandlers} class
* that provides useful handler implementations,</li>
* <li>the {@link com.sun.net.httpserver.HttpContext} class that maps a URI path
* to a {@code HttpHandler},</li>
* <li>the {@link com.sun.net.httpserver.HttpServer} class to listen for
* connections and dispatch requests to handlers,</li>
* <li>the {@link com.sun.net.httpserver.Filter} class that allows pre- and post-
* processing of requests.</li></ul>
* <p>
* The {@link com.sun.net.httpserver.SimpleFileServer} class offers a simple
* HTTP-only file server (intended for testing, development and debugging purposes
* only). A default implementation is provided via the {@code jwebserver} tool.
<p>
Programmers must implement the {@link com.sun.net.httpserver.HttpHandler} interface. This interface
provides a callback which is invoked to handle incoming requests from clients.
A HTTP request and its response is known as an exchange. HTTP exchanges are
@ -120,13 +138,6 @@
}
});
</pre></blockquote>
<p>
The {@link com.sun.net.httpserver.SimpleFileServer} class offers a simple
HTTP file server (intended for testing, development and debugging purposes
only). A default implementation is provided via the
<a href="{@docRoot}/jdk.httpserver/module-summary.html#entry-point">main entry point</a>
of the {@code jdk.httpserver} module.
@since 1.6
*/
package com.sun.net.httpserver;

View File

@ -24,41 +24,21 @@
*/
/**
* Defines the JDK-specific HTTP server API.
* <p>
* A basic high-level API for building embedded servers. Both HTTP and
* HTTPS are supported.
* <p>
* The main components are:
* <ul>
* <li>the {@link com.sun.net.httpserver.HttpExchange} class that describes a
* request and response pair,</li>
* <li>the {@link com.sun.net.httpserver.HttpHandler} interface to handle
* incoming requests, plus the {@link com.sun.net.httpserver.HttpHandlers} class
* that provides useful handler implementations,</li>
* <li>the {@link com.sun.net.httpserver.HttpContext} class that maps a URI path
* to a {@code HttpHandler},</li>
* <li>the {@link com.sun.net.httpserver.HttpServer} class to listen for
* connections and dispatch requests to handlers,</li>
* <li>the {@link com.sun.net.httpserver.Filter} class that allows pre- and post-
* processing of requests.</li></ul>
* <p>
* The {@link com.sun.net.httpserver.SimpleFileServer} class offers a simple
* HTTP file server (intended for testing, development and debugging purposes
* only). A default implementation is provided via the <a id="entry-point"></a>
* main entry point of the {@code jdk.httpserver} module, which can be used on
* the command line as such:
* <pre>{@code
* Usage: java -m jdk.httpserver [-b bind address] [-p port] [-d directory]
* [-o none|info|verbose] [-h to show options]
* Options:
* -b, --bind-address - Address to bind to. Default: 127.0.0.1 or ::1 (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 - Print this help message.
* }</pre>
* Defines the JDK-specific HTTP server API, and provides the jwebserver tool
* for running a minimal HTTP server.
*
* <p>The {@link com.sun.net.httpserver} package defines a high-level API for
* building servers that support HTTP and HTTPS. The SimpleFileServer class
* implements a simple HTTP-only file server intended for testing, development
* and debugging purposes. A default implementation is provided via the
* {@code jwebserver} tool and the main entry point of the module, which can
* also be invoked with {@code java -m jdk.httpserver}.
*
* <p>The {@link com.sun.net.httpserver.spi} package specifies a Service Provider
* Interface (SPI) for locating HTTP server implementations based on the
* {@code com.sun.net.httpserver} API.
*
* @toolGuide jwebserver
*
* @uses com.sun.net.httpserver.spi.HttpServerProvider
*

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2021, 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.simpleserver;
import java.io.PrintWriter;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Programmatic entry point to start the jwebserver tool.
*
* <p><b> This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interface are subject to change or deletion
* without notice.</b>
*/
public class JWebServer {
/**
* This constructor should never be called.
*/
private JWebServer() { throw new AssertionError(); }
/**
* The main entry point.
*
* <p> The command line arguments are parsed and the server is started. If
* started successfully, the server will run on a new non-daemon thread,
* and this method will return. Otherwise, if the server is not started
* successfully, e.g. an error is encountered while parsing the arguments
* or an I/O error occurs, the server is not started and this method invokes
* System::exit with an appropriate exit code.
*
* @param args the command-line options
* @throws NullPointerException if {@code args} is {@code null}, or if there
* are any {@code null} values in the {@code args} array
*/
public static void main(String... args) {
int ec = SimpleFileServerImpl.start(new PrintWriter(System.out, true, UTF_8), "jwebserver", args);
if (ec != 0) {
System.exit(ec);
} // otherwise, the server has either been started successfully and
// runs in another non-daemon thread, or -h or -version have been
// passed and the main thread has exited normally.
}
}

View File

@ -27,7 +27,7 @@ import java.io.PrintWriter;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Programmatic entry point to start the simpleserver tool.
* Programmatic entry point to start "java -m jdk.httpserver".
*
* <p><b> This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
@ -56,10 +56,11 @@ public class Main {
* are any {@code null} values in the {@code args} array
*/
public static void main(String... args) {
int ec = SimpleFileServerImpl.start(new PrintWriter(System.out, true, UTF_8), args);
if (ec != 0)
int ec = SimpleFileServerImpl.start(new PrintWriter(System.out, true, UTF_8), "java", args);
if (ec != 0) {
System.exit(ec);
// otherwise the server has been started successfully and runs in
// another non-daemon thread.
} // otherwise, the server has either been started successfully and
// runs in another non-daemon thread, or -h or -version have been
// passed and the main thread has exited normally.
}
}

View File

@ -71,11 +71,12 @@ final class SimpleFileServerImpl {
*
* @param writer the writer to which output should be written
* @param args the command line options
* @param launcher the launcher the server is started from
* @throws NullPointerException if any of the arguments are {@code null},
* or if there are any {@code null} values in the {@code args} array
* @return startup status code
*/
static int start(PrintWriter writer, String[] args) {
static int start(PrintWriter writer, String launcher, String[] args) {
Objects.requireNonNull(args);
for (var arg : args) {
Objects.requireNonNull(arg);
@ -96,7 +97,11 @@ final class SimpleFileServerImpl {
option = options.next();
switch (option) {
case "-h", "-?", "--help" -> {
out.showHelp();
out.showHelp(launcher);
return Startup.OK.statusCode;
}
case "-version", "--version" -> {
out.showVersion(launcher);
return Startup.OK.statusCode;
}
case "-b", "--bind-address" -> {
@ -115,7 +120,7 @@ final class SimpleFileServerImpl {
}
} catch (AssertionError ae) {
out.reportError(ResourceBundleHelper.getMessage("err.unknown.option", option));
out.showUsage();
out.showUsage(launcher);
return Startup.CMDERR.statusCode;
} catch (NoSuchElementException nsee) {
out.reportError(ResourceBundleHelper.getMessage("err.missing.arg", option));
@ -169,12 +174,16 @@ final class SimpleFileServerImpl {
}
}
void showUsage() {
writer.println(ResourceBundleHelper.getMessage("usage"));
void showUsage(String launcher) {
writer.println(ResourceBundleHelper.getMessage("usage." + launcher));
}
void showHelp() {
writer.println(ResourceBundleHelper.getMessage("usage"));
void showVersion(String launcher) {
writer.println(ResourceBundleHelper.getMessage("version", launcher, System.getProperty("java.version")));
}
void showHelp(String launcher) {
writer.println(ResourceBundleHelper.getMessage("usage." + launcher));
writer.println(ResourceBundleHelper.getMessage("options", LOOPBACK_ADDR.getHostAddress()));
}

View File

@ -23,9 +23,18 @@
# questions.
#
usage=\
usage.java=\
Usage: java -m jdk.httpserver [-b bind address] [-p port] [-d directory]\n\
\ [-o none|info|verbose] [-h to show options]
\ [-o none|info|verbose] [-h to show options]\n\
\ [-version to show version information]
usage.jwebserver=\
Usage: jwebserver [-b bind address] [-p port] [-d directory]\n\
\ [-o none|info|verbose] [-h to show options]\n\
\ [-version to show version information]
version=\
{0} {1}
options=\
Options:\n\
@ -34,7 +43,8 @@ Options:\n\
-d, --directory - Directory to serve. Default: current directory.\n\
-o, --output - Output format. none|info|verbose. Default: info.\n\
-p, --port - Port to listen on. Default: 8000.\n\
-h, -?, --help - Print this help message.\n\
-h, -?, --help - Prints this help message and exits.\n\
-version, --version - Prints version information and exits.\n\
To stop the server, press Ctrl + C.
opt.bindaddress=\

View File

@ -0,0 +1,175 @@
.\" Automatically generated by Pandoc 2.3.1
.\"
.TH "JWEBSERVER" "1" "2021" "JDK 18\-internal" "JDK Commands"
.hy
.SH NAME
.PP
jwebserver \- launch the Java Simple Web Server
.SH SYNOPSIS
.PP
\f[CB]jwebserver\f[R] [\f[I]options\f[R]]
.TP
.B \f[I]options\f[R]
Command\-line options.
For a detailed description of the options, see \f[B]Options\f[R].
.RS
.RE
.SH DESCRIPTION
.PP
The \f[CB]jwebserver\f[R] tool provides a minimal HTTP server, designed to
be used for prototyping, testing, and debugging.
It serves a single directory hierarchy, and only serves static files.
Only HTTP/1.1 is supported; HTTP/2 and HTTPS are not supported.
.PP
Only idempotent HEAD and GET requests are served.
Any other requests receive a \f[CB]501\ \-\ Not\ Implemented\f[R] or a
\f[CB]405\ \-\ Not\ Allowed\f[R] response.
GET requests are mapped to the directory being served, as follows:
.IP \[bu] 2
If the requested resource is a file, its content is served.
.IP \[bu] 2
If the requested resource is a directory that contains an index file,
the content of the index file is served.
.IP \[bu] 2
Otherwise, the names of all files and subdirectories of the directory
are listed.
Symbolic links and hidden files are not listed or served.
.PP
MIME types are configured automatically, using the built\-in table.
For example, \f[CB]\&.html\f[R] files are served as \f[CB]text/html\f[R] and
\f[CB]\&.java\f[R] files are served as \f[CB]text/plain\f[R].
.PP
\f[CB]jwebserver\f[R] is located in the jdk.httpserver module, and can
alternatively be started with \f[CB]java\ \-m\ jdk.httpserver\f[R].
It is based on the web server implementation in the
\f[CB]com.sun.net.httpserver\f[R] package.
The \f[CB]com.sun.net.httpserver.SimpleFileServer\f[R] class provides a
programmatic way to retrieve the server and its components for reuse and
extension.
.SH USAGE
.IP
.nf
\f[CB]
jwebserver\ [\-b\ bind\ address]\ [\-p\ port]\ [\-d\ directory]
\ \ \ \ \ \ \ \ \ \ \ [\-o\ none|info|verbose]\ [\-h\ to\ show\ options]
\ \ \ \ \ \ \ \ \ \ \ [\-version\ to\ show\ version\ information]
\f[R]
.fi
.SH OPTIONS
.TP
.B \f[CB]\-h\f[R] or \f[CB]\-?\f[R] or \f[CB]\-\-help\f[R]
Prints the help message and exits.
.RS
.RE
.TP
.B \f[CB]\-b\f[R] \f[I]addr\f[R] or \f[CB]\-\-bind\-address\f[R] \f[I]addr\f[R]
Specifies the address to bind to.
Default: 127.0.0.1 or ::1 (loopback).
For all interfaces use \f[CB]\-b\ 0.0.0.0\f[R] or \f[CB]\-b\ ::\f[R].
.RS
.RE
.TP
.B \f[CB]\-d\f[R] \f[I]dir\f[R] or \f[CB]\-\-directory\f[R] \f[I]dir\f[R]
Specifies the directory to serve.
Default: current directory.
.RS
.RE
.TP
.B \f[CB]\-o\f[R] \f[I]level\f[R] or \f[CB]\-\-output\f[R] \f[I]level\f[R]
Specifies the output format.
\f[CB]none\f[R] | \f[CB]info\f[R] | \f[CB]verbose\f[R].
Default: \f[CB]info\f[R].
.RS
.RE
.TP
.B \f[CB]\-p\f[R] \f[I]port\f[R] or \f[CB]\-\-port\f[R] \f[I]port\f[R]
Specifies the port to listen on.
Default: 8000.
.RS
.RE
.TP
.B \f[CB]\-version\f[R] or \f[CB]\-\-version\f[R]
Prints the version information and exits.
.RS
.RE
.PP
To stop the server, press \f[CB]Ctrl\ +\ C\f[R].
.SH STARTING THE SERVER
.PP
The following command starts the Simple Web Server:
.IP
.nf
\f[CB]
$\ jwebserver
\f[R]
.fi
.PP
If startup is successful, the server prints a message to
\f[CB]System.out\f[R] listing the local address and the absolute path of
the directory being served.
For example:
.IP
.nf
\f[CB]
$\ jwebserver
Binding\ to\ loopback\ by\ default.\ For\ all\ interfaces\ use\ "\-b\ 0.0.0.0"\ or\ "\-b\ ::".
Serving\ /cwd\ and\ subdirectories\ on\ 127.0.0.1\ port\ 8000
URL\ http://127.0.0.1:8000/
\f[R]
.fi
.SH CONFIGURATION
.PP
By default, the server runs in the foreground and binds to the loopback
address and port 8000.
This can be changed with the \f[CB]\-b\f[R] and \f[CB]\-p\f[R] options.
.PD 0
.P
.PD
For example, to bind the Simple Web Server to all interfaces, use:
.IP
.nf
\f[CB]
$\ jwebserver\ \-b\ 0.0.0.0
Serving\ /cwd\ and\ subdirectories\ on\ 0.0.0.0\ (all\ interfaces)\ port\ 8000
URL\ http://123.456.7.891:8000/
\f[R]
.fi
.PP
Note that this makes the web server accessible to all hosts on the
network.
\f[I]Do not do this unless you are sure the server cannot leak any
sensitive information.\f[R]
.PP
As another example, use the following command to run on port 9000:
.IP
.nf
\f[CB]
$\ jwebserver\ \-p\ 9000
\f[R]
.fi
.PP
By default, the files of the current directory are served.
A different directory can be specified with the \f[CB]\-d\f[R] option.
.PP
By default, every request is logged on the console.
The output looks like this:
.IP
.nf
\f[CB]
127.0.0.1\ \-\ \-\ [10/Feb/2021:14:34:11\ +0000]\ "GET\ /some/subdirectory/\ HTTP/1.1"\ 200\ \-
\f[R]
.fi
.PP
Logging output can be changed with the \f[CB]\-o\f[R] option.
The default setting is \f[CB]info\f[R].
The \f[CB]verbose\f[R] setting additionally includes the request and
response headers as well as the absolute path of the requested resource.
.SH STOPPING THE SERVER
.PP
Once started successfully, the server runs until it is stopped.
On Unix platforms, the server can be stopped by sending it a
\f[CB]SIGINT\f[R] signal (\f[CB]Ctrl+C\f[R] in a terminal window).
.SH HELP OPTION
.PP
The \f[CB]\-h\f[R] option displays a help message describing the usage and
the options of the \f[CB]jwebserver\f[R].

View File

@ -23,7 +23,7 @@
/*
* @test
* @summary Negative tests for simpleserver command-line tool
* @summary Negative tests for java -m jdk.httpserver command
* @library /test/lib
* @modules jdk.httpserver
* @run testng/othervm CommandLineNegativeTest

View File

@ -24,7 +24,7 @@
/*
* @test
* @bug 8276848
* @summary Tests the command-line tool with port not specified
* @summary Tests the java -m jdk.httpserver command with port not specified
* @modules jdk.httpserver
* @library /test/lib
* @run testng/othervm/manual CommandLinePortNotSpecifiedTest
@ -118,7 +118,8 @@ public class CommandLinePortNotSpecifiedTest {
-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 - Print this help message.
-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

View File

@ -23,7 +23,7 @@
/*
* @test
* @summary Positive tests for simpleserver command-line tool
* @summary Positive tests for java -m jdk.httpserver command
* @library /test/lib
* @modules jdk.httpserver
* @run testng/othervm CommandLinePositiveTest
@ -46,6 +46,7 @@ import static java.lang.System.out;
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 JAVA = getJava(JAVA_HOME);
static final Path CWD = Path.of(".").toAbsolutePath().normalize();
@ -106,7 +107,8 @@ public class CommandLinePositiveTest {
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]""";
[-o none|info|verbose] [-h to show options]
[-version to show version information]""";
static final String OPTIONS_TEXT = """
Options:
@ -115,7 +117,8 @@ public class CommandLinePositiveTest {
-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 - Print this help message.
-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);
@Test(dataProvider = "helpOptions")
@ -129,6 +132,18 @@ public class CommandLinePositiveTest {
.shouldContain(OPTIONS_TEXT);
}
@DataProvider
public Object[][] versionOptions() { return new Object[][] {{"-version"}, {"--version"}}; }
@Test(dataProvider = "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, "-m", "jdk.httpserver", opt)
.shouldHaveExitValue(0);
}
@DataProvider
public Object[][] bindOptions() { return new Object[][] {{"-b"}, {"--bind-address"}}; }
@ -207,11 +222,13 @@ public class CommandLinePositiveTest {
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());
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; }

View File

@ -0,0 +1,245 @@
/*
* Copyright (c) 2021, 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 testng/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 org.testng.SkipException;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.lang.System.out;
import static org.testng.Assert.assertFalse;
public class CommandLineNegativeTest {
static final Path JAVA_HOME = Path.of(System.getProperty("java.home"));
static final String JWEBSERVER = getJwebserver(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();
@BeforeTest
public void setup() throws IOException {
if (Files.exists(TEST_DIR)) {
FileUtils.deleteFileTreeWithRetry(TEST_DIR);
}
Files.createDirectories(TEST_DIR);
Files.createFile(TEST_FILE);
}
@DataProvider
public Object[][] unknownOption() {
return new Object[][] {
{"--unknownOption"},
{"null"}
};
}
@Test(dataProvider = "unknownOption")
public void testBadOption(String opt) throws Throwable {
out.println("\n--- testUnknownOption, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, opt)
.shouldNotHaveExitValue(0)
.shouldContain("Error: unknown option: " + opt);
}
@DataProvider
public 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
};
}
@Test(dataProvider = "tooManyOptionArgs")
public void testTooManyOptionArgs(String opt, String arg) throws Throwable {
out.println("\n--- testTooManyOptionArgs, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, opt, arg, arg)
.shouldNotHaveExitValue(0)
.shouldContain("Error: unknown option: " + arg);
}
@DataProvider
public 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
};
}
@Test(dataProvider = "noArg")
public void testNoArg(String opt, String msg) throws Throwable {
out.println("\n--- testNoArg, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, opt)
.shouldNotHaveExitValue(0)
.shouldContain("Error: no value given for " + opt)
.shouldContain(msg);
}
@DataProvider
public 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", "+-"}
};
}
@Test(dataProvider = "invalidValue")
public void testInvalidValue(String opt, String val) throws Throwable {
out.println("\n--- testInvalidValue, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, opt, val)
.shouldNotHaveExitValue(0)
.shouldContain("Error: invalid value given for " + opt + ": " + val);
}
@DataProvider
public Object[][] portOptions() { return new Object[][] {{"-p"}, {"--port"}}; }
@Test(dataProvider = "portOptions")
public void testPortOutOfRange(String opt) throws Throwable {
out.println("\n--- testPortOutOfRange, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, opt, "65536") // range 0 to 65535
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "port out of range:65536");
}
@DataProvider
public Object[][] directoryOptions() { return new Object[][] {{"-d"}, {"--directory"}}; }
@Test(dataProvider = "directoryOptions")
public void testRootNotAbsolute(String opt) throws Throwable {
out.println("\n--- testRootNotAbsolute, opt=\"%s\" ".formatted(opt));
var root = Path.of(".");
assertFalse(root.isAbsolute());
simpleserver(JWEBSERVER, opt, root.toString())
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "Path is not absolute:");
}
@Test(dataProvider = "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, opt, file)
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "Path is not a directory: " + file);
}
@Test(dataProvider = "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, opt, root.toString())
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "Path does not exist: " + root.toString());
}
@Test(dataProvider = "directoryOptions")
public void testRootNotReadable(String opt) throws Throwable {
out.println("\n--- testRootNotReadable, opt=\"%s\" ".formatted(opt));
if (Platform.isWindows()) {
// Not applicable to Windows. Reason: cannot revoke an owner's read
// access to a directory that was created by that owner
throw new SkipException("cannot run on Windows");
}
Path root = Files.createDirectories(TEST_DIR.resolve("not/readable/dir"));
try {
root.toFile().setReadable(false, false);
assertFalse(Files.isReadable(root));
simpleserver(JWEBSERVER, opt, root.toString())
.shouldNotHaveExitValue(0)
.shouldContain("Error: server config failed: " + "Path is not readable: " + root.toString());
} finally {
root.toFile().setReadable(true, false);
}
}
@AfterTest
public 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;
}
}

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2021, 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 testng/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 org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static java.lang.System.out;
public class CommandLinePortNotSpecifiedTest {
static final Path JAVA_HOME = Path.of(System.getProperty("java.home"));
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();
@BeforeTest
public 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)
.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);
}
@AfterTest
public 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;
}
}

View File

@ -0,0 +1,257 @@
/*
* Copyright (c) 2021, 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
* @modules jdk.httpserver
* @run testng/othervm CommandLinePositiveTest
*/
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 org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static java.lang.System.out;
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 JWEBSERVER = getJwebserver(JAVA_HOME);
static final Path CWD = Path.of(".").toAbsolutePath().normalize();
static final Path TEST_DIR = CWD.resolve("CommandLinePositiveTest");
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();
@BeforeTest
public 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;
}
}
@DataProvider
public Object[][] directoryOptions() { return new Object[][] {{"-d"}, {"--directory"}}; }
@Test(dataProvider = "directoryOptions")
public void testDirectory(String opt) throws Throwable {
out.println("\n--- testDirectory, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, "-p", "0", opt, TEST_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 " + TEST_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@DataProvider
public Object[][] portOptions() { return new Object[][] {{"-p"}, {"--port"}}; }
@Test(dataProvider = "portOptions")
public void testPort(String opt) throws Throwable {
out.println("\n--- testPort, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, 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 " + TEST_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@DataProvider
public 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);
@Test(dataProvider = "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, opt)
.shouldHaveExitValue(0)
.shouldContain(USAGE_TEXT)
.shouldContain(OPTIONS_TEXT);
}
@DataProvider
public Object[][] versionOptions() { return new Object[][] {{"-version"}, {"--version"}}; }
@Test(dataProvider = "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, opt)
.shouldHaveExitValue(0);
}
@DataProvider
public Object[][] bindOptions() { return new Object[][] {{"-b"}, {"--bind-address"}}; }
@Test(dataProvider = "bindOptions")
public void testBindAllInterfaces(String opt) throws Throwable {
out.println("\n--- testBindAllInterfaces, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, "-p", "0", opt, "0.0.0.0")
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Serving " + TEST_DIR_STR + " and subdirectories on 0.0.0.0 (all interfaces) port")
.shouldContain("URL http://" + InetAddress.getLocalHost().getHostAddress());
simpleserver(JWEBSERVER, opt, "::0")
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Serving " + TEST_DIR_STR + " and subdirectories on 0.0.0.0 (all interfaces) port")
.shouldContain("URL http://" + InetAddress.getLocalHost().getHostAddress());
}
@Test(dataProvider = "bindOptions")
public void testLastOneWinsBindAddress(String opt) throws Throwable {
out.println("\n--- testLastOneWinsBindAddress, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, "-p", "0", opt, "123.4.5.6", opt, LOOPBACK_ADDR)
.shouldHaveExitValue(NORMAL_EXIT_CODE)
.shouldContain("Serving " + TEST_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@Test(dataProvider = "directoryOptions")
public void testLastOneWinsDirectory(String opt) throws Throwable {
out.println("\n--- testLastOneWinsDirectory, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, "-p", "0", opt, TEST_DIR_STR, opt, TEST_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 " + TEST_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@DataProvider
public Object[][] outputOptions() { return new Object[][] {{"-o"}, {"--output"}}; }
@Test(dataProvider = "outputOptions")
public void testLastOneWinsOutput(String opt) throws Throwable {
out.println("\n--- testLastOneWinsOutput, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, "-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 " + TEST_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@Test(dataProvider = "portOptions")
public void testLastOneWinsPort(String opt) throws Throwable {
out.println("\n--- testLastOneWinsPort, opt=\"%s\" ".formatted(opt));
simpleserver(JWEBSERVER, 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 " + TEST_DIR_STR + " and subdirectories on " + LOOPBACK_ADDR + " port")
.shouldContain("URL http://" + LOOPBACK_ADDR);
}
@AfterTest
public 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 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).directory(TEST_DIR.toFile()),
line -> sb.append(line + "\n"),
line -> line.startsWith(waitForLine.value),
30, // suitably high default timeout, not expected to timeout
TimeUnit.SECONDS);
if (destroy) {
p.destroy(); // SIGTERM on Unix
}
int ec = p.waitFor();
var outputAnalyser = new OutputAnalyzer(sb.toString(), "", ec);
return outputAnalyser;
}
}

View File

@ -151,6 +151,7 @@ public class HelpFlagsTest extends TestHelper {
new ToolHelpSpec("rmiregistry", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways.
new ToolHelpSpec("serialver", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways.
new ToolHelpSpec("jpackage", 0, 1, 1, 0, 0, 1, 1), // -h, --help,
new ToolHelpSpec("jwebserver", 1, 1, 1, 0, 0, 1, 1), // -?, -h, --help
};
// Returns corresponding object from jdkTools array.

View File

@ -61,7 +61,8 @@ public class VersionCheck extends TestHelper {
"jmc.ini",
"jweblauncher",
"jpackage",
"ssvagent"
"ssvagent",
"jwebserver"
};
// tools that do not accept -version