mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-19 23:05:28 +00:00
8272758: Improve HttpServer to avoid partial file name matches while mapping request path to context path
Reviewed-by: dfuchs
This commit is contained in:
parent
759fe58877
commit
bea48b54e2
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -75,7 +75,7 @@ import java.util.concurrent.Executor;
|
||||
*
|
||||
* <p>The following table shows some request URIs and which, if any context they would
|
||||
* match with:
|
||||
* <table class="striped"><caption style="display:none">description</caption>
|
||||
* <table class="striped" style="text-align:left"><caption style="display:none">description</caption>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th scope="col"><i>Request URI</i></th>
|
||||
@ -278,10 +278,20 @@ public abstract class HttpServer {
|
||||
* <p>The class overview describes how incoming request URIs are
|
||||
* <a href="#mapping_description">mapped</a> to HttpContext instances.
|
||||
*
|
||||
* @apiNote The path should generally, but is not required to, end with '/'.
|
||||
* If the path does not end with '/', eg such as with {@code "/foo"} then
|
||||
* this would match requests with a path of {@code "/foobar"} or
|
||||
* {@code "/foo/bar"}.
|
||||
* @apiNote
|
||||
* The path should generally, but is not required to, end with {@code /}.
|
||||
* If the path does not end with {@code /}, e.g., such as with {@code /foo},
|
||||
* then some implementations may use <em>string prefix matching</em> where
|
||||
* this context path matches request paths {@code /foo},
|
||||
* {@code /foo/bar}, or {@code /foobar}. Others may use <em>path prefix
|
||||
* matching</em> where {@code /foo} matches request paths {@code /foo} and
|
||||
* {@code /foo/bar}, but not {@code /foobar}.
|
||||
*
|
||||
* @implNote
|
||||
* By default, the JDK built-in implementation uses path prefix matching.
|
||||
* String prefix matching can be enabled using the
|
||||
* {@link jdk.httpserver/##sun.net.httpserver.pathMatcher sun.net.httpserver.pathMatcher}
|
||||
* system property.
|
||||
*
|
||||
* @param path the root URI path to associate the context with
|
||||
* @param handler the handler to invoke for incoming requests
|
||||
@ -289,6 +299,8 @@ public abstract class HttpServer {
|
||||
* already exists for this path
|
||||
* @throws NullPointerException if either path, or handler are {@code null}
|
||||
* @return an instance of {@code HttpContext}
|
||||
*
|
||||
* @see jdk.httpserver/##sun.net.httpserver.pathMatcher sun.net.httpserver.pathMatcher
|
||||
*/
|
||||
public abstract HttpContext createContext(String path, HttpHandler handler);
|
||||
|
||||
@ -308,16 +320,28 @@ public abstract class HttpServer {
|
||||
* <p>The class overview describes how incoming request URIs are
|
||||
* <a href="#mapping_description">mapped</a> to {@code HttpContext} instances.
|
||||
*
|
||||
* @apiNote The path should generally, but is not required to, end with '/'.
|
||||
* If the path does not end with '/', eg such as with {@code "/foo"} then
|
||||
* this would match requests with a path of {@code "/foobar"} or
|
||||
* {@code "/foo/bar"}.
|
||||
* @apiNote
|
||||
* The path should generally, but is not required to, end with {@code /}.
|
||||
* If the path does not end with {@code /}, e.g., such as with {@code /foo},
|
||||
* then some implementations may use <em>string prefix matching</em> where
|
||||
* this context path matches request paths {@code /foo},
|
||||
* {@code /foo/bar}, or {@code /foobar}. Others may use <em>path prefix
|
||||
* matching</em> where {@code /foo} matches request paths
|
||||
* {@code /foo} and {@code /foo/bar}, but not {@code /foobar}.
|
||||
*
|
||||
* @implNote
|
||||
* By default, the JDK built-in implementation uses path prefix matching.
|
||||
* String prefix matching can be enabled using the
|
||||
* {@link jdk.httpserver/##sun.net.httpserver.pathMatcher sun.net.httpserver.pathMatcher}
|
||||
* system property.
|
||||
*
|
||||
* @param path the root URI path to associate the context with
|
||||
* @throws IllegalArgumentException if path is invalid, or if a context
|
||||
* already exists for this path
|
||||
* @throws NullPointerException if path is {@code null}
|
||||
* @return an instance of {@code HttpContext}
|
||||
*
|
||||
* @see jdk.httpserver/##sun.net.httpserver.pathMatcher sun.net.httpserver.pathMatcher
|
||||
*/
|
||||
public abstract HttpContext createContext(String path);
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -101,7 +101,35 @@ import com.sun.net.httpserver.*;
|
||||
* <li><p><b>{@systemProperty sun.net.httpserver.nodelay}</b> (default: false)<br>
|
||||
* Boolean value, which if true, sets the {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY}
|
||||
* socket option on all incoming connections.
|
||||
* </li></ul>
|
||||
* </li>
|
||||
* <li>
|
||||
* <p><b>{@systemProperty sun.net.httpserver.pathMatcher}</b> (default:
|
||||
* {@code pathPrefix})<br/>
|
||||
*
|
||||
* The path matching scheme used to route requests to context handlers.
|
||||
* The property can be configured with one of the following values:</p>
|
||||
*
|
||||
* <blockquote>
|
||||
* <dl>
|
||||
* <dt>{@code pathPrefix} (default)</dt>
|
||||
* <dd>The request path must begin with the context path and all matching path
|
||||
* segments must be identical. For instance, the context path {@code /foo}
|
||||
* would match request paths {@code /foo}, {@code /foo/}, and {@code /foo/bar},
|
||||
* but not {@code /foobar}.</dd>
|
||||
* <dt>{@code stringPrefix}</dt>
|
||||
* <dd>The request path string must begin with the context path string. For
|
||||
* instance, the context path {@code /foo} would match request paths
|
||||
* {@code /foo}, {@code /foo/}, {@code /foo/bar}, and {@code /foobar}.
|
||||
* </dd>
|
||||
* </dl>
|
||||
* </blockquote>
|
||||
*
|
||||
* <p>In case of a blank or invalid value, the default will be used.</p>
|
||||
*
|
||||
* <p>This property and the ability to restore the string prefix matching
|
||||
* behavior may be removed in a future release.</p>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @apiNote The API and SPI in this module are designed and implemented to support a minimal
|
||||
* HTTP server and simple HTTP semantics primarily.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,13 +26,22 @@
|
||||
package sun.net.httpserver;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
class ContextList {
|
||||
|
||||
private static final System.Logger LOGGER = System.getLogger(ContextList.class.getName());
|
||||
|
||||
private final LinkedList<HttpContextImpl> list = new LinkedList<>();
|
||||
|
||||
public synchronized void add(HttpContextImpl ctx) {
|
||||
assert ctx != null;
|
||||
// `findContext(String protocol, String path, ContextPathMatcher matcher)`
|
||||
// expects the protocol to be lower-cased using ROOT locale, hence:
|
||||
assert ctx.getProtocol().equals(ctx.getProtocol().toLowerCase(Locale.ROOT));
|
||||
assert ctx.getPath() != null;
|
||||
// `ContextPathMatcher` expects context paths to be non-empty:
|
||||
assert !ctx.getPath().isEmpty();
|
||||
if (contains(ctx)) {
|
||||
throw new IllegalArgumentException("cannot add context to list");
|
||||
}
|
||||
@ -40,21 +49,25 @@ class ContextList {
|
||||
}
|
||||
|
||||
boolean contains(HttpContextImpl ctx) {
|
||||
return findContext(ctx.getProtocol(), ctx.getPath(), true) != null;
|
||||
return findContext(ctx.getProtocol(), ctx.getPath(), ContextPathMatcher.EXACT) != null;
|
||||
}
|
||||
|
||||
public synchronized int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/* initially contexts are located only by protocol:path.
|
||||
* Context with longest prefix matches (currently case-sensitive)
|
||||
/**
|
||||
* {@return the context with the longest case-sensitive prefix match}
|
||||
*
|
||||
* @param protocol the request protocol
|
||||
* @param path the request path
|
||||
*/
|
||||
synchronized HttpContextImpl findContext(String protocol, String path) {
|
||||
return findContext(protocol, path, false);
|
||||
HttpContextImpl findContext(String protocol, String path) {
|
||||
var matcher = ContextPathMatcher.ofConfiguredPrefixPathMatcher();
|
||||
return findContext(protocol, path, matcher);
|
||||
}
|
||||
|
||||
synchronized HttpContextImpl findContext(String protocol, String path, boolean exact) {
|
||||
private synchronized HttpContextImpl findContext(String protocol, String path, ContextPathMatcher matcher) {
|
||||
protocol = protocol.toLowerCase(Locale.ROOT);
|
||||
String longest = "";
|
||||
HttpContextImpl lc = null;
|
||||
@ -63,9 +76,7 @@ class ContextList {
|
||||
continue;
|
||||
}
|
||||
String cpath = ctx.getPath();
|
||||
if (exact && !cpath.equals(path)) {
|
||||
continue;
|
||||
} else if (!exact && !path.startsWith(cpath)) {
|
||||
if (!matcher.test(cpath, path)) {
|
||||
continue;
|
||||
}
|
||||
if (cpath.length() > longest.length()) {
|
||||
@ -76,10 +87,174 @@ class ContextList {
|
||||
return lc;
|
||||
}
|
||||
|
||||
private enum ContextPathMatcher implements BiPredicate<String, String> {
|
||||
|
||||
/**
|
||||
* Tests if both the request path and the context path are identical.
|
||||
*/
|
||||
EXACT(String::equals),
|
||||
|
||||
/**
|
||||
* Tests <em>string prefix matches</em> where the request path string
|
||||
* starts with the context path string.
|
||||
*
|
||||
* <h3>Examples</h3>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th rowspan="2">Context path</th>
|
||||
* <th colspan="4">Request path</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <th>/foo</th>
|
||||
* <th>/foo/</th>
|
||||
* <th>/foo/bar</th>
|
||||
* <th>/foobar</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td>/</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>/foo</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>/foo/</td>
|
||||
* <td>N</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>N</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*/
|
||||
STRING_PREFIX((contextPath, requestPath) -> requestPath.startsWith(contextPath)),
|
||||
|
||||
/**
|
||||
* Tests <em>path prefix matches</em> where path segments must have an
|
||||
* exact match.
|
||||
*
|
||||
* <h3>Examples</h3>
|
||||
*
|
||||
* <table>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th rowspan="2">Context path</th>
|
||||
* <th colspan="4">Request path</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <th>/foo</th>
|
||||
* <th>/foo/</th>
|
||||
* <th>/foo/bar</th>
|
||||
* <th>/foobar</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr>
|
||||
* <td>/</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>/foo</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>N</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>/foo/</td>
|
||||
* <td>N</td>
|
||||
* <td>Y</td>
|
||||
* <td>Y</td>
|
||||
* <td>N</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*/
|
||||
PATH_PREFIX((contextPath, requestPath) -> {
|
||||
|
||||
// Fast-path for `/`
|
||||
if ("/".equals(contextPath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Does the request path prefix match?
|
||||
if (requestPath.startsWith(contextPath)) {
|
||||
|
||||
// Is it an exact match?
|
||||
int contextPathLength = contextPath.length();
|
||||
if (requestPath.length() == contextPathLength) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is it a path-prefix match?
|
||||
assert contextPathLength > 0;
|
||||
return
|
||||
// Case 1: The request path starts with the context
|
||||
// path, but the context path has an extra path
|
||||
// separator suffix. For instance, the context path is
|
||||
// `/foo/` and the request path is `/foo/bar`.
|
||||
contextPath.charAt(contextPathLength - 1) == '/' ||
|
||||
// Case 2: The request path starts with the
|
||||
// context path, but the request path has an
|
||||
// extra path separator suffix. For instance,
|
||||
// context path is `/foo` and the request path
|
||||
// is `/foo/` or `/foo/bar`.
|
||||
requestPath.charAt(contextPathLength) == '/';
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
});
|
||||
|
||||
private final BiPredicate<String, String> predicate;
|
||||
|
||||
ContextPathMatcher(BiPredicate<String, String> predicate) {
|
||||
this.predicate = predicate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(String contextPath, String requestPath) {
|
||||
return predicate.test(contextPath, requestPath);
|
||||
}
|
||||
|
||||
private static ContextPathMatcher ofConfiguredPrefixPathMatcher() {
|
||||
var propertyName = "sun.net.httpserver.pathMatcher";
|
||||
var propertyValueDefault = "pathPrefix";
|
||||
var propertyValue = System.getProperty(propertyName, propertyValueDefault);
|
||||
return switch (propertyValue) {
|
||||
case "pathPrefix" -> ContextPathMatcher.PATH_PREFIX;
|
||||
case "stringPrefix" -> ContextPathMatcher.STRING_PREFIX;
|
||||
default -> {
|
||||
LOGGER.log(
|
||||
System.Logger.Level.WARNING,
|
||||
"System property \"{}\" contains an invalid value: \"{}\". Falling back to the default: \"{}\"",
|
||||
propertyName, propertyValue, propertyValueDefault);
|
||||
yield ContextPathMatcher.PATH_PREFIX;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public synchronized void remove(String protocol, String path)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
HttpContextImpl ctx = findContext(protocol, path, true);
|
||||
HttpContextImpl ctx = findContext(protocol, path, ContextPathMatcher.EXACT);
|
||||
if (ctx == null) {
|
||||
throw new IllegalArgumentException("cannot remove element from list");
|
||||
}
|
||||
|
||||
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.net.http.HttpClient.Builder.NO_PROXY;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/*
|
||||
* @test id=default
|
||||
* @bug 8272758
|
||||
* @summary Verifies path prefix matching using defaults
|
||||
* @build EchoHandler
|
||||
* @run junit ${test.main.class}
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=withProperty
|
||||
* @bug 8272758
|
||||
* @summary Verifies path prefix matching by providing a system property
|
||||
* @build EchoHandler
|
||||
* @run junit/othervm
|
||||
* -Dsun.net.httpserver.pathMatcher=pathPrefix
|
||||
* ${test.main.class}
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=withInvalidProperty
|
||||
* @bug 8272758
|
||||
* @summary Verifies path prefix matching by providing a system property
|
||||
* containing an invalid value, and observing it fall back to the
|
||||
* default
|
||||
* @build EchoHandler
|
||||
* @run junit/othervm
|
||||
* -Dsun.net.httpserver.pathMatcher=noSuchMatcher
|
||||
* ${test.main.class}
|
||||
*/
|
||||
|
||||
public class ContextPathMatcherPathPrefixTest {
|
||||
|
||||
protected static final HttpClient CLIENT =
|
||||
HttpClient.newBuilder().proxy(NO_PROXY).build();
|
||||
|
||||
@AfterAll
|
||||
static void stopClient() {
|
||||
CLIENT.shutdownNow();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContextPathOfEmptyString() {
|
||||
var iae = assertThrows(IllegalArgumentException.class, () -> new Infra(""));
|
||||
assertEquals("Illegal value for path or protocol", iae.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContextPathAtRoot() throws Exception {
|
||||
try (var infra = new Infra("/")) {
|
||||
infra.expect(200, "/foo", "/foo/", "/foo/bar", "/foobar");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContextPathAtSubDir() throws Exception {
|
||||
try (var infra = new Infra("/foo")) {
|
||||
infra.expect(200, "/foo", "/foo/", "/foo/bar");
|
||||
infra.expect(404, "/foobar");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContextPathAtSubDirWithTrailingSlash() throws Exception {
|
||||
try (var infra = new Infra("/foo/")) {
|
||||
infra.expect(200, "/foo/", "/foo/bar");
|
||||
infra.expect(404, "/foo", "/foobar");
|
||||
}
|
||||
}
|
||||
|
||||
protected static final class Infra implements AutoCloseable {
|
||||
|
||||
private static final InetSocketAddress LO_SA_0 =
|
||||
new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||
|
||||
private static final HttpHandler HANDLER = new EchoHandler();
|
||||
|
||||
private final HttpServer server;
|
||||
|
||||
private final String contextPath;
|
||||
|
||||
protected Infra(String contextPath) throws IOException {
|
||||
this.server = HttpServer.create(LO_SA_0, 10);
|
||||
server.createContext(contextPath, HANDLER);
|
||||
server.start();
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
protected void expect(int statusCode, String... requestPaths) throws Exception {
|
||||
for (String requestPath : requestPaths) {
|
||||
var requestURI = URI.create("http://%s:%s%s".formatted(
|
||||
server.getAddress().getHostString(),
|
||||
server.getAddress().getPort(),
|
||||
requestPath));
|
||||
var request = HttpRequest.newBuilder(requestURI).build();
|
||||
var response = CLIENT.send(request, HttpResponse.BodyHandlers.discarding());
|
||||
assertEquals(
|
||||
statusCode, response.statusCode(),
|
||||
"unexpected status code " + Map.of(
|
||||
"contextPath", contextPath,
|
||||
"requestPath", requestPath));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
server.stop(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8272758
|
||||
* @summary Verifies string prefix matching configured using a system property
|
||||
* @build ContextPathMatcherPathPrefixTest
|
||||
* EchoHandler
|
||||
* @run junit/othervm
|
||||
* -Dsun.net.httpserver.pathMatcher=stringPrefix
|
||||
* ${test.main.class}
|
||||
*/
|
||||
|
||||
class ContextPathMatcherStringPrefixTest extends ContextPathMatcherPathPrefixTest {
|
||||
|
||||
@Test
|
||||
@Override
|
||||
void testContextPathAtRoot() throws Exception {
|
||||
try (var infra = new Infra("/")) {
|
||||
infra.expect(200, "/foo", "/foo/", "/foo/bar", "/foobar");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
void testContextPathAtSubDir() throws Exception {
|
||||
try (var infra = new Infra("/foo")) {
|
||||
infra.expect(200, "/foo", "/foo/", "/foo/bar", "/foobar");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
void testContextPathAtSubDirWithTrailingSlash() throws Exception {
|
||||
try (var infra = new Infra("/foo/")) {
|
||||
infra.expect(200, "/foo/", "/foo/bar");
|
||||
infra.expect(404, "/foo", "/foobar");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -208,9 +208,9 @@ public class PlainProxyConnectionTest {
|
||||
System.out.println("Server is: " + server.getAddress().toString());
|
||||
URI uri = new URI("http", null,
|
||||
server.getAddress().getAddress().getHostAddress(),
|
||||
server.getAddress().getPort(), PATH + "x",
|
||||
server.getAddress().getPort(), PATH + "/x",
|
||||
null, null);
|
||||
URI proxiedURI = new URI("http://some.host.that.does.not.exist:4242" + PATH + "x");
|
||||
URI proxiedURI = new URI("http://some.host.that.does.not.exist:4242" + PATH + "/x");
|
||||
|
||||
performSanityTest(server, uri, proxiedURI);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user