8277459: Add jwebserver tool
Reviewed-by: michaelm, dfuchs, ihse
This commit is contained in:
parent
84aa0a191b
commit
f505396ccc
30
make/modules/jdk.httpserver/Launcher.gmk
Normal file
30
make/modules/jdk.httpserver/Launcher.gmk
Normal 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, \
|
||||
))
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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=\
|
||||
|
175
src/jdk.httpserver/share/man/jwebserver.1
Normal file
175
src/jdk.httpserver/share/man/jwebserver.1
Normal 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].
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -61,7 +61,8 @@ public class VersionCheck extends TestHelper {
|
||||
"jmc.ini",
|
||||
"jweblauncher",
|
||||
"jpackage",
|
||||
"ssvagent"
|
||||
"ssvagent",
|
||||
"jwebserver"
|
||||
};
|
||||
|
||||
// tools that do not accept -version
|
||||
|
Loading…
Reference in New Issue
Block a user