Summary
The behavior of the JDK built-in com.sun.net.httpserver.HttpServer implementation when matching an incoming request path to an HttpContext path is switched from string prefix matching to path prefix matching where the request path must begin with the context path and all matching path segments must be identical.
For instance, the context path /foo would match request paths /foo, /foo/, and /foo/bar, but (different from the old behavior) not /foobar.
The old behavior, i.e., string prefix matching, can be enabled using the newly introduced sun.net.httpserver.pathMatcher system property.
Problem
HttpServer uses string prefix matching to map an incoming request to a configured context.
For instance, the context path /foo would match request paths /foo, /foo/, /foo/bar, and also, mostly unexpectedly, /foobar.
This matching scheme has two major downsides:
- It has been causing confusion while implementing tests — see this most recent example.
Due to this nuanced behavior, tests need to create handlers for
/foo/and target requests to/foo/barto ensure that the right handler is invoked. This not only confuses the developer implementing the test, but also the person reading the code. - The behavior is unexpected. Neither popular JEE/Jakarta frameworks (e.g., Helidon, Tomcat), nor others (e.g., Spring Boot) map handlers this way.
Note that this surprising behavior was earlier reported in JDK-8225479. There the conclusion was to document the behavior.
Solution
The behaviour of the JDK built-in implementation is switched from string prefix matching to path prefix matching where the request path must begin with the context path and all matching path segments must be identical.
In HttpServer::createContext modify @apiNote to better explain possible discrepencies between implementations, and allow an implementation to use either path prefix matching or string prefix matching behaviours. Existing implementations will thus remain conformant to the specification.
An @implNote is added to indicate that the JDK built-in implementation uses the new path prefix matching - as opposed to string prefix. This is a change of behaviour in the built-in implementation: in order to provide a way out for workflows depending on the earlier behavior, a new sun.net.httpserver.pathMatcher system property is introduced.
Existing third party implementations of the HttpServer SPI, such as that based on Jetty Server, are expected to be typically already implementing the path prefix matching behaviour. Changing the odd behavior of the JDK built-in implementation to use path prefix matching is thus expected to bring more consistency between the different existing implementations of the SPI.
Specification
/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpServer.java
@@ -278,10 +278,20 @@ public static HttpServer create(InetSocketAddress addr,
* <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 static HttpServer create(InetSocketAddress addr,
* 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 static HttpServer create(InetSocketAddress addr,
* <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);
/src/jdk.httpserver/share/classes/module-info.java
@@ -101,7 +101,32 @@
* <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.
- csr of
-
JDK-8272758 Improve HttpServer to avoid partial file name matches while mapping request path to context path
-
- Resolved
-