resources) {
@Override
public String toString() {
- return "classes: "
+ return "main: "
+ classes.stream().map(Path::toString).collect(joining(File.pathSeparator))
+ "\nresources: "
+ resources.stream().map(Path::toString).collect(joining(File.pathSeparator))
diff --git a/modules/jooby-run/src/main/java/io/jooby/internal/run/JoobyMultiModuleFinder.java b/modules/jooby-run/src/main/java/io/jooby/internal/run/JoobyMultiModuleFinder.java
index 64fb89378a..cfe643e832 100644
--- a/modules/jooby-run/src/main/java/io/jooby/internal/run/JoobyMultiModuleFinder.java
+++ b/modules/jooby-run/src/main/java/io/jooby/internal/run/JoobyMultiModuleFinder.java
@@ -16,7 +16,11 @@
/**
* The new class loader since 3.x. It creates 3 modules with their own classloader:
*
- * - classes: project classes - resources: project resources - jars: project dependencies
+ *
+ * - classes: project classes
+ *
- resources: project resources
+ *
- jars: project dependencies
+ *
*
* This approach reduce memory footprint allowing fast restart.
*/
diff --git a/modules/jooby-rxjava3/pom.xml b/modules/jooby-rxjava3/pom.xml
index 32c9bada8a..9394ba6e85 100644
--- a/modules/jooby-rxjava3/pom.xml
+++ b/modules/jooby-rxjava3/pom.xml
@@ -6,7 +6,7 @@
io.jooby
modules
- 3.11.1-SNAPSHOT
+ 3.11.7
jooby-rxjava3
jooby-rxjava3
diff --git a/modules/jooby-stork/pom.xml b/modules/jooby-stork/pom.xml
index a3fcf66462..c27d00637d 100644
--- a/modules/jooby-stork/pom.xml
+++ b/modules/jooby-stork/pom.xml
@@ -4,7 +4,7 @@
io.jooby
modules
- 3.11.1-SNAPSHOT
+ 3.11.7
jooby-stork
diff --git a/modules/jooby-swagger-ui/pom.xml b/modules/jooby-swagger-ui/pom.xml
index 7003752dd8..8c305c6ffa 100644
--- a/modules/jooby-swagger-ui/pom.xml
+++ b/modules/jooby-swagger-ui/pom.xml
@@ -6,7 +6,7 @@
io.jooby
modules
- 3.11.1-SNAPSHOT
+ 3.11.7
jooby-swagger-ui
jooby-swagger-ui
diff --git a/modules/jooby-test/pom.xml b/modules/jooby-test/pom.xml
index 002873c27c..9ae4eafc41 100644
--- a/modules/jooby-test/pom.xml
+++ b/modules/jooby-test/pom.xml
@@ -6,7 +6,7 @@
io.jooby
modules
- 3.11.1-SNAPSHOT
+ 3.11.7
jooby-test
jooby-test
diff --git a/modules/jooby-thymeleaf/pom.xml b/modules/jooby-thymeleaf/pom.xml
index ee23d72da4..2100f2937d 100644
--- a/modules/jooby-thymeleaf/pom.xml
+++ b/modules/jooby-thymeleaf/pom.xml
@@ -6,7 +6,7 @@
io.jooby
modules
- 3.11.1-SNAPSHOT
+ 3.11.7
jooby-thymeleaf
jooby-thymeleaf
diff --git a/modules/jooby-undertow/pom.xml b/modules/jooby-undertow/pom.xml
index 4e62f9126d..a635673204 100644
--- a/modules/jooby-undertow/pom.xml
+++ b/modules/jooby-undertow/pom.xml
@@ -6,7 +6,7 @@
io.jooby
modules
- 3.11.1-SNAPSHOT
+ 3.11.7
jooby-undertow
jooby-undertow
diff --git a/modules/jooby-undertow/src/main/java/io/jooby/internal/undertow/UndertowHandler.java b/modules/jooby-undertow/src/main/java/io/jooby/internal/undertow/UndertowHandler.java
index 677e91a063..88463181d4 100644
--- a/modules/jooby-undertow/src/main/java/io/jooby/internal/undertow/UndertowHandler.java
+++ b/modules/jooby-undertow/src/main/java/io/jooby/internal/undertow/UndertowHandler.java
@@ -7,6 +7,7 @@
import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Optional;
import io.jooby.*;
import io.undertow.io.Receiver;
@@ -18,6 +19,7 @@
import io.undertow.server.handlers.form.MultiPartParserDefinition;
import io.undertow.util.HeaderMap;
import io.undertow.util.Headers;
+import io.undertow.util.ParameterLimitException;
public class UndertowHandler implements HttpHandler {
protected final List applications;
@@ -92,7 +94,11 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
try {
parser.parse(execute(router, context));
} catch (Exception x) {
- context.sendError(x, StatusCode.BAD_REQUEST);
+ var cause = Optional.ofNullable(x.getCause()).orElse(x);
+ if (cause instanceof ParameterLimitException) {
+ context.setAttribute("__too_many_fields", cause);
+ }
+ router.match(context).execute(context, Route.FORM_DECODER_HANDLER);
}
}
} else {
diff --git a/modules/jooby-undertow/src/main/java/io/jooby/internal/undertow/UndertowWebSocket.java b/modules/jooby-undertow/src/main/java/io/jooby/internal/undertow/UndertowWebSocket.java
index 22b06de829..f9e8285a12 100644
--- a/modules/jooby-undertow/src/main/java/io/jooby/internal/undertow/UndertowWebSocket.java
+++ b/modules/jooby-undertow/src/main/java/io/jooby/internal/undertow/UndertowWebSocket.java
@@ -325,9 +325,7 @@ void fireConnect() {
conf.hasPath("websocket.idleTimeout")
? conf.getDuration("websocket.idleTimeout", TimeUnit.MILLISECONDS)
: TimeUnit.MINUTES.toMillis(5);
- if (timeout > 0) {
- channel.setIdleTimeout(timeout);
- }
+ channel.setIdleTimeout(timeout);
if (onConnectCallback != null) {
dispatch(webSocketTask(() -> onConnectCallback.onConnect(this), true));
} else {
diff --git a/modules/jooby-undertow/src/main/java/io/jooby/undertow/UndertowServer.java b/modules/jooby-undertow/src/main/java/io/jooby/undertow/UndertowServer.java
index db3b2bfe5e..649cc9d413 100644
--- a/modules/jooby-undertow/src/main/java/io/jooby/undertow/UndertowServer.java
+++ b/modules/jooby-undertow/src/main/java/io/jooby/undertow/UndertowServer.java
@@ -64,13 +64,13 @@ protected ServerOptions defaultOptions() {
return new ServerOptions().setIoThreads(ServerOptions.IO_THREADS).setServer("utow");
}
- @NonNull @Override
+ @Override
public String getName() {
return "undertow";
}
@Override
- public @NonNull Server start(@NonNull Jooby... application) {
+ public Server start(@NonNull Jooby... application) {
// force options to be non-null
var options = getOptions();
var portInUse = options.getPort();
@@ -114,7 +114,7 @@ public String getName() {
.addAll(OptionMap.create(Options.WORKER_NAME, "worker"))
.getMap());
- Undertow.Builder builder =
+ var builder =
Undertow.builder()
.setBufferSize(options.getBufferSize())
/** Socket : */
@@ -123,6 +123,7 @@ public String getName() {
// HTTP/1.1 is keep-alive by default, turn this option off
.setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false)
.setServerOption(UndertowOptions.MAX_HEADER_SIZE, options.getMaxHeaderSize())
+ .setServerOption(UndertowOptions.MAX_PARAMETERS, options.getMaxFormFields())
.setServerOption(UndertowOptions.ALLOW_EQUALS_IN_COOKIE_VALUE, true)
.setServerOption(UndertowOptions.ALWAYS_SET_DATE, options.getDefaultHeaders())
.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, false)
diff --git a/modules/jooby-whoops/pom.xml b/modules/jooby-whoops/pom.xml
index ddc3b64fcd..2ef9f6dec4 100644
--- a/modules/jooby-whoops/pom.xml
+++ b/modules/jooby-whoops/pom.xml
@@ -6,7 +6,7 @@
io.jooby
modules
- 3.11.1-SNAPSHOT
+ 3.11.7
jooby-whoops
jooby-whoops
diff --git a/modules/jooby-yasson/pom.xml b/modules/jooby-yasson/pom.xml
index cef4e26d2f..370c7c5f28 100644
--- a/modules/jooby-yasson/pom.xml
+++ b/modules/jooby-yasson/pom.xml
@@ -6,7 +6,7 @@
io.jooby
modules
- 3.11.1-SNAPSHOT
+ 3.11.7
jooby-yasson
jooby-yasson
diff --git a/modules/pom.xml b/modules/pom.xml
index 06c2b68c72..abc2bf2947 100644
--- a/modules/pom.xml
+++ b/modules/pom.xml
@@ -4,7 +4,7 @@
io.jooby
jooby-project
- 3.11.1-SNAPSHOT
+ 3.11.7
modules
diff --git a/pom.xml b/pom.xml
index f79b83b8fd..ad2ea48623 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.jooby
jooby-project
- 3.11.1-SNAPSHOT
+ 3.11.7
pom
jooby-project
@@ -58,11 +58,11 @@
2.3.34
- 4.4.0
+ 4.5.0
1.3.7
3.2.4
- 2.19.2
- 2.13.1
+ 2.21.1
+ 2.13.2
3.0.1
3.0.4
2.2.1
@@ -70,76 +70,76 @@
3.2.1
- 6.3.0
+ 6.3.3
1.2
- 6.6.22.Final
+ 6.6.33.Final
15.11.0
- 3.49.5
- 11.8.2
+ 3.51.0
+ 11.15.0
24.1
6.7.1.RELEASE
2.12.1
- 4.0.0
- 3.2.2
+ 4.1.0
+ 3.2.3
- 1.4.4
+ 1.4.6
7.0.0
- 1.5.18
- 2.25.1
+ 1.5.32
+ 2.25.3
2.0.17
1.6.0
- 4.2.33
+ 4.2.38
- 2.1.6.Final
+ 2.2.0.Final
- 9.8
+ 9.9.1
- 2.3.18.Final
- 12.0.23
- 4.2.3.Final
+ 2.3.23.Final
+ 12.1.7
+ 4.2.10.Final
- 2.2.34
- 2.1.31
+ 2.2.45
+ 2.1.39
2.0.0-rc.20
2.5.2
- 11.5
- 3.5
- 2.12
+ 11.6
+ 3.1
+ 2.17
2.0.1.MR
3.1.1
4.0.0
- 4.9.3
+ 4.9.8
- 5.1.0
- 0.12.6
- 6.1.3
- 2.5.0
+ 5.3.2
+ 0.13.0
+ 6.3.3
+ 2.5.1
9.2.1
8.0.1
- 1.12.788
- 4.11.0
+ 1.12.797
+ 4.15.0
1.9.3
- 2.20.0
+ 2.21.0
2.2.0
@@ -147,15 +147,15 @@
true
- 0.8.13
- 5.13.3
- 5.5.5
+ 0.8.14
+ 6.0.0
+ 5.5.6
3.27.3
- 5.18.0
- 33.4.8-jre
+ 5.20.0
+ 33.5.0-jre
- 0.21.0
- 1.4.4
+ 0.23.0
+ 1.4.5
2.6
@@ -207,7 +207,7 @@
false
yyyy-MM-dd HH:mm:ssa
- 2025-07-21T10:12:15Z
+ 2026-03-10T16:43:31Z
UTF-8
etc${file.separator}source${file.separator}formatter.sh
diff --git a/tests/pom.xml b/tests/pom.xml
index a1d238b386..2dd0bb8224 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -6,7 +6,7 @@
io.jooby
jooby-project
- 3.11.1-SNAPSHOT
+ 3.11.7
tests
tests
diff --git a/tests/src/test/java/io/jooby/i2806/Issue2806.java b/tests/src/test/java/io/jooby/i2806/Issue2806.java
index 3b5ff321e5..208af9a784 100644
--- a/tests/src/test/java/io/jooby/i2806/Issue2806.java
+++ b/tests/src/test/java/io/jooby/i2806/Issue2806.java
@@ -5,9 +5,9 @@
*/
package io.jooby.i2806;
+import static io.jooby.test.TestUtil._19kb;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import java.util.Arrays;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
@@ -22,9 +22,6 @@ public class Issue2806 {
@ServerTest
public void renderShouldWorkFromErrorHandlerWhenLargeRequestAreSent(ServerTestRunner runner) {
- char[] chars = new char[19 * 1024];
- Arrays.fill(chars, 'S');
- String _19kb = new String(chars);
runner
.define(
app -> {
@@ -43,20 +40,32 @@ public void renderShouldWorkFromErrorHandlerWhenLargeRequestAreSent(ServerTestRu
ctx.render(map);
});
- app.post("/2806", ctx -> ctx.body().value(""));
+ app.post(
+ "/2806",
+ ctx -> {
+ return ctx.body().value("");
+ });
app.get("/2806", ctx -> ctx.body().value(""));
})
.ready(
client -> {
// Exceeds
- client.post(
- "/2806",
- RequestBody.create(_19kb, MediaType.get("text/plain")),
- rsp -> {
- assertEquals(413, rsp.code());
- assertEquals("{\"router\":true,\"route\":true}", rsp.body().string());
- });
+ client
+ .post("/2806", RequestBody.create(_19kb, MediaType.get("text/plain")))
+ .execute(
+ rsp -> {
+ assertEquals(413, rsp.code());
+ assertEquals("{\"router\":true,\"route\":true}", rsp.body().string());
+ });
+
+ client
+ .get("/2806")
+ .execute(
+ rsp -> {
+ assertEquals(200, rsp.code());
+ assertEquals("", rsp.body().string());
+ });
});
}
}
diff --git a/tests/src/test/java/io/jooby/i3756/C3756.java b/tests/src/test/java/io/jooby/i3756/C3756.java
new file mode 100644
index 0000000000..47125737e9
--- /dev/null
+++ b/tests/src/test/java/io/jooby/i3756/C3756.java
@@ -0,0 +1,25 @@
+/*
+ * Jooby https://jooby.io
+ * Apache License Version 2.0 https://jooby.io/LICENSE.txt
+ * Copyright 2014 Edgar Espina
+ */
+package io.jooby.i3756;
+
+import io.jooby.annotation.GET;
+import io.jooby.annotation.Path;
+
+@Path("/C3756")
+public class C3756 {
+ private final S3756 s3756;
+
+ public C3756(S3756 s3756) {
+ super();
+ this.s3756 = s3756;
+ }
+
+ @GET
+ public String handle() {
+ s3756.accept("hello");
+ return "hello";
+ }
+}
diff --git a/tests/src/test/java/io/jooby/i3756/S3756.java b/tests/src/test/java/io/jooby/i3756/S3756.java
new file mode 100644
index 0000000000..2afbf013b5
--- /dev/null
+++ b/tests/src/test/java/io/jooby/i3756/S3756.java
@@ -0,0 +1,11 @@
+/*
+ * Jooby https://jooby.io
+ * Apache License Version 2.0 https://jooby.io/LICENSE.txt
+ * Copyright 2014 Edgar Espina
+ */
+package io.jooby.i3756;
+
+public interface S3756 {
+
+ void accept(String s);
+}
diff --git a/tests/src/test/java/io/jooby/i3783/Issue3783.java b/tests/src/test/java/io/jooby/i3783/Issue3783.java
new file mode 100644
index 0000000000..282df458bf
--- /dev/null
+++ b/tests/src/test/java/io/jooby/i3783/Issue3783.java
@@ -0,0 +1,45 @@
+/*
+ * Jooby https://jooby.io
+ * Apache License Version 2.0 https://jooby.io/LICENSE.txt
+ * Copyright 2014 Edgar Espina
+ */
+package io.jooby.i3783;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import io.jooby.ServerOptions;
+import io.jooby.junit.ServerTest;
+import io.jooby.junit.ServerTestRunner;
+import okhttp3.FormBody;
+
+public class Issue3783 {
+
+ @ServerTest
+ public void shouldAllowToSetMaxFormFields(ServerTestRunner runner) {
+ runner
+ .define(
+ app -> {
+ app.setServerOptions(new ServerOptions().setMaxFormFields(2));
+ app.error((ctx, cause, code) -> ctx.send(cause.getMessage()));
+ app.post(
+ "/3723",
+ ctx -> {
+ return ctx.form("f").toList();
+ });
+ })
+ .ready(
+ http -> {
+ http.post(
+ "/3723",
+ new FormBody.Builder()
+ .add("f1", "value 1")
+ .add("f2", "value 2")
+ .add("f3", "value 3")
+ .build(),
+ rsp -> {
+ assertEquals(400, rsp.code());
+ assertEquals("Too many form fields", rsp.body().string());
+ });
+ });
+ }
+}
diff --git a/tests/src/test/java/io/jooby/test/Http2Test.java b/tests/src/test/java/io/jooby/test/Http2Test.java
index 3e7293aac1..851f8ea960 100644
--- a/tests/src/test/java/io/jooby/test/Http2Test.java
+++ b/tests/src/test/java/io/jooby/test/Http2Test.java
@@ -131,7 +131,7 @@ public void onAccept(Session session) {}
Response.Builder builder = new Response.Builder();
session.newStream(
frame,
- new Promise.Adapter<>(),
+ Promise.noop(),
new Stream.Listener() {
@Override
public void onHeaders(final Stream stream, final HeadersFrame frame) {
diff --git a/tests/src/test/java/io/jooby/test/WebClient.java b/tests/src/test/java/io/jooby/test/WebClient.java
index 93d622b98e..93607e41ed 100644
--- a/tests/src/test/java/io/jooby/test/WebClient.java
+++ b/tests/src/test/java/io/jooby/test/WebClient.java
@@ -14,10 +14,7 @@
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -142,17 +139,46 @@ public void close() {
public class Request {
private final okhttp3.Request.Builder req;
+ private SneakyThrows.Consumer configurer;
public Request(okhttp3.Request.Builder req) {
this.req = req;
}
public Request prepare(SneakyThrows.Consumer configurer) {
- configurer.accept(req);
+ this.configurer = configurer;
return this;
}
public void execute(SneakyThrows.Consumer callback) {
+ execute(1, callback);
+ }
+
+ public void execute(int concurrency, SneakyThrows.Consumer callback) {
+ if (configurer != null) {
+ configurer.accept(req);
+ }
+ if (concurrency > 1) {
+ var futures = new ArrayList>();
+ for (var i = 0; i < concurrency; i++) {
+ futures.add(
+ CompletableFuture.supplyAsync(
+ () -> {
+ executeCall(callback);
+ return "success";
+ }));
+ try {
+ CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+ } catch (CompletionException x) {
+ throw SneakyThrows.propagate(x.getCause());
+ }
+ }
+ } else {
+ executeCall(callback);
+ }
+ }
+
+ private void executeCall(SneakyThrows.Consumer callback) {
okhttp3.Request r = req.build();
try (Response rsp = client.newCall(r).execute()) {
callback.accept(rsp);