diff --git a/avaje-jex/src/main/java/io/avaje/jex/DefaultRouting.java b/avaje-jex/src/main/java/io/avaje/jex/DefaultRouting.java
index 94bf2c9f..2cf07e2f 100644
--- a/avaje-jex/src/main/java/io/avaje/jex/DefaultRouting.java
+++ b/avaje-jex/src/main/java/io/avaje/jex/DefaultRouting.java
@@ -7,6 +7,7 @@
import java.util.Deque;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import io.avaje.jex.security.Role;
@@ -179,7 +180,6 @@ public Routing filter(HttpFilter handler) {
return this;
}
-
private static class Entry implements Routing.Entry {
private final Type type;
diff --git a/avaje-jex/src/main/java/io/avaje/jex/FilterChain.java b/avaje-jex/src/main/java/io/avaje/jex/FilterChain.java
index b5966196..033f6aaf 100644
--- a/avaje-jex/src/main/java/io/avaje/jex/FilterChain.java
+++ b/avaje-jex/src/main/java/io/avaje/jex/FilterChain.java
@@ -8,7 +8,7 @@
public interface FilterChain {
/**
- * Calls the next filter in the chain, or else the users exchange handler, if this is the final
+ * Calls the next filter in the chain, or else the user's exchange handler, if this is the final
* filter in the chain. The {@link HttpFilter} may decide to terminate the chain, by not
* calling this method. In this case, the filter must send the response to the request,
* because the application's {@linkplain HttpExchange exchange} handler will not be invoked.
diff --git a/avaje-jex/src/main/java/io/avaje/jex/Handler.java b/avaje-jex/src/main/java/io/avaje/jex/Handler.java
index e23816d2..aacbf15f 100644
--- a/avaje-jex/src/main/java/io/avaje/jex/Handler.java
+++ b/avaje-jex/src/main/java/io/avaje/jex/Handler.java
@@ -1,7 +1,18 @@
package io.avaje.jex;
+/**
+ * A handler which is invoked to process HTTP exchanges. Each HTTP exchange is handled by one of
+ * these handlers.
+ */
@FunctionalInterface
public interface Handler {
+ /**
+ * Handle the given request and generate an appropriate response. See {@link Context} for a
+ * description of the steps involved in handling an exchange.
+ *
+ * @param ctx the request context containing the request from the client and used to send the
+ * response
+ */
void handle(Context ctx);
}
diff --git a/avaje-jex/src/main/java/io/avaje/jex/Routing.java b/avaje-jex/src/main/java/io/avaje/jex/Routing.java
index c2096e8f..c36022f6 100644
--- a/avaje-jex/src/main/java/io/avaje/jex/Routing.java
+++ b/avaje-jex/src/main/java/io/avaje/jex/Routing.java
@@ -3,6 +3,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import io.avaje.jex.security.Role;
@@ -128,6 +129,26 @@ public interface Routing {
*/
Routing filter(HttpFilter handler);
+ /** Add a preprocessing filter for all requests. */
+ default Routing before(Consumer handler) {
+
+ return filter(
+ (ctx, chain) -> {
+ handler.accept(ctx);
+ chain.proceed();
+ });
+ }
+
+ /** Add a post-processing filter for all requests. */
+ default Routing after(Consumer handler) {
+
+ return filter(
+ (ctx, chain) -> {
+ chain.proceed();
+ handler.accept(ctx);
+ });
+ }
+
/**
* Return all the registered handlers.
*/
diff --git a/avaje-jex/src/main/java/io/avaje/jex/jdk/RoutingFilter.java b/avaje-jex/src/main/java/io/avaje/jex/jdk/RoutingFilter.java
index f78fdbdd..32ecb31d 100644
--- a/avaje-jex/src/main/java/io/avaje/jex/jdk/RoutingFilter.java
+++ b/avaje-jex/src/main/java/io/avaje/jex/jdk/RoutingFilter.java
@@ -46,6 +46,7 @@ public void doFilter(HttpExchange exchange, Filter.Chain chain) {
handleException(ctx, e);
} finally {
routes.dec();
+ exchange.close();
}
} else {
route.inc();
@@ -63,6 +64,7 @@ public void doFilter(HttpExchange exchange, Filter.Chain chain) {
}
} finally {
route.dec();
+ exchange.close();
}
}
}
diff --git a/avaje-jex/src/test/java/io/avaje/jex/jdk/FilterTest.java b/avaje-jex/src/test/java/io/avaje/jex/jdk/FilterTest.java
index 7d9e4b03..a40afe17 100644
--- a/avaje-jex/src/test/java/io/avaje/jex/jdk/FilterTest.java
+++ b/avaje-jex/src/test/java/io/avaje/jex/jdk/FilterTest.java
@@ -26,11 +26,7 @@ static TestPair init() {
.get("/one", ctx -> ctx.text("one"))
.get("/two", ctx -> ctx.text("two"))
.get("/two/{id}", ctx -> ctx.text("two-id"))
- .filter(
- (ctx, chain) -> {
- ctx.header("before-all", "set");
- chain.proceed();
- })
+ .before(ctx -> ctx.header("before-all", "set"))
.filter(
(ctx, chain) -> {
if (ctx.url().contains("/two/")) {
@@ -38,11 +34,7 @@ static TestPair init() {
}
chain.proceed();
})
- .filter(
- (ctx, chain) -> {
- chain.proceed();
- afterAll.set("set");
- })
+ .after(ctx -> afterAll.set("set"))
.filter(
(ctx, chain) -> {
chain.proceed();