diff --git a/nima/tests/integration/webserver/webserver/pom.xml b/nima/tests/integration/webserver/webserver/pom.xml
index 88934e31cc7..8af0ac6d4ed 100644
--- a/nima/tests/integration/webserver/webserver/pom.xml
+++ b/nima/tests/integration/webserver/webserver/pom.xml
@@ -42,6 +42,11 @@
junit-jupiter-api
test
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
org.hamcrest
hamcrest-all
diff --git a/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RoutingTest.java b/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RoutingTest.java
index fa5f3e97de0..492e5c9fcab 100644
--- a/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RoutingTest.java
+++ b/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RoutingTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,23 @@
package io.helidon.nima.tests.integration.server;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
import io.helidon.common.http.Http;
import io.helidon.nima.testing.junit5.webserver.ServerTest;
import io.helidon.nima.testing.junit5.webserver.SetUpRoute;
+import io.helidon.nima.testing.junit5.webserver.SetUpServer;
import io.helidon.nima.webclient.http1.Http1Client;
import io.helidon.nima.webclient.http1.Http1ClientResponse;
+import io.helidon.nima.webclient.http1.Http1ClientRequest;
+import io.helidon.nima.webserver.WebServer;
import io.helidon.nima.webserver.http.HttpRouting;
+import io.helidon.nima.webserver.http.ServerRequest;
+import io.helidon.nima.webserver.http.ServerResponse;
+
+import static io.helidon.common.testing.http.junit5.HttpHeaderMatcher.hasHeader;
import org.junit.jupiter.api.Test;
@@ -29,39 +40,49 @@
import static org.hamcrest.MatcherAssert.assertThat;
@ServerTest
-class RoutingTest {
- private final Http1Client client;
+class RoutingTest extends RoutingTestBase {
RoutingTest(Http1Client client) {
this.client = client;
}
- @SetUpRoute
- static void routing(HttpRouting.Builder router) {
- router.get("/my path", (req, res) -> res.send("done"))
- .get("/českáCesta", (req, res) -> res.send("done"));
- }
-
- @Test
- void testRouteWithSpace() {
- try (Http1ClientResponse response = client.get("/my path").request()) {
-
- assertThat(response.status(), is(Http.Status.OK_200));
-
- String message = response.as(String.class);
- assertThat(message, is("done"));
- }
- }
-
- @Test
- void testRouteWithUtf8() {
- try (Http1ClientResponse response = client.get("/českáCesta").request()) {
-
- assertThat(response.status(), is(Http.Status.OK_200));
-
- String message = response.as(String.class);
- assertThat(message, is("done"));
- }
-
+ @SetUpServer
+ static void setUp(WebServer.Builder builder) {
+ builder.addRouting(HttpRouting.builder()
+ .get("/my path", (req, res) -> res.send("done"))
+ .get("/českáCesta", (req, res) -> res.send("done"))
+ // shortcut methods with path matchers
+ .get("/wildcard_*", (req, res) -> res.send("wildcard_test1"))
+ .post("/wildcard/*", (req, res) -> res.send("wildcard_test2"))
+ // shortcut methods with path
+ .get("/get", (req, res) -> res.send("get"))
+ .post("/post", (req, res) -> res.send("post"))
+ .put("/put", (req, res) -> res.send("put"))
+ .delete("/delete", (req, res) -> res.send("delete"))
+ .head("/head", (req, res) -> res.send("head"))
+ .options("/options", (req, res) -> res.send("options"))
+ .trace("/trace", (req, res) -> res.send("trace"))
+ .patch("/patch", (req, res) -> res.send("patch"))
+ .any("/any", (req, res) -> res.send("any"))
+ // shortcut methods using multiple handlers
+ .get("/get_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("get_multi"))
+ .post("/post_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("post_multi"))
+ .put("/put_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("put_multi"))
+ .delete("/delete_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("delete_multi"))
+ .head("/head_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("head_multi"))
+ .options("/options_multi",
+ RoutingTestBase::multiHandler,
+ (req, res) -> res.send("options_multi"))
+ .trace("/trace_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("trace_multi"))
+ .patch("/patch_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("patch_multi"))
+ // shortcut methods with no path pattern
+ .get((req, res) -> res.send("get_catchall"))
+ .post((req, res) -> res.send("post_catchall"))
+ .put((req, res) -> res.send("put_catchall"))
+ .delete((req, res) -> res.send("delete_catchall"))
+ .head((req, res) -> res.send("head_catchall"))
+ .options((req, res) -> res.send("options_catchall"))
+ .trace((req, res) -> res.send("trace_catchall"))
+ .patch((req, res) -> res.send("patch_catchall")));
}
}
diff --git a/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RoutingTestBase.java b/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RoutingTestBase.java
new file mode 100644
index 00000000000..3db3beab6bb
--- /dev/null
+++ b/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RoutingTestBase.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.helidon.nima.tests.integration.server;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Stream;
+
+import io.helidon.common.http.Http;
+import io.helidon.nima.testing.junit5.webserver.ServerTest;
+import io.helidon.nima.testing.junit5.webserver.SetUpRoute;
+import io.helidon.nima.webclient.http1.Http1Client;
+import io.helidon.nima.webclient.http1.Http1ClientResponse;
+import io.helidon.nima.webclient.http1.Http1ClientRequest;
+import io.helidon.nima.webserver.http.HttpRouting;
+import io.helidon.nima.webserver.http.ServerRequest;
+import io.helidon.nima.webserver.http.ServerResponse;
+
+import static io.helidon.common.testing.http.junit5.HttpHeaderMatcher.hasHeader;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+// Use by both RoutingTest and RulesTest to share the same test methods
+class RoutingTestBase {
+ private static final Http.HeaderValue MULTI_HANDLER = Http.Header.createCached(
+ Http.Header.create("X-Multi-Handler"), "true");
+ static Http1Client client;
+ // Functions that will be used to execute http webclient shortcut methods
+ private static Function get = x -> client.get(x);
+ private static Function post = x -> client.post(x);
+ private static Function put = x -> client.put(x);
+ private static Function delete = x -> client.delete(x);
+ private static Function head = x -> client.head(x);
+ private static Function options = x -> client.options(x);
+ private static Function trace = x -> client.trace(x);
+ private static Function patch = x -> client.patch(x);
+
+ // Add header to indicate that this is a multi handler routing
+ static void multiHandler(ServerRequest req, ServerResponse res) {
+ res.headers().set(MULTI_HANDLER);
+ res.next();
+ }
+
+ @Test
+ void testRouteWithSpace() {
+ try (Http1ClientResponse response = client.get("/my path").request()) {
+
+ assertThat(response.status(), is(Http.Status.OK_200));
+
+ String message = response.as(String.class);
+ assertThat(message, is("done"));
+ }
+ }
+
+ @Test
+ void testRouteWithUtf8() {
+ try (Http1ClientResponse response = client.get("/českáCesta").request()) {
+
+ assertThat(response.status(), is(Http.Status.OK_200));
+
+ String message = response.as(String.class);
+ assertThat(message, is("done"));
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("basic")
+ void testHttpShortcutMethods(Function request, String path, String responseMessage) {
+ try (Http1ClientResponse response = request.apply(path).request()) {
+ assertThat(response.status(), is(Http.Status.OK_200));
+ assertThat(response.as(String.class), is(responseMessage));
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("withoutPathPattern")
+ void testHttpShortcutMethodsWithoutPathPattern(Function request,
+ String path,
+ String responseMessage) {
+ try (Http1ClientResponse response = request.apply(path).request()) {
+ assertThat(response.status(), is(Http.Status.OK_200));
+ assertThat(response.as(String.class), is(responseMessage));
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("withPathMatcher")
+ void testHttpShortcutMethodsWithPathMatcher(Function request,
+ String path,
+ String responseMessage) {
+ try (Http1ClientResponse response = request.apply(path).request()) {
+ assertThat(response.status(), is(Http.Status.OK_200));
+ assertThat(response.as(String.class), is(responseMessage));
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("withMultiHandlers")
+ void testHttpShortcutMethodsWithMultiHandlers(Function request,
+ String path,
+ String responseMessage) {
+ try (Http1ClientResponse response = request.apply(path).request()) {
+ assertThat(response.status(), is(Http.Status.OK_200));
+ assertThat(response.headers(), hasHeader(MULTI_HANDLER));
+ assertThat(response.as(String.class), is(responseMessage));
+ }
+ }
+
+ private static Stream basic() {
+ return Stream.of(
+ arguments(get, "/get", "get"),
+ arguments(post, "/post", "post"),
+ arguments(put, "/put", "put"),
+ arguments(delete, "/delete", "delete"),
+ arguments(head, "/head", "head"),
+ arguments(options, "/options", "options"),
+ arguments(trace, "/trace", "trace"),
+ arguments(patch, "/patch", "patch"),
+ arguments(delete, "/any", "any"),
+ arguments(post, "/any", "any"),
+ arguments(get, "/any", "any")
+ );
+ }
+
+ private static Stream withoutPathPattern() {
+ return Stream.of(
+ arguments(get, "/get_catchall", "get_catchall"),
+ arguments(post, "/post_catchall", "post_catchall"),
+ arguments(put, "/put_catchall", "put_catchall"),
+ arguments(delete, "/delete_catchall", "delete_catchall"),
+ arguments(head, "/head_catchall", "head_catchall"),
+ arguments(options, "/options_catchall", "options_catchall"),
+ arguments(trace, "/trace_catchall", "trace_catchall"),
+ arguments(patch, "/patch_catchall", "patch_catchall")
+ );
+ }
+
+ private static Stream withPathMatcher() {
+ return Stream.of(
+ arguments(get, "/wildcard_any", "wildcard_test1"),
+ arguments(post, "/wildcard/any", "wildcard_test2")
+ );
+ }
+
+ private static Stream withMultiHandlers() {
+ return Stream.of(
+ arguments(get, "/get_multi", "get_multi"),
+ arguments(post, "/post_multi", "post_multi"),
+ arguments(put, "/put_multi", "put_multi"),
+ arguments(delete, "/delete_multi", "delete_multi"),
+ arguments(head, "/head_multi", "head_multi"),
+ arguments(options, "/options_multi", "options_multi"),
+ arguments(trace, "/trace_multi", "trace_multi"),
+ arguments(patch, "/patch_multi", "patch_multi")
+ );
+ }
+}
diff --git a/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RulesTest.java b/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RulesTest.java
new file mode 100644
index 00000000000..070020b4329
--- /dev/null
+++ b/nima/tests/integration/webserver/webserver/src/test/java/io/helidon/nima/tests/integration/server/RulesTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.helidon.nima.tests.integration.server;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import io.helidon.common.http.Http;
+import io.helidon.nima.testing.junit5.webserver.ServerTest;
+import io.helidon.nima.testing.junit5.webserver.SetUpRoute;
+import io.helidon.nima.webclient.http1.Http1Client;
+import io.helidon.nima.webclient.http1.Http1ClientResponse;
+import io.helidon.nima.webclient.http1.Http1ClientRequest;
+import io.helidon.nima.webserver.http.HttpRules;
+import io.helidon.nima.webserver.http.ServerRequest;
+import io.helidon.nima.webserver.http.ServerResponse;
+
+import static io.helidon.common.testing.http.junit5.HttpHeaderMatcher.hasHeader;
+
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+@ServerTest
+class RulesTest extends RoutingTestBase {
+
+ RulesTest(Http1Client client) {
+ this.client = client;
+ }
+
+ @SetUpRoute
+ static void routing(HttpRules rules) {
+ rules.get("/my path", (req, res) -> res.send("done"))
+ .get("/českáCesta", (req, res) -> res.send("done"))
+ // shortcut methods with path matchers
+ .get("/wildcard_*", (req, res) -> res.send("wildcard_test1"))
+ .post("/wildcard/*", (req, res) -> res.send("wildcard_test2"))
+ // shortcut methods with path
+ .get("/get", (req, res) -> res.send("get"))
+ .post("/post", (req, res) -> res.send("post"))
+ .put("/put", (req, res) -> res.send("put"))
+ .delete("/delete", (req, res) -> res.send("delete"))
+ .head("/head", (req, res) -> res.send("head"))
+ .options("/options", (req, res) -> res.send("options"))
+ .trace("/trace", (req, res) -> res.send("trace"))
+ .patch("/patch", (req, res) -> res.send("patch"))
+ .any("/any", (req, res) -> res.send("any"))
+ // shortcut methods using multiple handlers
+ .get("/get_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("get_multi"))
+ .post("/post_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("post_multi"))
+ .put("/put_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("put_multi"))
+ .delete("/delete_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("delete_multi"))
+ .head("/head_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("head_multi"))
+ .options("/options_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("options_multi"))
+ .trace("/trace_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("trace_multi"))
+ .patch("/patch_multi", RoutingTestBase::multiHandler, (req, res) -> res.send("patch_multi"))
+ // shortcut methods with no path pattern
+ .get((req, res) -> res.send("get_catchall"))
+ .post((req, res) -> res.send("post_catchall"))
+ .put((req, res) -> res.send("put_catchall"))
+ .delete((req, res) -> res.send("delete_catchall"))
+ .head((req, res) -> res.send("head_catchall"))
+ .options((req, res) -> res.send("options_catchall"))
+ .trace((req, res) -> res.send("trace_catchall"))
+ .patch((req, res) -> res.send("patch_catchall"));
+ }
+}
diff --git a/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http/HttpRouting.java b/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http/HttpRouting.java
index ff7fdab3df9..35e96a52004 100644
--- a/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http/HttpRouting.java
+++ b/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http/HttpRouting.java
@@ -33,7 +33,6 @@
import io.helidon.common.http.HttpPrologue;
import io.helidon.common.http.NotFoundException;
import io.helidon.common.http.PathMatcher;
-import io.helidon.common.http.PathMatchers;
import io.helidon.common.http.RequestException;
import io.helidon.nima.webserver.ConnectionContext;
import io.helidon.nima.webserver.Routing;
@@ -179,6 +178,13 @@ default Builder route(Predicate methodPredicate, PathMatcher pathMa
.handler(handler));
}
+ @Override
+ default Builder route(Http.Method method, Handler handler) {
+ return route(HttpRoute.builder()
+ .methods(method)
+ .handler(handler));
+ }
+
@Override
default Builder get(String pathPattern, Handler... handlers) {
for (Handler handler : handlers) {
@@ -190,7 +196,55 @@ default Builder get(String pathPattern, Handler... handlers) {
@Override
default Builder get(Handler... handlers) {
for (Handler handler : handlers) {
- route(Http.Method.GET, PathMatchers.any(), handler);
+ route(Http.Method.GET, handler);
+ }
+ return this;
+ }
+
+ @Override
+ default Builder post(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.POST, pathPattern, handler);
+ }
+ return this;
+ }
+
+ @Override
+ default Builder post(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.POST, handler);
+ }
+ return this;
+ }
+
+ @Override
+ default Builder put(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.PUT, pathPattern, handler);
+ }
+ return this;
+ }
+
+ @Override
+ default Builder put(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.PUT, handler);
+ }
+ return this;
+ }
+
+ @Override
+ default Builder delete(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.DELETE, pathPattern, handler);
+ }
+ return this;
+ }
+
+ @Override
+ default Builder delete(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.DELETE, handler);
}
return this;
}
@@ -203,6 +257,14 @@ default Builder head(String pathPattern, Handler... handlers) {
return this;
}
+ @Override
+ default Builder head(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.HEAD, handler);
+ }
+ return this;
+ }
+
@Override
default Builder options(String pathPattern, Handler... handlers) {
for (Handler handler : handlers) {
@@ -212,30 +274,62 @@ default Builder options(String pathPattern, Handler... handlers) {
}
@Override
- default Builder put(String pathPattern, Handler handler) {
- return route(Http.Method.PUT, pathPattern, handler);
+ default Builder options(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.OPTIONS, handler);
+ }
+ return this;
}
@Override
- default Builder post(String pathPattern, Handler... handlers) {
+ default Builder trace(String pathPattern, Handler... handlers) {
for (Handler handler : handlers) {
- route(Http.Method.POST, pathPattern, handler);
+ route(Http.Method.TRACE, pathPattern, handler);
}
return this;
}
@Override
- default Builder post(String pathPattern, Handler handler) {
- return route(HttpRoute.builder()
- .methods(Http.Method.POST)
- .path(pathPattern)
- .handler(handler));
+ default Builder trace(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.TRACE, handler);
+ }
+ return this;
}
@Override
- default Builder any(Handler handler) {
- return route(HttpRoute.builder()
- .handler(handler));
+ default Builder patch(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.PATCH, pathPattern, handler);
+ }
+ return this;
+ }
+
+ @Override
+ default Builder patch(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.PATCH, handler);
+ }
+ return this;
+ }
+
+ @Override
+ default Builder any(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(HttpRoute.builder()
+ .path(pathPattern)
+ .handler(handler));
+ }
+ return this;
+ }
+
+ @Override
+ default Builder any(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(HttpRoute.builder()
+ .handler(handler));
+ }
+ return this;
}
@Override
diff --git a/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http/HttpRules.java b/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http/HttpRules.java
index 31a38a6b2db..e2aeb7972f7 100644
--- a/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http/HttpRules.java
+++ b/nima/webserver/webserver/src/main/java/io/helidon/nima/webserver/http/HttpRules.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@ public interface HttpRules {
/**
* Register a service on sub-path of the current path.
*
- * @param pathPattern path pattern
+ * @param pathPattern URI path pattern
* @param service service to register
* @return updated rules
*/
@@ -68,9 +68,9 @@ default HttpRules route(Supplier extends HttpRoute> route) {
/**
* Add a route.
*
- * @param method method to handle
- * @param pathPattern path pattern
- * @param handler handler to use
+ * @param method HTTP method to handle
+ * @param pathPattern URI path pattern
+ * @param handler handler to process HTTP request
* @return updated rules
*/
default HttpRules route(Http.Method method, String pathPattern, Handler handler) {
@@ -80,9 +80,9 @@ default HttpRules route(Http.Method method, String pathPattern, Handler handler)
/**
* Add a route.
*
- * @param method method to handle
- * @param pathMatcher path matcher
- * @param handler handler
+ * @param method HTTP method to handle
+ * @param pathMatcher URI path matcher, see {@link io.helidon.common.http.PathMatchers#create(String)}
+ * @param handler handler to process HTTP request
* @return updated rules
*/
default HttpRules route(Http.Method method, PathMatcher pathMatcher, Handler handler) {
@@ -92,9 +92,9 @@ default HttpRules route(Http.Method method, PathMatcher pathMatcher, Handler han
/**
* Add a route.
*
- * @param methodPredicate method predicate, see {@link Http.Method#predicate(io.helidon.common.http.Http.Method...)}
- * @param pathMatcher path matcher, see {@link io.helidon.common.http.PathMatchers#create(String)}
- * @param handler handler
+ * @param methodPredicate HTTP method predicate, see {@link Http.Method#predicate(io.helidon.common.http.Http.Method...)}
+ * @param pathMatcher URI path matcher, see {@link io.helidon.common.http.PathMatchers#create(String)}
+ * @param handler handler to process HTTP request
* @return updated rules
*/
default HttpRules route(Predicate methodPredicate, PathMatcher pathMatcher, Handler handler) {
@@ -105,11 +105,25 @@ default HttpRules route(Predicate methodPredicate, PathMatcher path
.build());
}
+ /**
+ * Add a route.
+ *
+ * @param method HTTP method to handle
+ * @param handler handler to process HTTP request
+ * @return updated rules
+ */
+ default HttpRules route(Http.Method method, Handler handler) {
+ return route(HttpRoute.builder()
+ .methods(method)
+ .handler(handler)
+ .build());
+ }
+
/**
* Add a get route.
*
- * @param pathPattern path pattern
- * @param handlers handlers
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
default HttpRules get(String pathPattern, Handler... handlers) {
@@ -122,12 +136,93 @@ default HttpRules get(String pathPattern, Handler... handlers) {
/**
* Add a get route.
*
- * @param handlers handlers
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
default HttpRules get(Handler... handlers) {
for (Handler handler : handlers) {
- route(Http.Method.GET, PathMatchers.any(), handler);
+ route(Http.Method.GET, handler);
+ }
+ return this;
+ }
+
+ /**
+ * Add a post route.
+ *
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
+ * @return updated rules
+ */
+ default HttpRules post(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.POST, pathPattern, handler);
+ }
+ return this;
+ }
+
+ /**
+ * Add a post route.
+ *
+ * @param handlers handlers to process HTTP request
+ * @return updated rules
+ */
+ default HttpRules post(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.POST, handler);
+ }
+ return this;
+ }
+
+ /**
+ * Add a put route.
+ *
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
+ * @return updated rules
+ */
+ default HttpRules put(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.PUT, pathPattern, handler);
+ }
+ return this;
+ }
+
+ /**
+ * Add a put route.
+ *
+ * @param handlers handlers to process HTTP request
+ * @return updated rules
+ */
+ default HttpRules put(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.PUT, handler);
+ }
+ return this;
+ }
+
+ /**
+ * Add a delete route.
+ *
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
+ * @return updated rules
+ */
+ default HttpRules delete(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.DELETE, pathPattern, handler);
+ }
+ return this;
+ }
+
+ /**
+ * Add a delete route.
+ *
+ * @param handlers handlers to process HTTP request
+ * @return updated rules
+ */
+ default HttpRules delete(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.DELETE, handler);
}
return this;
}
@@ -135,8 +230,8 @@ default HttpRules get(Handler... handlers) {
/**
* Add a head route.
*
- * @param pathPattern path pattern
- * @param handlers handlers
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
default HttpRules head(String pathPattern, Handler... handlers) {
@@ -147,124 +242,124 @@ default HttpRules head(String pathPattern, Handler... handlers) {
}
/**
- * Add an options route.
+ * Add a head route.
*
- * @param pathPattern path pattern
- * @param handlers handlers
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
- default HttpRules options(String pathPattern, Handler... handlers) {
+ default HttpRules head(Handler... handlers) {
for (Handler handler : handlers) {
- route(Http.Method.OPTIONS, pathPattern, handler);
+ route(Http.Method.HEAD, handler);
}
return this;
}
/**
- * Add a put route.
+ * Add an options route.
*
- * @param pathPattern path pattern
- * @param handler handler
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
- default HttpRules put(String pathPattern, Handler handler) {
- return route(Http.Method.PUT, pathPattern, handler);
+ default HttpRules options(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.OPTIONS, pathPattern, handler);
+ }
+ return this;
}
/**
- * Add a post route.
+ * Add an options route.
*
- * @param pathPattern path pattern
- * @param handlers handlers
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
- default HttpRules post(String pathPattern, Handler... handlers) {
+ default HttpRules options(Handler... handlers) {
for (Handler handler : handlers) {
- route(Http.Method.POST, pathPattern, handler);
+ route(Http.Method.OPTIONS, handler);
}
return this;
}
/**
- * Add a post route.
+ * Add an options route.
*
- * @param pathPattern path pattern
- * @param handler handler
- * @return updated builder
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
+ * @return updated rules
*/
- default HttpRules post(String pathPattern, Handler handler) {
- return route(HttpRoute.builder()
- .methods(Http.Method.POST)
- .path(pathPattern)
- .handler(handler));
+ default HttpRules trace(String pathPattern, Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.TRACE, pathPattern, handler);
+ }
+ return this;
}
/**
- * Add a route that executes on any HTTP method and any path.
+ * Add an options route.
*
- * @param handler handler
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
- default HttpRules any(Handler handler) {
- return route(HttpRoute.builder()
- .handler(handler));
+ default HttpRules trace(Handler... handlers) {
+ for (Handler handler : handlers) {
+ route(Http.Method.TRACE, handler);
+ }
+ return this;
}
/**
- * Add a route that executes on any HTTP method and any path.
+ * Add an options route.
*
- * @param pathPattern path pattern
- * @param handlers handlers
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
- default HttpRules any(String pathPattern, Handler... handlers) {
+ default HttpRules patch(String pathPattern, Handler... handlers) {
for (Handler handler : handlers) {
- route(HttpRoute.builder()
- .path(pathPattern)
- .handler(handler));
+ route(Http.Method.PATCH, pathPattern, handler);
}
return this;
}
/**
- * Add a delete route.
+ * Add an options route.
*
- * @param handlers handlers
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
- default HttpRules delete(Handler... handlers) {
+ default HttpRules patch(Handler... handlers) {
for (Handler handler : handlers) {
- route(HttpRoute.builder()
- .methods(Http.Method.DELETE)
- .handler(handler));
+ route(Http.Method.PATCH, handler);
}
return this;
}
/**
- * Add a delete route.
+ * Add a route that executes on any HTTP method and any path.
*
- * @param pathPattern path pattern to register the handler(s) on
- * @param handlers handlers
+ * @param pathPattern URI path pattern
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
- default HttpRules delete(String pathPattern, Handler... handlers) {
+ default HttpRules any(String pathPattern, Handler... handlers) {
for (Handler handler : handlers) {
- route(Http.Method.DELETE, pathPattern, handler);
+ route(HttpRoute.builder()
+ .path(pathPattern)
+ .handler(handler));
}
return this;
}
/**
- * Add a put route.
+ * Add a route that executes on any HTTP method and any path.
*
- * @param handlers handlers
+ * @param handlers handlers to process HTTP request
* @return updated rules
*/
- default HttpRules put(Handler... handlers) {
+ default HttpRules any(Handler... handlers) {
for (Handler handler : handlers) {
route(HttpRoute.builder()
- .methods(Http.Method.PUT)
.handler(handler));
}
return this;
@@ -273,8 +368,8 @@ default HttpRules put(Handler... handlers) {
/**
* Add a route.
*
- * @param method method to handle
- * @param pathPattern path pattern
+ * @param method HTTP method to handle
+ * @param pathPattern URI path pattern
* @param handler handler as a consumer of {@link ServerRequest}
* @return updated builder
*/
@@ -288,8 +383,8 @@ default HttpRules route(Http.Method method, String pathPattern, Consumer hand
.handler(Handler.create(handler)));
}
-
}
diff --git a/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http/HttpRoutingTest.java b/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http/HttpRoutingTest.java
new file mode 100644
index 00000000000..279127bf9e0
--- /dev/null
+++ b/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http/HttpRoutingTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.helidon.nima.webserver.http;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import io.helidon.common.http.Http;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class HttpRoutingTest {
+ private static final Handler handler = (req, res) -> res.send("done");
+
+ // Functions that will be used to execute Routing http method shortcuts
+ private static Function get = x ->
+ (FakeHttpRoutingBuilder) (x == null ? new FakeHttpRoutingBuilder().get(handler) : new FakeHttpRoutingBuilder().get(x, handler));
+ private static Function post = x ->
+ (FakeHttpRoutingBuilder) (x == null ? new FakeHttpRoutingBuilder().post(handler) : new FakeHttpRoutingBuilder().post(x, handler));
+ private static Function put = x ->
+ (FakeHttpRoutingBuilder) (x == null ? new FakeHttpRoutingBuilder().put(handler) : new FakeHttpRoutingBuilder().put(x, handler));
+ private static Function delete = x ->
+ (FakeHttpRoutingBuilder) (x == null ? new FakeHttpRoutingBuilder().delete(handler) : new FakeHttpRoutingBuilder().delete(x, handler));
+ private static Function head = x ->
+ (FakeHttpRoutingBuilder) (x == null ? new FakeHttpRoutingBuilder().head(handler) : new FakeHttpRoutingBuilder().head(x, handler));
+ private static Function options = x ->
+ (FakeHttpRoutingBuilder) (x == null ? new FakeHttpRoutingBuilder().options(handler) : new FakeHttpRoutingBuilder().options(x, handler));
+ private static Function trace = x ->
+ (FakeHttpRoutingBuilder) (x == null ? new FakeHttpRoutingBuilder().trace(handler) : new FakeHttpRoutingBuilder().trace(x, handler));
+ private static Function patch = x ->
+ (FakeHttpRoutingBuilder) (x == null ? new FakeHttpRoutingBuilder().patch(handler) : new FakeHttpRoutingBuilder().patch(x, handler));
+
+ @ParameterizedTest
+ @MethodSource("httpMethodShortcut")
+ void testHttpMethodShortcut(Http.Method method,
+ Function request) {
+ FakeHttpRoutingBuilder rule = request.apply(null);
+ assertThat(rule.getMethod(), is(method));
+ assertThat(rule.getPathPattern(), is(nullValue()));
+ assertThat(rule.getHandler(), is(handler));
+ }
+
+ @ParameterizedTest
+ @MethodSource("httpMethodShortcutWithPathPattern")
+ void testHttpMethodShortcutWithPathPattern(Http.Method method,
+ Function request,
+ String pathPattern) {
+ FakeHttpRoutingBuilder rule = request.apply(pathPattern);
+ assertThat(rule.getMethod(), is(method));
+ assertThat(rule.getPathPattern(), is(pathPattern));
+ assertThat(rule.getHandler(), is(handler));
+
+ }
+
+ private static Stream httpMethodShortcut() {
+ return Stream.of(
+ arguments(Http.Method.GET, get),
+ arguments(Http.Method.POST, post),
+ arguments(Http.Method.PUT, put),
+ arguments(Http.Method.DELETE, delete),
+ arguments(Http.Method.HEAD, head),
+ arguments(Http.Method.OPTIONS, options),
+ arguments(Http.Method.TRACE, trace),
+ arguments(Http.Method.PATCH, patch)
+ );
+ }
+
+ private static Stream httpMethodShortcutWithPathPattern() {
+ return Stream.of(
+ arguments(Http.Method.GET, get, "/get"),
+ arguments(Http.Method.POST, post, "/post"),
+ arguments(Http.Method.PUT, put, "/put"),
+ arguments(Http.Method.DELETE, delete, "/delete"),
+ arguments(Http.Method.HEAD, head, "/head"),
+ arguments(Http.Method.OPTIONS, options, "/options"),
+ arguments(Http.Method.TRACE, trace, "/trace"),
+ arguments(Http.Method.PATCH, patch, "/patch")
+ );
+ }
+
+ private static class FakeHttpRoutingBuilder implements HttpRouting.Builder {
+ private Http.Method method;
+ private String pathPattern;
+ private Handler handler;
+
+ public Http.Method getMethod() {
+ return method;
+ }
+
+ public String getPathPattern() {
+ return pathPattern;
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+
+ @Override
+ public HttpRouting.Builder register(Supplier extends HttpService>... service) {
+ return null;
+ }
+
+ @Override
+ public HttpRouting.Builder register(String path, Supplier extends HttpService>... service) {
+ return null;
+ }
+
+ @Override
+ public HttpRouting.Builder route(HttpRoute route) {
+ return null;
+ }
+
+ @Override
+ public HttpRouting.Builder addFilter(Filter filter) {
+ return null;
+ }
+
+ @Override
+ public HttpRouting.Builder addFeature(Supplier extends HttpFeature> feature) {
+ return null;
+ }
+
+ @Override
+ public HttpRouting.Builder error(Class exceptionClass, ErrorHandler super T> handler) {
+ return null;
+ }
+
+ @Override
+ public HttpRouting.Builder maxReRouteCount(int maxReRouteCount) {
+ return null;
+ }
+
+ @Override
+ public HttpRouting.Builder security(HttpSecurity security) {
+ return null;
+ }
+
+ @Override
+ public HttpRouting build() {
+ return null;
+ }
+
+ @Override
+ public HttpRouting.Builder route(Http.Method method, String pathPattern, Handler handler) {
+ this.method = method;
+ this.pathPattern = pathPattern;
+ this.handler = handler;
+ return route(HttpRoute.builder().methods(method).path(pathPattern).handler(handler));
+ }
+
+ @Override
+ public HttpRouting.Builder route(Http.Method method, Handler handler) {
+ this.method = method;
+ this.pathPattern = null;
+ this.handler = handler;
+ return route(HttpRoute.builder().methods(method).handler(handler));
+ }
+ }
+}
diff --git a/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http/HttpRulesTest.java b/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http/HttpRulesTest.java
new file mode 100644
index 00000000000..a3efb768c2f
--- /dev/null
+++ b/nima/webserver/webserver/src/test/java/io/helidon/nima/webserver/http/HttpRulesTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.helidon.nima.webserver.http;
+
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import io.helidon.common.http.Http;
+import io.helidon.common.http.PathMatchers;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class HttpRulesTest {
+ private static final Handler handler = (req, res) -> res.send("done");
+
+ // Functions that will be used to execute Rule http method shortcuts
+ private static Function get = x ->
+ (FakeHttpRules) (x == null ? new FakeHttpRules().get(handler) : new FakeHttpRules().get(x, handler));
+ private static Function post = x ->
+ (FakeHttpRules) (x == null ? new FakeHttpRules().post(handler) : new FakeHttpRules().post(x, handler));
+ private static Function put = x ->
+ (FakeHttpRules) (x == null ? new FakeHttpRules().put(handler) : new FakeHttpRules().put(x, handler));
+ private static Function delete = x ->
+ (FakeHttpRules) (x == null ? new FakeHttpRules().delete(handler) : new FakeHttpRules().delete(x, handler));
+ private static Function head = x ->
+ (FakeHttpRules) (x == null ? new FakeHttpRules().head(handler) : new FakeHttpRules().head(x, handler));
+ private static Function options = x ->
+ (FakeHttpRules) (x == null ? new FakeHttpRules().options(handler) : new FakeHttpRules().options(x, handler));
+ private static Function trace = x ->
+ (FakeHttpRules) (x == null ? new FakeHttpRules().trace(handler) : new FakeHttpRules().trace(x, handler));
+ private static Function patch = x ->
+ (FakeHttpRules) (x == null ? new FakeHttpRules().patch(handler) : new FakeHttpRules().patch(x, handler));
+
+ @ParameterizedTest
+ @MethodSource("httpMethodShortcut")
+ void testHttpMethodShortcut(Http.Method method,
+ Function request) {
+ FakeHttpRules rule = request.apply(null);
+ assertThat(rule.getMethod(), is(method));
+ assertThat(rule.getPathPattern(), is(nullValue()));
+ assertThat(rule.getHandler(), is(handler));
+ }
+
+ @ParameterizedTest
+ @MethodSource("httpMethodShortcutWithPathPattern")
+ void testHttpMethodShortcutWithPathPattern(Http.Method method,
+ Function request,
+ String pathPattern) {
+ FakeHttpRules rule = request.apply(pathPattern);
+ assertThat(rule.getMethod(), is(method));
+ assertThat(rule.getPathPattern(), is(pathPattern));
+ assertThat(rule.getHandler(), is(handler));
+
+ }
+
+ private static Stream httpMethodShortcut() {
+ return Stream.of(
+ arguments(Http.Method.GET, get),
+ arguments(Http.Method.POST, post),
+ arguments(Http.Method.PUT, put),
+ arguments(Http.Method.DELETE, delete),
+ arguments(Http.Method.HEAD, head),
+ arguments(Http.Method.OPTIONS, options),
+ arguments(Http.Method.TRACE, trace),
+ arguments(Http.Method.PATCH, patch)
+ );
+ }
+
+ private static Stream httpMethodShortcutWithPathPattern() {
+ return Stream.of(
+ arguments(Http.Method.GET, get, "/get"),
+ arguments(Http.Method.POST, post, "/post"),
+ arguments(Http.Method.PUT, put, "/put"),
+ arguments(Http.Method.DELETE, delete, "/delete"),
+ arguments(Http.Method.HEAD, head, "/head"),
+ arguments(Http.Method.OPTIONS, options, "/options"),
+ arguments(Http.Method.TRACE, trace, "/trace"),
+ arguments(Http.Method.PATCH, patch, "/patch")
+ );
+ }
+
+ private static class FakeHttpRules implements HttpRules {
+ private Http.Method method;
+ private String pathPattern;
+ private Handler handler;
+
+ public Http.Method getMethod() {
+ return method;
+ }
+
+ public String getPathPattern() {
+ return pathPattern;
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+
+ @Override
+ public HttpRules register(Supplier extends HttpService>... service) {
+ return null;
+ }
+
+ @Override
+ public HttpRules register(String pathPattern, Supplier extends HttpService>... service) {
+ return null;
+ }
+
+ @Override
+ public HttpRules route(HttpRoute route) {
+ return null;
+ }
+
+ @Override
+ public HttpRules route(Http.Method method, String pathPattern, Handler handler) {
+ this.method = method;
+ this.pathPattern = pathPattern;
+ this.handler = handler;
+ return route(Http.Method.predicate(method), PathMatchers.create(pathPattern), handler);
+ }
+
+ @Override
+ public HttpRules route(Http.Method method, Handler handler) {
+ this.method = method;
+ this.pathPattern = null;
+ this.handler = handler;
+ return route(HttpRoute.builder()
+ .methods(method)
+ .handler(handler));
+ }
+ }
+
+}