diff --git a/avaje-jex/src/main/java/io/avaje/jex/Jex.java b/avaje-jex/src/main/java/io/avaje/jex/Jex.java index b405178b..5aee33bc 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/Jex.java +++ b/avaje-jex/src/main/java/io/avaje/jex/Jex.java @@ -104,7 +104,7 @@ default Jex post(String path, ExchangeHandler handler, Role... roles) { * @param roles An array of roles that are associated with this endpoint. */ default Jex put(String path, ExchangeHandler handler, Role... roles) { - routing().get(path, handler, roles); + routing().put(path, handler, roles); return this; } diff --git a/avaje-jex/src/main/java/io/avaje/jex/routes/PathParser.java b/avaje-jex/src/main/java/io/avaje/jex/routes/PathParser.java index e70402b3..c60d0b71 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/routes/PathParser.java +++ b/avaje-jex/src/main/java/io/avaje/jex/routes/PathParser.java @@ -16,7 +16,7 @@ final class PathParser { PathParser(String path, boolean ignoreTrailingSlashes) { this.rawPath = path; - final RegBuilder regBuilder = new RegBuilder(); + final RegBuilder regBuilder = new RegBuilder(ignoreTrailingSlashes); for (String rawSeg : path.split("/")) { if (!rawSeg.isEmpty()) { segmentCount++; diff --git a/avaje-jex/src/main/java/io/avaje/jex/routes/RegBuilder.java b/avaje-jex/src/main/java/io/avaje/jex/routes/RegBuilder.java index 1c97de19..7501352c 100644 --- a/avaje-jex/src/main/java/io/avaje/jex/routes/RegBuilder.java +++ b/avaje-jex/src/main/java/io/avaje/jex/routes/RegBuilder.java @@ -10,10 +10,15 @@ final class RegBuilder { private final StringJoiner full = new StringJoiner("/"); private final StringJoiner extract = new StringJoiner("/"); + private final boolean ignoreTrailingSlashes; private boolean trailingSlash; private boolean multiSlash; private boolean literal = true; + public RegBuilder(boolean ignoreTrailingSlashes) { + this.ignoreTrailingSlashes = ignoreTrailingSlashes; + } + void add(PathSegment pathSegment, List paramNames) { full.add(pathSegment.asRegexString(false)); extract.add(pathSegment.asRegexString(true)); @@ -48,6 +53,11 @@ private String wrap(String parts) { if (trailingSlash) { parts += "\\/"; } + + if (!ignoreTrailingSlashes) { + return "^/" + parts; + } + return "^/" + parts + "/?$"; } diff --git a/avaje-jex/src/test/java/io/avaje/jex/ShutDownTest.java b/avaje-jex/src/test/java/io/avaje/jex/ShutDownTest.java new file mode 100644 index 00000000..488ecba0 --- /dev/null +++ b/avaje-jex/src/test/java/io/avaje/jex/ShutDownTest.java @@ -0,0 +1,25 @@ +package io.avaje.jex; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +class ShutDownTest { + + @Test + void shutDownHooks() { + + List results = new ArrayList<>(); + var jex = Jex.create().config(c -> c.socketBacklog(0)); + jex.lifecycle().onShutdown(() -> results.add("onShut")); + jex.lifecycle().registerShutdownHook(() -> results.add("onHook")); + var server = jex.start(); + + server.onShutdown(() -> results.add("serverShut")); + server.shutdown(); + assertThat(results).hasSize(2); // 2 because jvm shutdown won't run in a junit + } +} diff --git a/avaje-jex/src/test/java/io/avaje/jex/compression/CompressionTest.java b/avaje-jex/src/test/java/io/avaje/jex/compression/CompressionTest.java index 4091e88b..c7f320d4 100644 --- a/avaje-jex/src/test/java/io/avaje/jex/compression/CompressionTest.java +++ b/avaje-jex/src/test/java/io/avaje/jex/compression/CompressionTest.java @@ -13,6 +13,7 @@ import io.avaje.jex.Jex; import io.avaje.jex.core.Constants; import io.avaje.jex.core.TestPair; +import io.avaje.jex.http.ContentType; class CompressionTest { @@ -27,7 +28,8 @@ static TestPair init() { r.get( "/compress", ctx -> - ctx.write(CompressionTest.class.getResourceAsStream("/64KB.json"))) + ctx.contentType(ContentType.APPLICATION_JSON) + .write(CompressionTest.class.getResourceAsStream("/64KB.json"))) .get( "/sus", ctx -> diff --git a/avaje-jex/src/test/java/io/avaje/jex/http/TrailingSlashTest.java b/avaje-jex/src/test/java/io/avaje/jex/http/TrailingSlashTest.java new file mode 100644 index 00000000..260d495f --- /dev/null +++ b/avaje-jex/src/test/java/io/avaje/jex/http/TrailingSlashTest.java @@ -0,0 +1,42 @@ +package io.avaje.jex.http; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.http.HttpResponse; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +import io.avaje.jex.Jex; +import io.avaje.jex.core.TestPair; + +class TrailingSlashTest { + + static TestPair pair = init(); + + static TestPair init() { + final Jex app = + Jex.create() + .config(c -> c.socketBacklog(0).ignoreTrailingSlashes(false)) + .get("/slash", ctx -> {}); + + return TestPair.create(app); + } + + @AfterAll + static void end() { + pair.shutdown(); + } + + @Test + void get() { + HttpResponse res = pair.request().path("slash/").GET().asString(); + assertThat(res.statusCode()).isEqualTo(404); + } + + @Test + void getNoTrailing() { + HttpResponse res = pair.request().path("slash").GET().asString(); + assertThat(res.statusCode()).isEqualTo(204); + } +} diff --git a/avaje-jex/src/test/java/io/avaje/jex/routes/PathParserTest.java b/avaje-jex/src/test/java/io/avaje/jex/routes/PathParserTest.java index dcfaf889..b5ec772f 100644 --- a/avaje-jex/src/test/java/io/avaje/jex/routes/PathParserTest.java +++ b/avaje-jex/src/test/java/io/avaje/jex/routes/PathParserTest.java @@ -21,7 +21,7 @@ void matches_trailingSlash_honor() { assertTrue(pathParser.matches("/one/1/")); assertTrue(pathParser.matches("/one/2/")); - assertTrue(pathParser.matches("/one/3//")); // accepts trailing double slash? + assertFalse(pathParser.matches("/one/3//")); // accepts trailing double slash? assertFalse(pathParser.matches("/one/3///")); // but not triple slash? assertFalse(pathParser.matches("/one/1")); assertFalse(pathParser.matches("/one/2"));