Repository: jooby-project/jooby
Pull request #2498 (2022-01-08 00:36:33)

Bump pac4j-core from 4.4.0 to 5.2.0 in /modules/jooby-bom
Bumps [pac4j-core](https://github.com/pac4j/pac4j) from 4.4.0 to 5.2.0.
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/pac4j/pac4j/commit/3d0e3cff83e1ec8b918b531225fb8e407810da49"><code>3d0e3cf</code></a> [maven-release-plugin] prepare release pac4j-parent-5.2.0</li>
<li><a href="https://github.com/pac4j/pac4j/commit/24479eda531e480dff63fd619e7e91d22b47d87b"><code>24479ed</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/pac4j/pac4j/issues/2092">#2092</a> from pac4j/renovate/httpcore.version</li>
<li><a href="https://github.com/pac4j/pac4j/commit/5e89ee0829d30deda375af2c9b03c8b12dd2fd6a"><code>5e89ee0</code></a> chore(deps): update dependency org.apache.httpcomponents:httpcore to v4.4.15</li>
<li><a href="https://github.com/pac4j/pac4j/commit/38b3921c4de86d2289e16acdec6d374705759948"><code>38b3921</code></a> chore(deps): update dependency com.unboundid:unboundid-ldapsdk to v6.0.3 (<a href="https://github-redirect.dependabot.com/pac4j/pac4j/issues/2091">#2091</a>)</li>
<li><a href="https://github.com/pac4j/pac4j/commit/90092f8d2e9459a296975b37ab78c93fc5b9c043"><code>90092f8</code></a> fix doc</li>
<li><a href="https://github.com/pac4j/pac4j/commit/22b82ffd702a132d9f09da60362fc6264fc281ae"><code>22b82ff</code></a> reinforce security on OIDC</li>
<li><a href="https://github.com/pac4j/pac4j/commit/d3c5f614b936db48570764506637be275430011a"><code>d3c5f61</code></a> ad blog entry: jee-pac4j vs pac4j-jee</li>
<li><a href="https://github.com/pac4j/pac4j/commit/f0f1c8cf28e9cf995740a8d2e722e7387c96e380"><code>f0f1c8c</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/pac4j/pac4j/issues/2089">#2089</a> from pac4j/renovate/com.github.tomakehurst-wiremock-...</li>
<li><a href="https://github.com/pac4j/pac4j/commit/dbafea50f2f0f282ac5c2bbda8a8beedc78ffe69"><code>dbafea5</code></a> chore(deps): update dependency com.github.tomakehurst:wiremock-jre8 to v2.32.0</li>
<li><a href="https://github.com/pac4j/pac4j/commit/cedec400be9f4130e18b776108962bb9053f95ca"><code>cedec40</code></a> Create the pac4j-jee dependency</li>
<li>Additional commits viewable in <a href="https://github.com/pac4j/pac4j/compare/pac4j-4.4.0...pac4j-parent-5.2.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=org.pac4j:pac4j-core&package-manager=maven&previous-version=4.4.0&new-version=5.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/jooby-project/jooby/network/alerts).

</details>


Patch:
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index 3ac0a4edd..000000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,1 +0,0 @@
-github: [jknack]
diff --git a/.github/workflows/full-build.yml b/.github/workflows/full-build.yml
index d93180b1c..a907b48f4 100644
--- a/.github/workflows/full-build.yml
+++ b/.github/workflows/full-build.yml
@@ -8,7 +8,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        java_version: [1.8, 11, 17, 18]
+        java_version: [1.8, 11, 13]
         os: [ubuntu-latest, windows-latest, macOS-latest]
 
     steps:
diff --git a/.github/workflows/quick-build.yml b/.github/workflows/quick-build.yml
index 93c009e5a..a7b0d6629 100644
--- a/.github/workflows/quick-build.yml
+++ b/.github/workflows/quick-build.yml
@@ -2,9 +2,6 @@ name: Quick Build
 
 on: [pull_request]
 
-permissions:
-  contents: read
-
 jobs:
   build:
 
@@ -12,7 +9,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        java_version: [1.8, 11, 17]
+        java_version: [1.8, 11, 13]
         os: [ubuntu-latest]
 
     steps:
diff --git a/.github/workflows/starter.yml b/.github/workflows/starter.yml
index 521489fed..cef8da0fb 100644
--- a/.github/workflows/starter.yml
+++ b/.github/workflows/starter.yml
@@ -8,9 +8,6 @@ on:
     paths:
       - 'starters/**'
 
-permissions:
-  contents: read
-
 jobs:
   build:
     runs-on: ${{ matrix.os }}
diff --git a/docs/asciidoc/dev-tools.adoc b/docs/asciidoc/dev-tools.adoc
index 4f5d18bef..b5719935a 100644
--- a/docs/asciidoc/dev-tools.adoc
+++ b/docs/asciidoc/dev-tools.adoc
@@ -109,7 +109,6 @@ The next example shows all the available options with their default values:
       <restartExtensions>conf,properties,class</restartExtensions> <2>
       <compileExtensions>java,kt</compileExtensions>               <3>
       <port>8080</port>                                            <4>
-      <waitTimeBeforeRestart>500</waitTimeBeforeRestart>           <5>
     </configuration>
   </plugin>
   ...
@@ -138,7 +137,6 @@ joobyRun {
   restartExtensions = ["conf", "properties", "class"]              <2>
   compileExtensions = ["java", "kt"]                               <3>
   port = 8080                                                      <4>
-  waitTimeBeforeRestart = 500                                      <5>
 }
 ----
 
@@ -146,5 +144,4 @@ joobyRun {
 <2> Restart extensions. A change on these files trigger a restart request.
 <3> Source extensions. A change on these files trigger a compilation request, followed by a restart request.
 <4> Application port
-<5> How long to wait after last file change to restart. Default is: `500` milliseconds.
 
diff --git a/docs/asciidoc/execution-model.adoc b/docs/asciidoc/execution-model.adoc
index 2ce07ead1..581156453 100644
--- a/docs/asciidoc/execution-model.adoc
+++ b/docs/asciidoc/execution-model.adoc
@@ -364,13 +364,13 @@ workerThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2) * 8
 For example `8` cores gives us `64` worker threads.
 
 - Undertow: The javadoc:utow.Utow[text=Undertow server] implementation multiply the number of available processors
-by 8.
+(with a minimum of 2) by 8.
 
 ----
-workerThreads = Runtime.getRuntime().availableProcessors() * 8
+workerThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2) * 8
 ----
 
-For `8` cores gives us `64` worker threads.
+For example `8` cores gives us `64` worker threads.
 
 - Jetty: The javadoc:jetty.Jetty[text=Jetty server] implementation uses the default configuration
 with `200` worker threads.
diff --git a/docs/asciidoc/modules/openapi.adoc b/docs/asciidoc/modules/openapi.adoc
index 11e6e551c..4a8468af5 100644
--- a/docs/asciidoc/modules/openapi.adoc
+++ b/docs/asciidoc/modules/openapi.adoc
@@ -393,7 +393,7 @@ and/or `@Operation` annotations.
 
 ==== Responses & Status
 
-The default response code is `Success(200)` (or `NO_CONTENT(204)` for DELETE mvc routes). Now, if
+The default response code is `Success(200)` (or `NO_CONENT(204)` for DELETE mvc routes). Now, if
 you need to:
 
 - document the default response
diff --git a/docs/asciidoc/servers.adoc b/docs/asciidoc/servers.adoc
index 29af8eeb8..772f54b58 100644
--- a/docs/asciidoc/servers.adoc
+++ b/docs/asciidoc/servers.adoc
@@ -48,7 +48,6 @@ Server options are available via javadoc:ServerOptions[] class:
       .setMaxRequestSize(10485760)
       .setSecurePort(8433)
       .setSsl(SslOptions.selfSigned())
-      .setHttpsOnly(false)
       .setHttp2(true)
       .setExpectContinue(true)
   ); 
@@ -71,9 +70,8 @@ Server options are available via javadoc:ServerOptions[] class:
     maxRequestSize = 10485760
     securePort = 8443
     ssl = SslOptions.selfSigned()
-    isHttpsOnly = true
-    isHttp2 = true
-    isExpectContinue = true
+    http2 = true
+    expectContinue = true
   }
 }
 ----
@@ -89,9 +87,8 @@ Server options are available via javadoc:ServerOptions[] class:
 - maxRequestSize: Maximum request size in bytes. Request exceeding this value results in 413(REQUEST_ENTITY_TOO_LARGE) response. Default is `10mb`.
 - securePort: Enable HTTPS. This option is fully covered in next section.
 - ssl: SSL options with certificate details. This option is fully covered in next section.
-- isHttpsOnly: bind only to HTTPS port, not HTTP. This requires SSL options to be configured.
-- isHttp2: Enable HTTP 2.0.
-- isExpectContinue: Whenever 100-Expect and continue requests are handled by the server. This is off
+- http2: Enable HTTP 2.0.
+- expectContinue: Whenever 100-Expect and continue requests are handled by the server. This is off
   by default, except for Jetty which is always ON.
 
 Server options are available as application configuration properties too:
@@ -110,7 +107,6 @@ server.defaultHeaders = true
 server.maxRequestSize = 10485760
 server.securePort = 8443
 server.ssl.type = self-signed | PKCS12 | X509
-server.httpsOnly = false
 server.http2 = true
 server.expectContinue = false
 ----
diff --git a/docs/asciidoc/websocket.adoc b/docs/asciidoc/websocket.adoc
index b24afc021..3598d1c42 100644
--- a/docs/asciidoc/websocket.adoc
+++ b/docs/asciidoc/websocket.adoc
@@ -1,6 +1,7 @@
 == Web Sockets
 
-Adding a javadoc:WebSocket[]:
+Jooby supports https://developer.mozilla.org/es/docs/Web/API/WebSockets_API[WebSockets].
+A javadoc:WebSocket[] is registered like any other handler:
 
 .WebSocket
 [source,java,role="primary"]
@@ -169,9 +170,7 @@ import io.jooby.json.JacksonModule;
 Structure messages depends/requires a javadoc:MessageDecoder[] and jadoc:MessageEncoder[]. In this
 example both are provided by the JacksonModule.
 
-=== Options
-
-==== Connection Timeouts
+=== Connection Timeouts
 By default Jooby will timeout idle connections that have no activity after 5 minutes. You can 
 control this behaviour by setting the `websocket.idleTimeout` property:
 
@@ -182,15 +181,3 @@ websocket.idleTimeout = 1h
 ----
 
 See https://github.com/lightbend/config/blob/master/HOCON.md#duration-format[duration format]
-
-==== Max size
-
-Max size is set to `128K` you can override it like:
-
-.application.conf
-[source, properties]
-----
-websocket.maxSize = 128K
-----
-
-See https://github.com/lightbend/config/blob/master/HOCON.md#size-in-bytes-format[sizes in bytes]
diff --git a/docs/pom.xml b/docs/pom.xml
index d0fb98859..9e983241f 100644
--- a/docs/pom.xml
+++ b/docs/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>jooby-project</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -56,7 +56,7 @@
     <dependency>
       <groupId>io.methvin</groupId>
       <artifactId>directory-watcher</artifactId>
-      <version>0.15.1</version>
+      <version>0.15.0</version>
     </dependency>
 
     <!-- zt-exec -->
@@ -70,7 +70,7 @@
     <dependency>
       <groupId>org.jsoup</groupId>
       <artifactId>jsoup</artifactId>
-      <version>1.15.3</version>
+      <version>1.14.2</version>
     </dependency>
 
   </dependencies>
diff --git a/jooby/pom.xml b/jooby/pom.xml
index 081462a2f..e038200a6 100644
--- a/jooby/pom.xml
+++ b/jooby/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>jooby-project</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -277,7 +277,7 @@
     <dependency>
       <groupId>org.webjars.npm</groupId>
       <artifactId>vue</artifactId>
-      <version>3.2.33</version>
+      <version>3.0.3</version>
       <scope>test</scope>
     </dependency>
 
diff --git a/jooby/src/main/java/io/jooby/DefaultContext.java b/jooby/src/main/java/io/jooby/DefaultContext.java
index a95f62d46..538a8e029 100644
--- a/jooby/src/main/java/io/jooby/DefaultContext.java
+++ b/jooby/src/main/java/io/jooby/DefaultContext.java
@@ -23,7 +23,6 @@ import java.time.Instant;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -206,7 +205,7 @@ public interface DefaultContext extends Context {
   }
 
   @Override default boolean accept(@Nonnull MediaType contentType) {
-    return accept(singletonList(contentType)).equals(contentType);
+    return accept(singletonList(contentType)) == contentType;
   }
 
   @Override default MediaType accept(@Nonnull List<MediaType> produceTypes) {
diff --git a/jooby/src/main/java/io/jooby/Jooby.java b/jooby/src/main/java/io/jooby/Jooby.java
index 61d229bb5..2d25380d3 100644
--- a/jooby/src/main/java/io/jooby/Jooby.java
+++ b/jooby/src/main/java/io/jooby/Jooby.java
@@ -893,19 +893,17 @@ public class Jooby implements Router, Registry {
     log.info("    app dir: {}", System.getProperty("user.dir"));
     log.info("    tmp dir: {}", tmpdir);
 
-    List<Object> args = new ArrayList<>();
     StringBuilder buff = new StringBuilder();
     buff.append("routes: \n\n{}\n\nlistening on:\n");
-    args.add(router);
 
     ServerOptions options = server.getOptions();
     String host = options.getHost().replace("0.0.0.0", "localhost");
-    if (!options.isHttpsOnly()) {
-      args.add(host);
-      args.add(options.getPort());
-      args.add(router.getContextPath());
-      buff.append("  http://{}:{}{}\n");
-    }
+    List<Object> args = new ArrayList<>();
+    args.add(router);
+    args.add(host);
+    args.add(options.getPort());
+    args.add(router.getContextPath());
+    buff.append("  http://{}:{}{}\n");
 
     if (options.isSSLEnabled()) {
       args.add(host);
diff --git a/jooby/src/main/java/io/jooby/ServerOptions.java b/jooby/src/main/java/io/jooby/ServerOptions.java
index d630022d3..649ebcf54 100644
--- a/jooby/src/main/java/io/jooby/ServerOptions.java
+++ b/jooby/src/main/java/io/jooby/ServerOptions.java
@@ -61,8 +61,6 @@ public class ServerOptions {
   /** 10mb constant in bytes. */
   public static final int _10MB = 10485760;
 
-  private static final String LOCAL_HOST = "0.0.0.0";
-
   /** Buffer size used by server. Usually for reading/writing data. */
   private int bufferSize = _16KB;
 
@@ -88,7 +86,7 @@ public class ServerOptions {
    */
   private boolean defaultHeaders = true;
 
-  /** Name of server: Jetty, Netty or Undertow. */
+  /** Name of server: Jetty, Netty or Utow. */
   private String server;
 
   /**
@@ -97,17 +95,12 @@ public class ServerOptions {
    */
   private int maxRequestSize = _10MB;
 
-  private String host = LOCAL_HOST;
+  private String host = "0.0.0.0";
 
   private SslOptions ssl;
 
   private Integer securePort;
 
-  /**
-   * Bind only https port. Default is false.
-   */
-  private boolean httpsOnly;
-
   private Integer compressionLevel;
 
   private Boolean http2;
@@ -162,9 +155,6 @@ public class ServerOptions {
       }
       // ssl
       SslOptions.from(conf, "server.ssl").ifPresent(options::setSsl);
-      if (conf.hasPath("server.httpsOnly")) {
-        options.httpsOnly = conf.getBoolean("server.httpsOnly");
-      }
       if (conf.hasPath("server.http2")) {
         options.setHttp2(conf.getBoolean("server.http2"));
       }
@@ -184,7 +174,6 @@ public class ServerOptions {
     buff.append(", workerThreads: ").append(getWorkerThreads());
     buff.append(", bufferSize: ").append(bufferSize);
     buff.append(", maxRequestSize: ").append(maxRequestSize);
-    buff.append(", httpsOnly: ").append(httpsOnly);
     if (compressionLevel != null) {
       buff.append(", gzip");
     }
@@ -257,7 +246,7 @@ public class ServerOptions {
    * @param securePort Port number or <code>0</code> for random number.
    * @return This options.
    */
-  public @Nonnull ServerOptions setSecurePort(@Nullable Integer securePort) {
+  public @Nullable ServerOptions setSecurePort(@Nullable Integer securePort) {
     if (securePort == null) {
       this.securePort = null;
     } else {
@@ -266,26 +255,6 @@ public class ServerOptions {
     return this;
   }
 
-  /**
-   * Bind only https port. Default is false.
-   *
-   * @return True when only https is required.
-   */
-  public boolean isHttpsOnly() {
-    return httpsOnly;
-  }
-
-  /**
-   * Bind only https port. Default is false.
-   *
-   * @param httpsOnly True to bind only HTTPS.
-   * @return This options.
-   */
-  public @Nonnull ServerOptions setHttpsOnly(boolean httpsOnly) {
-    this.httpsOnly = httpsOnly;
-    return this;
-  }
-
   /**
    * Number of IO threads used by the server. Required by Netty and Undertow.
    *
@@ -483,7 +452,7 @@ public class ServerOptions {
    */
   public void setHost(String host) {
     if (host == null || host.trim().length() == 0 || "localhost".equalsIgnoreCase(host.trim())) {
-      this.host = LOCAL_HOST;
+      this.host = "0.0.0";
     } else {
       this.host = host;
     }
diff --git a/jooby/src/main/java/io/jooby/WebSocket.java b/jooby/src/main/java/io/jooby/WebSocket.java
index 7face7f8e..1fa16347a 100644
--- a/jooby/src/main/java/io/jooby/WebSocket.java
+++ b/jooby/src/main/java/io/jooby/WebSocket.java
@@ -108,7 +108,7 @@ public interface WebSocket {
     void onError(@Nonnull WebSocket ws, @Nonnull Throwable cause);
   }
 
-  /** Max message size for websocket (128K). */
+  /** Max message size for websocket (13k). */
   int MAX_BUFFER_SIZE = 131072;
 
   /**
diff --git a/jooby/src/main/java/io/jooby/annotations/Transactional.java b/jooby/src/main/java/io/jooby/annotations/Transactional.java
index bf1b6b551..d4b1d8449 100644
--- a/jooby/src/main/java/io/jooby/annotations/Transactional.java
+++ b/jooby/src/main/java/io/jooby/annotations/Transactional.java
@@ -33,7 +33,7 @@ import java.lang.annotation.Target;
  * This annotation has no effect on the behavior of {@code SessionRequest} decorator(s).
  */
 @RouteAttribute
-@Target({ElementType.TYPE, ElementType.METHOD})
+@Target(ElementType.METHOD)
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Transactional {
 
diff --git a/jooby/src/main/java/io/jooby/exception/ProvisioningException.java b/jooby/src/main/java/io/jooby/exception/ProvisioningException.java
index 1cdeae54a..d553adadb 100644
--- a/jooby/src/main/java/io/jooby/exception/ProvisioningException.java
+++ b/jooby/src/main/java/io/jooby/exception/ProvisioningException.java
@@ -6,8 +6,6 @@
 package io.jooby.exception;
 
 import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Executable;
 import java.lang.reflect.Parameter;
@@ -26,9 +24,9 @@ public class ProvisioningException extends BadRequestException {
    * Creates a provisioning exception.
    *
    * @param parameter Failing parameter.
-   * @param cause Cause. Nullable.
+   * @param cause Cause.
    */
-  public ProvisioningException(@Nonnull Parameter parameter, @Nullable Throwable cause) {
+  public ProvisioningException(@Nonnull Parameter parameter, @Nonnull Throwable cause) {
     this("Unable to provision parameter: '" + toString(parameter) + "', require by: " + toString(
         parameter.getDeclaringExecutable()), cause);
   }
@@ -39,7 +37,7 @@ public class ProvisioningException extends BadRequestException {
    * @param message Error message.
    * @param cause Cause.
    */
-  public ProvisioningException(@Nonnull String message, @Nullable Throwable cause) {
+  public ProvisioningException(@Nonnull String message, @Nonnull Throwable cause) {
     super(message, cause);
   }
 
diff --git a/jooby/src/main/java/io/jooby/internal/HttpMessageEncoder.java b/jooby/src/main/java/io/jooby/internal/HttpMessageEncoder.java
index e73819b40..31987fadb 100644
--- a/jooby/src/main/java/io/jooby/internal/HttpMessageEncoder.java
+++ b/jooby/src/main/java/io/jooby/internal/HttpMessageEncoder.java
@@ -100,11 +100,7 @@ public class HttpMessageEncoder implements MessageEncoder {
     }
     if (encoders != null) {
       // Content negotiation, find best:
-      List<MediaType> produces = ctx.getRoute().getProduces();
-      if (produces.isEmpty()) {
-        produces = new ArrayList<>(encoders.keySet());
-      }
-      MediaType type = ctx.accept(produces);
+      MediaType type = ctx.accept(new ArrayList<>(encoders.keySet()));
       MessageEncoder encoder = encoders.getOrDefault(type, MessageEncoder.TO_STRING);
       return encoder.encode(ctx, value);
     } else {
diff --git a/jooby/src/main/java/io/jooby/internal/MissingValue.java b/jooby/src/main/java/io/jooby/internal/MissingValue.java
index 74b22a5b4..d41f8d26d 100644
--- a/jooby/src/main/java/io/jooby/internal/MissingValue.java
+++ b/jooby/src/main/java/io/jooby/internal/MissingValue.java
@@ -5,18 +5,16 @@
  */
 package io.jooby.internal;
 
+import io.jooby.exception.MissingValueException;
+import io.jooby.ValueNode;
+
+import javax.annotation.Nonnull;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 
-import javax.annotation.Nonnull;
-
-import io.jooby.ValueNode;
-import io.jooby.exception.MissingValueException;
-
 public class MissingValue implements ValueNode {
   private String name;
 
@@ -75,15 +73,4 @@ public class MissingValue implements ValueNode {
   @Override public String toString() {
     return "<missing>";
   }
-
-  @Override public boolean equals(Object o) {
-    if (o instanceof MissingValue) {
-      return Objects.equals(name, ((MissingValue) o).name);
-    }
-    return false;
-  }
-
-  @Override public int hashCode() {
-    return Objects.hash(name);
-  }
 }
diff --git a/jooby/src/main/java/io/jooby/internal/MultipartNode.java b/jooby/src/main/java/io/jooby/internal/MultipartNode.java
index ea0dfd660..5cb03e072 100644
--- a/jooby/src/main/java/io/jooby/internal/MultipartNode.java
+++ b/jooby/src/main/java/io/jooby/internal/MultipartNode.java
@@ -11,7 +11,13 @@ import io.jooby.Multipart;
 import io.jooby.SneakyThrows;
 
 import javax.annotation.Nonnull;
-import java.util.*;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 public class MultipartNode extends FormdataNode implements Multipart {
@@ -36,8 +42,7 @@ public class MultipartNode extends FormdataNode implements Multipart {
   @Nonnull @Override public FileUpload file(@Nonnull String name) {
     List<FileUpload> files = files(name);
     if (files.isEmpty()) {
-      final String error = "Field '" + name + "' is missing";
-      throw SneakyThrows.propagate(new NoSuchElementException(error));
+      throw SneakyThrows.propagate(new FileNotFoundException(name));
     }
     return files.get(0);
   }
diff --git a/jooby/src/main/java/io/jooby/internal/RouteAnalyzer.java b/jooby/src/main/java/io/jooby/internal/RouteAnalyzer.java
index 73c607d94..ad427abf6 100644
--- a/jooby/src/main/java/io/jooby/internal/RouteAnalyzer.java
+++ b/jooby/src/main/java/io/jooby/internal/RouteAnalyzer.java
@@ -44,6 +44,7 @@ public class RouteAnalyzer {
       ClassReader reader = new ClassReader(source.byteCode(method.getDeclaringClass()));
       MethodFinder visitor = new MethodFinder(method, debug);
       reader.accept(visitor, 0);
+      ReturnType returnTypeVisitor = new ReturnType(typeParser, visitor.node);
 
       if (debug) {
         System.out.println(method);
@@ -52,7 +53,7 @@ public class RouteAnalyzer {
         writer.flush();
       }
 
-      return ReturnType.find(typeParser, visitor.node);
+      return returnTypeVisitor.returnType();
     } catch (Exception x) {
       throw SneakyThrows.propagate(x);
     }
diff --git a/jooby/src/main/java/io/jooby/internal/asm/MethodFinder.java b/jooby/src/main/java/io/jooby/internal/asm/MethodFinder.java
index d87036be0..23ccf292b 100644
--- a/jooby/src/main/java/io/jooby/internal/asm/MethodFinder.java
+++ b/jooby/src/main/java/io/jooby/internal/asm/MethodFinder.java
@@ -23,7 +23,7 @@ public class MethodFinder extends ClassVisitor {
   public ASMifier printer;
 
   public MethodFinder(Method method, boolean debug) {
-    super(Opcodes.ASM9);
+    super(Opcodes.ASM7);
     this.descriptor = Type.getMethodDescriptor(method);
     this.name = method.getName();
     this.debug = debug;
@@ -42,6 +42,13 @@ public class MethodFinder extends ClassVisitor {
         return new TraceMethodVisitor(this.node, printer);
       }
       return this.node;
+    } else {
+      if (debug) {
+        if (printer == null) {
+          printer = new ASMifier();
+        }
+        return new TraceMethodVisitor(this.node, printer);
+      }
     }
     return up;
   }
diff --git a/jooby/src/main/java/io/jooby/internal/asm/ReturnType.java b/jooby/src/main/java/io/jooby/internal/asm/ReturnType.java
index 7883ec838..388a30e4f 100644
--- a/jooby/src/main/java/io/jooby/internal/asm/ReturnType.java
+++ b/jooby/src/main/java/io/jooby/internal/asm/ReturnType.java
@@ -5,17 +5,8 @@
  */
 package io.jooby.internal.asm;
 
-import static java.util.Optional.ofNullable;
-
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
-
 import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.tree.AbstractInsnNode;
@@ -30,180 +21,174 @@ import org.objectweb.asm.tree.MethodNode;
 import org.objectweb.asm.tree.TypeInsnNode;
 import org.objectweb.asm.tree.VarInsnNode;
 
-public class ReturnType {
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static io.jooby.internal.asm.Insns.last;
+
+public class ReturnType extends MethodVisitor {
 
   private static final Predicate<AbstractInsnNode> LABEL = LabelNode.class::isInstance;
 
   private static final Predicate<AbstractInsnNode> LINE_NUMBER = LineNumberNode.class::isInstance;
 
-  private static final Predicate<AbstractInsnNode> KT_INTERNAL = it -> {
-    if (it instanceof MethodInsnNode) {
-      return ((MethodInsnNode) it).owner.startsWith("kotlin/jvm/internal");
-    }
-    return false;
-  };
+  private final TypeParser typeParser;
 
-  private static final Predicate<AbstractInsnNode> IGNORE = KT_INTERNAL
-      .or(LABEL)
-      .or(LINE_NUMBER);
-
-  private static final Predicate<AbstractInsnNode> KT_LDC = it -> {
-    if (it instanceof LdcInsnNode) {
-      return it.getPrevious() != null && it.getPrevious().getOpcode() == Opcodes.DUP;
-    }
-    return false;
-  };
+  public ReturnType(TypeParser typeParser, MethodNode visitor) {
+    super(Opcodes.ASM6, visitor);
+    this.typeParser = typeParser;
+  }
 
-  public static java.lang.reflect.Type find(TypeParser typeParser, MethodNode node) {
-    List<AbstractInsnNode> returns = findReturns(node);
-    Set<java.lang.reflect.Type> types = new LinkedHashSet<>();
-    for (AbstractInsnNode aReturn : returns) {
-      AbstractInsnNode previous = previous(aReturn.getPrevious());
-      if (previous instanceof MethodInsnNode) {
-        MethodInsnNode call = (MethodInsnNode) previous;
-        // Constructor?
-        if (call.name.equals("<init>")) {
-          types.add(typeParser.resolve(call.owner));
-        } else {
-          types.add(typeParser.parseTypeDescriptor(Type.getReturnType(call.desc).getDescriptor()));
-        }
-      } else if (previous instanceof VarInsnNode) {
-        localVariable(typeParser, node, (VarInsnNode) previous)
-            .ifPresent(types::add);
-      } else if (previous instanceof InvokeDynamicInsnNode) {
-        // visitInvokeDynamicInsn("call", "()Ljava/util/concurrent/Callable;", new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;", false), new Object[]{Type.getType("()Ljava/lang/Object;"), new Handle(Opcodes.H_INVOKESTATIC, "io/jooby/internal/ReturnTypeTest", "lambda$null$11", "()Ljava/lang/Character;", false), Type.getType("()Ljava/lang/Character;")});
-        // (Callable<Character>) () -> 'x'
-        InvokeDynamicInsnNode invokeDynamic = (InvokeDynamicInsnNode) previous;
-        String handleDescriptor = Stream.of(invokeDynamic.bsmArgs)
-            .filter(Handle.class::isInstance)
-            .map(Handle.class::cast)
-            .findFirst()
-            .map(h -> {
-              String desc = Type.getReturnType(h.getDesc()).getDescriptor();
-              return "V".equals(desc) ? "java/lang/Object" : desc;
-            })
-            .orElse(null);
-        // Ljava/util/concurrent/Callable;
-        String descriptor = Type
-            .getReturnType(invokeDynamic.desc)
-            .getDescriptor();
-        if (handleDescriptor != null) {
-          // Handle: ()Ljava/lang/Character;
-          if (descriptor.endsWith(";")) {
-            descriptor = descriptor.substring(0, descriptor.length() - 1);
+  public java.lang.reflect.Type returnType() {
+    MethodNode node = (MethodNode) mv;
+    Set<java.lang.reflect.Type> result = last(node.instructions)
+        .filter(it -> it.getOpcode() == Opcodes.ARETURN)
+        .map(it -> {
+          AbstractInsnNode previous = Insns.previous(it.getPrevious())
+              .filter(LABEL.negate().and(LINE_NUMBER.negate()))
+              .findFirst()
+              .orElse(it.getPrevious());
+          String sourcedesc = null;
+          /** return 1; return true; return new Foo(); */
+          if (previous instanceof MethodInsnNode) {
+            MethodInsnNode minnsn = (MethodInsnNode) previous;
+            if (minnsn.name.equals("<init>")) {
+              return typeParser.resolve(minnsn.owner);
+            }
+            if (previous.getOpcode() == Opcodes.INVOKEVIRTUAL) {
+              AbstractInsnNode invokeDynamic = Insns.previous(previous)
+                  .filter(InvokeDynamicInsnNode.class::isInstance)
+                  .findFirst()
+                  .orElse(null);
+              if (invokeDynamic != null) {
+                sourcedesc = minnsn.desc;
+                previous = invokeDynamic;
+              } else {
+                return typeParser
+                    .parseTypeDescriptor(
+                        Type.getReturnType(minnsn.desc).getDescriptor());
+              }
+            } else if (previous.getOpcode() == Opcodes.INVOKESPECIAL) {
+              try {
+                MethodInsnNode invokeSpecial = (MethodInsnNode) previous;
+                Class owner = typeParser.resolve(invokeSpecial.owner);
+                Class[] args = Stream.of(Type.getArgumentTypes(invokeSpecial.desc))
+                    .map(e -> typeParser.resolve(e.getInternalName()))
+                    .toArray(Class[]::new);
+                Method reference = owner.getDeclaredMethod(invokeSpecial.name, args);
+                return reference.getGenericReturnType();
+              } catch (NoSuchMethodException x) {
+                return Object.class;
+              }
+            } else {
+              return typeParser
+                  .parseTypeDescriptor(Type.getReturnType(minnsn.desc).getDescriptor());
+            }
           }
-          descriptor += "<" + handleDescriptor + ">";
-        }
-        types.add(typeParser.parseTypeDescriptor(descriptor));
-      } else if (previous instanceof LdcInsnNode) {
-        /** return "String" | int | double */
-        Object cst = ((LdcInsnNode) previous).cst;
-        if (cst instanceof Type) {
-          types.add(typeParser.parseTypeDescriptor(((Type) cst).getDescriptor()));
-        } else {
-          types.add(cst.getClass());
-        }
-      } else {
-        // array
-        switch (previous.getOpcode()) {
-          case Opcodes.NEWARRAY: {
-            ofNullable(primitiveEmptyArray(previous))
-                .ifPresent(types::add);
+          /** return "String" | int | double */
+          if (previous instanceof LdcInsnNode) {
+            Object cst = ((LdcInsnNode) previous).cst;
+            if (cst instanceof Type) {
+              return typeParser.parseTypeDescriptor(((Type) cst).getDescriptor());
+            }
+            return cst.getClass();
           }
-          break;
-          case Opcodes.ANEWARRAY: {
-            TypeInsnNode typeInsn = (TypeInsnNode) previous;
-            types.add(typeParser.parseTypeDescriptor("[" + typeInsn.desc));
+          /** return variable */
+          if (previous instanceof VarInsnNode) {
+            VarInsnNode varInsn = (VarInsnNode) previous;
+            return localVariable(node, varInsn);
           }
-          break;
-          case Opcodes.BASTORE:
-            types.add(boolean[].class);
-            break;
-          case Opcodes.CASTORE:
-            types.add(char[].class);
-            break;
-          case Opcodes.SASTORE:
-            types.add(short[].class);
-            break;
-          case Opcodes.IASTORE:
-            types.add(int[].class);
-            break;
-          case Opcodes.LASTORE:
-            types.add(long[].class);
-            break;
-          case Opcodes.FASTORE:
-            types.add(float[].class);
-            break;
-          case Opcodes.DASTORE:
-            types.add(double[].class);
-            break;
-          case Opcodes.AASTORE:
-            return Insns.previous(previous)
-                .filter(e -> e.getOpcode() == Opcodes.ANEWARRAY)
+          /** Invoke dynamic: */
+          if (previous instanceof InvokeDynamicInsnNode) {
+            InvokeDynamicInsnNode invokeDynamic = (InvokeDynamicInsnNode) previous;
+            String handleDescriptor = Stream.of(invokeDynamic.bsmArgs)
+                .filter(Handle.class::isInstance)
+                .map(Handle.class::cast)
                 .findFirst()
-                .map(e -> {
-                  TypeInsnNode typeInsn = (TypeInsnNode) e;
-                  return typeParser.parseTypeDescriptor("[" + typeInsn.desc);
+                .map(h -> {
+                  String desc = Type.getReturnType(h.getDesc()).getDescriptor();
+                  return "V".equals(desc) ? "java/lang/Object" : desc;
                 })
-                .orElse(Object.class);
-        }
-      }
-    }
-    return types.isEmpty() ? Object.class : typeParser.commonAncestor(types);
-  }
-
-  private static Class primitiveEmptyArray(AbstractInsnNode previous) {
-    // empty primitive array
-    if (previous instanceof IntInsnNode) {
-      switch (((IntInsnNode) previous).operand) {
-        case Opcodes.T_BOOLEAN:
-          return boolean[].class;
-        case Opcodes.T_CHAR:
-          return char[].class;
-        case Opcodes.T_BYTE:
-          return byte[].class;
-        case Opcodes.T_SHORT:
-          return short[].class;
-        case Opcodes.T_INT:
-          return int[].class;
-        case Opcodes.T_LONG:
-          return long[].class;
-        case Opcodes.T_FLOAT:
-          return float[].class;
-        case Opcodes.T_DOUBLE:
-          return double[].class;
-      }
-    }
-    return null;
-  }
-
-  private static AbstractInsnNode previous(AbstractInsnNode it) {
-    while (it != null) {
-      if (IGNORE.test(it)) {
-        it = it.getPrevious();
-      } else {
-        if (KT_LDC.test(it)) {
-          it = it.getPrevious().getPrevious();
-        } else {
-          return it;
-        }
-      }
-    }
-    return null;
-  }
+                .orElse(null);
+            String descriptor = Type
+                .getReturnType(Optional.ofNullable(sourcedesc).orElse(invokeDynamic.desc))
+                .getDescriptor();
+            if (handleDescriptor != null) {
+              if (descriptor.endsWith(";")) {
+                descriptor = descriptor.substring(0, descriptor.length() - 1);
+              }
+              descriptor += "<" + handleDescriptor + ">";
+            }
+            return typeParser.parseTypeDescriptor(descriptor);
+          }
+          /** array literal: */
+          if (previous.getOpcode() == Opcodes.NEWARRAY) {
+            // empty primitive array
+            if (previous instanceof IntInsnNode) {
+              switch (((IntInsnNode) previous).operand) {
+                case Opcodes.T_BOOLEAN:
+                  return boolean[].class;
+                case Opcodes.T_CHAR:
+                  return char[].class;
+                case Opcodes.T_BYTE:
+                  return byte[].class;
+                case Opcodes.T_SHORT:
+                  return short[].class;
+                case Opcodes.T_INT:
+                  return int[].class;
+                case Opcodes.T_LONG:
+                  return long[].class;
+                case Opcodes.T_FLOAT:
+                  return float[].class;
+                case Opcodes.T_DOUBLE:
+                  return double[].class;
+              }
+            }
+          }
+          // empty array of objects
+          if (previous.getOpcode() == Opcodes.ANEWARRAY) {
+            TypeInsnNode typeInsn = (TypeInsnNode) previous;
+            return typeParser.parseTypeDescriptor("[" + typeInsn.desc);
+          }
+          // non empty array
+          switch (previous.getOpcode()) {
+            case Opcodes.BASTORE:
+              return boolean[].class;
+            case Opcodes.CASTORE:
+              return char[].class;
+            case Opcodes.SASTORE:
+              return short[].class;
+            case Opcodes.IASTORE:
+              return int[].class;
+            case Opcodes.LASTORE:
+              return long[].class;
+            case Opcodes.FASTORE:
+              return float[].class;
+            case Opcodes.DASTORE:
+              return double[].class;
+            case Opcodes.AASTORE:
+              return Insns.previous(previous)
+                  .filter(e -> e.getOpcode() == Opcodes.ANEWARRAY)
+                  .findFirst()
+                  .map(e -> {
+                    TypeInsnNode typeInsn = (TypeInsnNode) e;
+                    return typeParser.parseTypeDescriptor("[" + typeInsn.desc);
+                  })
+                  .orElse(Object.class);
+          }
 
-  private static List<AbstractInsnNode> findReturns(MethodNode node) {
-    List<AbstractInsnNode> result = new ArrayList<>();
-    for (AbstractInsnNode instruction : node.instructions) {
-      if (instruction.getOpcode() == Opcodes.ARETURN) {
-        result.add(instruction);
-      }
-    }
-    return result;
+          return Object.class;
+        })
+        .collect(Collectors.toSet());
+    return typeParser.commonAncestor(result);
   }
 
-  private static Optional<java.lang.reflect.Type> localVariable(TypeParser typeParser, MethodNode m,
-      VarInsnNode varInsn) {
+  private java.lang.reflect.Type localVariable(final MethodNode m, final VarInsnNode varInsn) {
     if (varInsn.getOpcode() == Opcodes.ALOAD) {
       List<LocalVariableNode> vars = m.localVariables;
       LocalVariableNode var = vars.stream()
@@ -211,10 +196,10 @@ public class ReturnType {
           .findFirst()
           .orElse(null);
       if (var != null) {
-        String signature = ofNullable(var.signature).orElse(var.desc);
-        return Optional.of(typeParser.parseTypeDescriptor(signature));
+        String signature = Optional.ofNullable(var.signature).orElse(var.desc);
+        return typeParser.parseTypeDescriptor(signature);
       }
     }
-    return Optional.empty();
+    return Object.class;
   }
 }
diff --git a/jooby/src/main/java/io/jooby/internal/converter/ReflectiveBeanConverter.java b/jooby/src/main/java/io/jooby/internal/converter/ReflectiveBeanConverter.java
index 581876782..a1f20fb4d 100644
--- a/jooby/src/main/java/io/jooby/internal/converter/ReflectiveBeanConverter.java
+++ b/jooby/src/main/java/io/jooby/internal/converter/ReflectiveBeanConverter.java
@@ -17,7 +17,6 @@ import java.lang.reflect.Parameter;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
@@ -67,7 +66,7 @@ public class ReflectiveBeanConverter implements BeanConverter {
           Collections.emptySet());
     }
     Constructor constructor = selectConstructor(constructors);
-    Set<Object> state = new HashSet<>();
+    Set<ValueNode> state = new HashSet<>();
     Object[] args = constructor.getParameterCount() == 0
         ? NO_ARGS
         : inject(node, constructor, state::add);
@@ -131,34 +130,15 @@ public class ReflectiveBeanConverter implements BeanConverter {
     throw Usage.parameterNameNotPresent(parameter);
   }
 
-  /**
-   * Collect all possible values.
-   *
-   * @param node Root node.
-   * @return Names, including file names.
-   */
-  private static Set<String> names(ValueNode node) {
-    Set<String> names = new LinkedHashSet<>();
-    for (ValueNode item : node) {
-      names.add(item.name());
-    }
-    if (node instanceof Multipart) {
-      for (FileUpload file : ((Multipart) node).files()) {
-        names.add(file.getName());
-      }
-    }
-    return names;
-  }
-
-  private static <T> T setters(T newInstance, ValueNode node, Set<Object> skip) {
+  private static <T> T setters(T newInstance, ValueNode node, Set<ValueNode> skip) {
     Method[] methods = newInstance.getClass().getMethods();
-    for (String name: names(node)) {
-      ValueNode value = node.get(name);
+    for (ValueNode value : node) {
       if (!skip.contains(value)) {
-        String methodName = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
-        Method method = findSetter(methods, methodName);
+        String name = value.name();
+        String setter1 = "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+        Method method = findMethod(methods, setter1);
         if (method == null) {
-          method = findSetter(methods, name);
+          method = findMethod(methods, name);
         }
         if (method != null) {
           Parameter parameter = method.getParameters()[0];
@@ -223,7 +203,7 @@ public class ReflectiveBeanConverter implements BeanConverter {
     return FileUpload.class == type;
   }
 
-  private static Method findSetter(Method[] methods, String name) {
+  private static Method findMethod(Method[] methods, String name) {
     for (Method method : methods) {
       if (method.getName().equals(name) && method.getParameterCount() == 1) {
         return method;
diff --git a/jooby/src/main/kotlin/io/jooby/HandlerContext.kt b/jooby/src/main/kotlin/io/jooby/HandlerContext.kt
index 91c6dc133..ee46afdcc 100644
--- a/jooby/src/main/kotlin/io/jooby/HandlerContext.kt
+++ b/jooby/src/main/kotlin/io/jooby/HandlerContext.kt
@@ -9,7 +9,7 @@ class AfterContext(val ctx: Context, val result: Any?, val failure: Any?)
 
 class DecoratorContext(val ctx: Context, val next: Route.Handler)
 
-class HandlerContext(val ctx: Context): java.io.Serializable
+class HandlerContext(val ctx: Context)
 
 class WebSocketInitContext(val ctx: Context, val configurer: WebSocketConfigurer)
 
diff --git a/modules/jooby-apt/pom.xml b/modules/jooby-apt/pom.xml
index 7cc0c0654..790a8739c 100644
--- a/modules/jooby-apt/pom.xml
+++ b/modules/jooby-apt/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-apt/src/main/java/io/jooby/internal/apt/HandlerCompiler.java b/modules/jooby-apt/src/main/java/io/jooby/internal/apt/HandlerCompiler.java
index e7bc5b3ae..ad4a1f701 100644
--- a/modules/jooby-apt/src/main/java/io/jooby/internal/apt/HandlerCompiler.java
+++ b/modules/jooby-apt/src/main/java/io/jooby/internal/apt/HandlerCompiler.java
@@ -231,9 +231,25 @@ public class HandlerCompiler {
   private void processReturnType(MethodVisitor visitor) throws Exception {
     TypeKind kind = executable.getReturnType().getKind();
     if (kind == TypeKind.VOID) {
-      Method getResponseCode = Context.class.getDeclaredMethod("getResponseCode");
       visitor.visitVarInsn(ALOAD, 1);
-      visitor.visitMethodInsn(INVOKEINTERFACE, CTX.getInternalName(), getResponseCode.getName(), getMethodDescriptor(getResponseCode), true);
+      Method isResponseStarted = Context.class.getDeclaredMethod("isResponseStarted");
+      visitor.visitMethodInsn(INVOKEINTERFACE, CTX.getInternalName(), isResponseStarted.getName(),
+          getMethodDescriptor(isResponseStarted), true);
+      Label label0 = new Label();
+      visitor.visitJumpInsn(IFEQ, label0);
+      visitor.visitVarInsn(ALOAD, 1);
+      visitor.visitInsn(ARETURN);
+      visitor.visitLabel(label0);
+      visitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+
+      visitor.visitVarInsn(ALOAD, 1);
+      visitor.visitVarInsn(ALOAD, 1);
+      Method getResponseCode = Context.class.getDeclaredMethod("getResponseCode");
+      visitor.visitMethodInsn(INVOKEINTERFACE, CTX.getInternalName(), getResponseCode.getName(),
+          getMethodDescriptor(getResponseCode), true);
+      Method sendStatusCode = Context.class.getDeclaredMethod("send", StatusCode.class);
+      visitor.visitMethodInsn(INVOKEINTERFACE, CTX.getInternalName(), sendStatusCode.getName(),
+          getMethodDescriptor(sendStatusCode), true);
     } else {
       Method wrapper = Primitives.wrapper(kind);
       if (wrapper == null) {
@@ -242,11 +258,9 @@ public class HandlerCompiler {
           visitor.visitVarInsn(ASTORE, 2);
           visitor.visitVarInsn(ALOAD, 1);
           visitor.visitVarInsn(ALOAD, 2);
-          Method setResponseCode = Context.class.getDeclaredMethod("setResponseCode", StatusCode.class);
-          visitor.visitMethodInsn(INVOKEINTERFACE, CTX.getInternalName(), setResponseCode.getName(),
-              getMethodDescriptor(setResponseCode), true);
-          visitor.visitInsn(POP);
-          visitor.visitVarInsn(ALOAD, 2);
+          Method send = Context.class.getDeclaredMethod("send", StatusCode.class);
+          visitor.visitMethodInsn(INVOKEINTERFACE, CTX.getInternalName(), send.getName(),
+              getMethodDescriptor(send), true);
         }
       } else {
         // Primitive wrapper
diff --git a/modules/jooby-apt/src/main/java/io/jooby/internal/apt/ModuleCompiler.java b/modules/jooby-apt/src/main/java/io/jooby/internal/apt/ModuleCompiler.java
index 4abf78fc5..e33f23481 100644
--- a/modules/jooby-apt/src/main/java/io/jooby/internal/apt/ModuleCompiler.java
+++ b/modules/jooby-apt/src/main/java/io/jooby/internal/apt/ModuleCompiler.java
@@ -10,7 +10,6 @@ import io.jooby.MediaType;
 import io.jooby.MvcFactory;
 import io.jooby.Reified;
 import io.jooby.Route;
-import io.jooby.StatusCode;
 import io.jooby.annotations.Dispatch;
 import io.jooby.internal.apt.asm.ArrayWriter;
 import io.jooby.internal.apt.asm.NameGenerator;
@@ -267,7 +266,7 @@ public class ModuleCompiler {
     if (handler.isSuspendFunction()) {
       visitor.visitLdcInsn(Type.getType("Lkotlin/coroutines/Continuation;"));
     } else if (returnType.isVoid()) {
-      visitor.visitLdcInsn(Type.getType(StatusCode.class));
+      visitor.visitLdcInsn(Type.getType(Context.class));
     } else if (returnType.isPrimitive()) {
       Method wrapper = Primitives.wrapper(returnType);
       visitor.visitFieldInsn(GETSTATIC, Type.getInternalName(wrapper.getDeclaringClass()), "TYPE",
diff --git a/modules/jooby-archetype/pom.xml b/modules/jooby-archetype/pom.xml
index f8da43135..423435636 100644
--- a/modules/jooby-archetype/pom.xml
+++ b/modules/jooby-archetype/pom.xml
@@ -7,7 +7,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>jooby-archetype</artifactId>
diff --git a/modules/jooby-awssdk-v1/pom.xml b/modules/jooby-awssdk-v1/pom.xml
index 635ef3e6d..df116ea78 100644
--- a/modules/jooby-awssdk-v1/pom.xml
+++ b/modules/jooby-awssdk-v1/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-banner/pom.xml b/modules/jooby-banner/pom.xml
index 4fbf88874..6b88ff3eb 100644
--- a/modules/jooby-banner/pom.xml
+++ b/modules/jooby-banner/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-bom/pom.xml b/modules/jooby-bom/pom.xml
index 719735071..0b3a06c5d 100644
--- a/modules/jooby-bom/pom.xml
+++ b/modules/jooby-bom/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -16,20 +16,20 @@
   <!-- THIS FILE IS AUTO GENERATED. DON'T EDIT -->
 
   <properties>
-  <jooby.version>2.15.2-SNAPSHOT</jooby.version>
+  <jooby.version>2.13.1-SNAPSHOT</jooby.version>
   <HikariCP.version>4.0.3</HikariCP.version>
   <archetype-packaging.version>3.2.0</archetype-packaging.version>
-  <asm.version>9.3</asm.version>
-  <auto-service.version>1.0.1</auto-service.version>
-  <aws-java-sdk.version>1.12.261</aws-java-sdk.version>
+  <asm.version>9.1</asm.version>
+  <auto-service.version>1.0</auto-service.version>
+  <aws-java-sdk.version>1.12.57</aws-java-sdk.version>
   <bucket4j-core.version>6.4.1</bucket4j-core.version>
   <caffeine.version>2.8.8</caffeine.version>
   <checkstyle.version>8.42</checkstyle.version>
   <commons-email.version>1.5</commons-email.version>
-  <commons-io.version>2.11.0</commons-io.version>
+  <commons-io.version>2.8.0</commons-io.version>
   <commons-pool2.version>2.11.1</commons-pool2.version>
   <compile-testing.version>0.19</compile-testing.version>
-  <config.version>1.4.2</config.version>
+  <config.version>1.4.1</config.version>
   <conscrypt.version>2.5.2</conscrypt.version>
   <cron-utils.version>9.1.6</cron-utils.version>
   <ebean.version>12.16.0</ebean.version>
@@ -37,92 +37,91 @@
   <flyway.version>7.5.1</flyway.version>
   <freemarker.version>2.3.31</freemarker.version>
   <frontend-maven-plugin.version>1.11.0</frontend-maven-plugin.version>
-  <frontend-plugin-core.version>1.12.1</frontend-plugin-core.version>
+  <frontend-plugin-core.version>1.12.0</frontend-plugin-core.version>
   <gradle-core.version>2.6</gradle-core.version>
   <gradle-plugins.version>0.9-rc-1</gradle-plugins.version>
   <gradle-tooling-api.version>2.6</gradle-tooling-api.version>
   <graphql-java.version>16.2</graphql-java.version>
   <gson.version>2.9.0</gson.version>
-  <guava.version>31.1-jre</guava.version>
-  <guice.version>5.1.0</guice.version>
-  <h2.version>2.1.212</h2.version>
-  <handlebars.version>4.3.0</handlebars.version>
+  <guava.version>30.0-jre</guava.version>
+  <guice.version>5.0.1</guice.version>
+  <h2.version>2.1.210</h2.version>
+  <handlebars.version>4.2.0</handlebars.version>
   <hibernate.version>5.6.3.Final</hibernate.version>
-  <jackson-databind.version>2.13.3</jackson-databind.version>
-  <jackson-dataformat-yaml.version>2.13.2</jackson-dataformat-yaml.version>
-  <jackson.version>2.13.3</jackson.version>
-  <jacoco-maven-plugin.version>0.8.8</jacoco-maven-plugin.version>
-  <jacoco.version>0.8.8</jacoco.version>
+  <jackson-databind.version>2.13.2.1</jackson-databind.version>
+  <jackson.version>2.13.2</jackson.version>
+  <jacoco-maven-plugin.version>0.8.7</jacoco-maven-plugin.version>
+  <jacoco.version>0.8.7</jacoco.version>
   <jakarta.json.bind-api.version>1.0.2</jakarta.json.bind-api.version>
   <jakarta.ws.rs-api.version>2.1.6</jakarta.ws.rs-api.version>
   <jasypt.version>1.9.3</jasypt.version>
   <javax.inject.version>1</javax.inject.version>
   <jboss-modules.version>1.11.0.Final</jboss-modules.version>
-  <jdbi.version>3.29.0</jdbi.version>
-  <jetty.version>9.4.46.v20220331</jetty.version>
-  <jfiglet.version>0.0.9</jfiglet.version>
-  <jmespath-java.version>1.12.230</jmespath-java.version>
-  <jooby-maven-plugin.version>2.15.2-SNAPSHOT</jooby-maven-plugin.version>
-  <jooby.version>2.15.2-SNAPSHOT</jooby.version>
-  <jsonwebtoken.version>0.11.5</jsonwebtoken.version>
+  <jdbi.version>3.27.0</jdbi.version>
+  <jetty.version>9.4.44.v20210927</jetty.version>
+  <jfiglet.version>0.0.8</jfiglet.version>
+  <jmespath-java.version>1.12.57</jmespath-java.version>
+  <jooby-maven-plugin.version>2.13.1-SNAPSHOT</jooby-maven-plugin.version>
+  <jooby.version>2.13.1-SNAPSHOT</jooby.version>
+  <jsonwebtoken.version>0.11.2</jsonwebtoken.version>
   <jsr305.version>3.0.2</jsr305.version>
-  <junit.version>5.8.2</junit.version>
+  <junit.version>5.8.1</junit.version>
   <kafka.version>2.7.0</kafka.version>
-  <kotlin.version>1.6.21</kotlin.version>
-  <kotlinx-coroutines-core.version>1.6.2</kotlinx-coroutines-core.version>
-  <lettuce.version>6.1.8.RELEASE</lettuce.version>
+  <kotlin.version>1.4.32</kotlin.version>
+  <kotlinx-coroutines-core.version>1.4.1</kotlinx-coroutines-core.version>
+  <lettuce.version>6.1.6.RELEASE</lettuce.version>
   <license-maven-plugin.version>4.1</license-maven-plugin.version>
-  <log4j.version>2.17.2</log4j.version>
+  <log4j.version>2.17.1</log4j.version>
   <log4jdbc.version>1.2</log4jdbc.version>
-  <logback-classic.version>1.2.11</logback-classic.version>
+  <logback-classic.version>1.2.9</logback-classic.version>
   <maven-antrun-plugin.version>3.0.0</maven-antrun-plugin.version>
   <maven-archetype-plugin.version>3.2.0</maven-archetype-plugin.version>
   <maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
   <maven-checkstyle-plugin.version>3.1.2</maven-checkstyle-plugin.version>
   <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
-  <maven-core.version>3.8.5</maven-core.version>
+  <maven-core.version>3.8.1</maven-core.version>
   <maven-dependency-plugin.version>3.1.2</maven-dependency-plugin.version>
   <maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
   <maven-enforcer-plugin.version>3.0.0-M3</maven-enforcer-plugin.version>
   <maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
-  <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
+  <maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
   <maven-javadoc-plugin.version>3.3.0</maven-javadoc-plugin.version>
-  <maven-plugin-annotations.version>3.6.4</maven-plugin-annotations.version>
-  <maven-plugin-api.version>3.8.5</maven-plugin-api.version>
+  <maven-plugin-annotations.version>3.6.0</maven-plugin-annotations.version>
+  <maven-plugin-api.version>3.8.1</maven-plugin-api.version>
   <maven-plugin-plugin.version>3.6.0</maven-plugin-plugin.version>
   <maven-project.version>2.2.1</maven-project.version>
   <maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
   <maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
   <maven-site-plugin.version>3.8.2</maven-site-plugin.version>
   <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
-  <maven-surefire-plugin.version>3.0.0-M6</maven-surefire-plugin.version>
+  <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
   <maven-tiles-plugin.version>2.24</maven-tiles-plugin.version>
-  <metrics.version>4.2.9</metrics.version>
-  <mockito.version>4.6.0</mockito.version>
+  <metrics.version>4.2.8</metrics.version>
+  <mockito.version>4.4.0</mockito.version>
   <mojo-executor.version>2.3.1</mojo-executor.version>
-  <mysql-connector-java.version>8.0.29</mysql-connector-java.version>
-  <netty.version>4.1.77.Final</netty.version>
+  <mysql-connector-java.version>8.0.27</mysql-connector-java.version>
+  <netty.version>4.1.75.Final</netty.version>
   <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version>
   <node.version>v12.19.0</node.version>
   <okhttp.version>4.9.3</okhttp.version>
-  <pac4j.version>4.5.6</pac4j.version>
+  <pac4j.version>5.2.0</pac4j.version>
   <pebble.version>3.1.5</pebble.version>
-  <plexus-utils.version>3.4.1</plexus-utils.version>
+  <plexus-utils.version>3.3.0</plexus-utils.version>
   <quartz.version>2.3.2</quartz.version>
-  <reactor.version>3.4.18</reactor.version>
+  <reactor.version>3.4.14</reactor.version>
   <redoc.version>2.0.0-rc.20</redoc.version>
-  <rest-assured.version>5.1.0</rest-assured.version>
+  <rest-assured.version>4.3.3</rest-assured.version>
   <rocker.version>1.3.0</rocker.version>
   <rxjava.version>2.2.21</rxjava.version>
-  <slf4j.version>1.7.36</slf4j.version>
-  <spring.version>5.3.20</spring.version>
+  <slf4j.version>1.7.32</slf4j.version>
+  <spring.version>5.3.7</spring.version>
   <stork-maven-plugin.version>3.1.1</stork-maven-plugin.version>
-  <swagger-parser.version>2.0.33</swagger-parser.version>
-  <swagger.version>2.2.0</swagger.version>
+  <swagger-parser.version>2.0.30</swagger-parser.version>
+  <swagger.version>2.1.13</swagger.version>
   <thymeleaf.version>3.0.12.RELEASE</thymeleaf.version>
-  <truth.version>1.1.3</truth.version>
+  <truth.version>1.1.2</truth.version>
   <unbescape.version>1.1.6.RELEASE</unbescape.version>
-  <undertow.version>2.2.19.Final</undertow.version>
+  <undertow.version>2.2.14.Final</undertow.version>
   <versions-maven-plugin.version>2.8.1</versions-maven-plugin.version>
   <weld.version>3.1.5.Final</weld.version>
   <yasson.version>1.0.9</yasson.version>
@@ -523,7 +522,7 @@
     <dependency>
       <groupId>com.fasterxml.jackson.dataformat</groupId>
       <artifactId>jackson-dataformat-yaml</artifactId>
-      <version>${jackson-dataformat-yaml.version}</version>
+      <version>${jackson.version}</version>
       <type>jar</type>
     </dependency>
     <dependency>
@@ -1102,180 +1101,6 @@
       <version>${plexus-utils.version}</version>
       <type>jar</type>
     </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-emrserverless</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-chimesdkmediapipelines</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-ivschat</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-pinpointsmsvoicev2</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-gamesparks</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-billingconductor</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-keyspaces</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-amplifyuibuilder</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-backupgateway</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-workspacesweb</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-iottwinmaker</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-recyclebin</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-cloudwatchrum</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-cloudwatchevidently</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-inspector2</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-migrationhubrefactorspaces</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-appconfigdata</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-drs</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-migrationhubstrategyrecommendations</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-resiliencehub</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-chimesdkmeetings</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-panorama</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-managedgrafana</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-cloudcontrolapi</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-account</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-voiceid</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-connectwisdom</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-kafkaconnect</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
-    <dependency>
-      <groupId>com.amazonaws</groupId>
-      <artifactId>aws-java-sdk-opensearch</artifactId>
-      <version>${aws-java-sdk.version}</version>
-      <type>jar</type>
-    </dependency>
     <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>aws-java-sdk-memorydb</artifactId>
diff --git a/modules/jooby-caffeine/pom.xml b/modules/jooby-caffeine/pom.xml
index f2debcbb8..328b9dd17 100644
--- a/modules/jooby-caffeine/pom.xml
+++ b/modules/jooby-caffeine/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-cli/pom.xml b/modules/jooby-cli/pom.xml
index 89ade49a0..3efb18d8d 100644
--- a/modules/jooby-cli/pom.xml
+++ b/modules/jooby-cli/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-cli/src/main/java/io/jooby/cli/Cli.java b/modules/jooby-cli/src/main/java/io/jooby/cli/Cli.java
index 09616e7b0..48839a09e 100644
--- a/modules/jooby-cli/src/main/java/io/jooby/cli/Cli.java
+++ b/modules/jooby-cli/src/main/java/io/jooby/cli/Cli.java
@@ -71,8 +71,8 @@ public class Cli extends Cmd {
       } else if ("-V".equalsIgnoreCase(arg) || "--version".equals(arg)) {
         ctx.println(ctx.getVersion());
       } else {
-        ctx.println("Unknown command or option(s): " + args.stream().collect(Collectors.joining(" ")));
-        ctx.println("  " + ctx);
+        ctx.println(
+            "Unknown command or option(s): " + args.stream().collect(Collectors.joining(" ")));
         ctx.println(spec.commandLine().getUsageMessage());
       }
     } else {
diff --git a/modules/jooby-cli/src/main/java/io/jooby/cli/Version.java b/modules/jooby-cli/src/main/java/io/jooby/cli/Version.java
index becf24dbd..633a81ebe 100644
--- a/modules/jooby-cli/src/main/java/io/jooby/cli/Version.java
+++ b/modules/jooby-cli/src/main/java/io/jooby/cli/Version.java
@@ -35,7 +35,7 @@ public class Version implements CommandLine.IVersionProvider {
   private static String doVersion() {
     try {
       URL url = URI
-          .create("https://search.maven.org/solrsearch/select?q=+g:io.jooby+a:jooby&start=0&rows=1")
+          .create("http://search.maven.org/solrsearch/select?q=+g:io.jooby+a:jooby&start=0&rows=1")
           .toURL();
       URLConnection connection = url.openConnection();
       try (Reader in = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)) {
@@ -49,7 +49,7 @@ public class Version implements CommandLine.IVersionProvider {
       return Optional.ofNullable(Version.class.getPackage())
           .map(Package::getImplementationVersion)
           .filter(Objects::nonNull)
-          .orElse("2.15.0");
+          .orElse("2.0.6");
     }
   }
 }
diff --git a/modules/jooby-cli/src/main/java/io/jooby/internal/cli/CommandContextImpl.java b/modules/jooby-cli/src/main/java/io/jooby/internal/cli/CommandContextImpl.java
index 20e457f94..1644d337c 100644
--- a/modules/jooby-cli/src/main/java/io/jooby/internal/cli/CommandContextImpl.java
+++ b/modules/jooby-cli/src/main/java/io/jooby/internal/cli/CommandContextImpl.java
@@ -9,7 +9,6 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
-import java.io.Reader;
 import java.io.Writer;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -47,8 +46,6 @@ public class CommandContextImpl implements Context {
 
   private Properties versions;
 
-  private Path configurationFile;
-
   public CommandContextImpl(LineReader reader, String version) throws IOException {
     this.reader = reader;
     this.out = reader.getTerminal().writer();
@@ -57,27 +54,17 @@ public class CommandContextImpl implements Context {
     this.templates.setPrettyPrint(true);
     this.version = version;
 
-    // move from .jooby to .config/jooby.conf
-    configurationFile = Paths.get(System.getProperty("user.home"), ".config", "jooby.conf");
-    migrateOldConfiguration(Paths.get(System.getProperty("user.home"), ".jooby"), configurationFile);
+    Path file = configurationPath();
 
-    if (Files.exists(configurationFile)) {
-      try (Reader in = Files.newBufferedReader(configurationFile)) {
-        configuration = Cli.gson.fromJson(in, LinkedHashMap.class);
-      }
+    if (Files.exists(file)) {
+      configuration = Cli.gson.fromJson(Files.newBufferedReader(file), LinkedHashMap.class);
     } else {
       configuration = new LinkedHashMap();
     }
   }
 
-  private void migrateOldConfiguration(Path from, Path to) throws IOException {
-    if (Files.exists(from)) {
-      if (!Files.exists(to.getParent())) {
-        Files.createDirectories(to.getParent());
-      }
-      Files.copy(from, to);
-      Files.delete(from);
-    }
+  private Path configurationPath() {
+    return Paths.get(System.getProperty("user.home"), ".jooby");
   }
 
   @Nonnull @Override public String getVersion() {
@@ -96,7 +83,8 @@ public class CommandContextImpl implements Context {
     }
     configuration.put("workspace", workspace.toAbsolutePath().toString());
     String json = Cli.gson.toJson(configuration);
-    Files.write(configurationFile, json.getBytes(StandardCharsets.UTF_8));
+    Files.write(configurationPath(), json.getBytes(StandardCharsets.UTF_8));
+
   }
 
   @Override public void exit(int code) {
@@ -166,8 +154,4 @@ public class CommandContextImpl implements Context {
     }
     return result;
   }
-
-  @Override public String toString() {
-    return "version: " + getVersion() + "; conf: " + configurationFile;
-  }
 }
diff --git a/modules/jooby-cli/src/main/resources/cli/pom.xml.hbs b/modules/jooby-cli/src/main/resources/cli/pom.xml.hbs
index 6980e9c2e..0513db089 100644
--- a/modules/jooby-cli/src/main/resources/cli/pom.xml.hbs
+++ b/modules/jooby-cli/src/main/resources/cli/pom.xml.hbs
@@ -48,16 +48,13 @@
 
   <build>
   {{#if kotlin}}
-    <sourceDirectory>${project.basedir}${file.separator}src${file.separator}main${file.separator}kotlin</sourceDirectory>
-    <testSourceDirectory>${project.basedir}${file.separator}src${file.separator}test${file.separator}kotlin</testSourceDirectory>
+    <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
+    <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
   {{/if}}
     <resources>
       <resource>
         <directory>conf</directory>
       </resource>
-      <resource>
-        <directory>src${file.separator}main${file.separator}resources</directory>
-      </resource>
     </resources>
     <plugins>
     {{#if kotlin}}
diff --git a/modules/jooby-commons-email/pom.xml b/modules/jooby-commons-email/pom.xml
index 63b1e68a6..94be443b2 100644
--- a/modules/jooby-commons-email/pom.xml
+++ b/modules/jooby-commons-email/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-conscrypt/pom.xml b/modules/jooby-conscrypt/pom.xml
index fbb884538..f5654cb07 100644
--- a/modules/jooby-conscrypt/pom.xml
+++ b/modules/jooby-conscrypt/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-distribution/pom.xml b/modules/jooby-distribution/pom.xml
index fdadfe0a8..7d200565b 100644
--- a/modules/jooby-distribution/pom.xml
+++ b/modules/jooby-distribution/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-ebean/pom.xml b/modules/jooby-ebean/pom.xml
index 4e36a8b68..d3e003540 100644
--- a/modules/jooby-ebean/pom.xml
+++ b/modules/jooby-ebean/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-flyway/pom.xml b/modules/jooby-flyway/pom.xml
index dabe282a7..ed144bffc 100644
--- a/modules/jooby-flyway/pom.xml
+++ b/modules/jooby-flyway/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-freemarker/pom.xml b/modules/jooby-freemarker/pom.xml
index 7caabf4b4..a7a491122 100644
--- a/modules/jooby-freemarker/pom.xml
+++ b/modules/jooby-freemarker/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-gradle-plugin/gradle/wrapper/gradle-wrapper.properties b/modules/jooby-gradle-plugin/gradle/wrapper/gradle-wrapper.properties
index 0dfb4f8c7..68ca99ac4 100644
--- a/modules/jooby-gradle-plugin/gradle/wrapper/gradle-wrapper.properties
+++ b/modules/jooby-gradle-plugin/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
diff --git a/modules/jooby-gradle-plugin/src/main/java/io/jooby/gradle/RunTask.java b/modules/jooby-gradle-plugin/src/main/java/io/jooby/gradle/RunTask.java
index b2a2ebfb1..e8b5e4acf 100644
--- a/modules/jooby-gradle-plugin/src/main/java/io/jooby/gradle/RunTask.java
+++ b/modules/jooby-gradle-plugin/src/main/java/io/jooby/gradle/RunTask.java
@@ -53,11 +53,6 @@ public class RunTask extends BaseTask {
 
   private Integer port;
 
-  /**
-   * How long to wait after last file change to restart. Default is: <code>500</code> milliseconds.
-   */
-  private Long waitTimeBeforeRestart;
-
   /**
    * Run task.
    *
@@ -86,9 +81,6 @@ public class RunTask extends BaseTask {
       if (restartExtensions != null) {
         config.setRestartExtensions(restartExtensions);
       }
-      if (waitTimeBeforeRestart != null) {
-        config.setWaitTimeBeforeRestart(waitTimeBeforeRestart);
-      }
       config.setProjectName(current.getName());
       getLogger().info("jooby options: {}", config);
 
@@ -263,27 +255,6 @@ public class RunTask extends BaseTask {
     this.port = port;
   }
 
-  /**
-   * How long to wait after last file change to restart. Default is: <code>500</code> milliseconds.
-   *
-   * @return How long to wait after last file change.
-   */
-  @Input
-  @org.gradle.api.tasks.Optional
-  public Long getWaitTimeBeforeRestart() {
-    return waitTimeBeforeRestart;
-  }
-
-  /**
-   * Set How long to wait after last file change to restart.
-   * Default is: <code>500</code> milliseconds.
-   *
-   * @param waitTimeBeforeRestart How long to wait after last file change to restart.
-   */
-  public void setWaitTimeBeforeRestart(Long waitTimeBeforeRestart) {
-    this.waitTimeBeforeRestart = waitTimeBeforeRestart;
-  }
-
   /**
    *
    * Shutdown without killing gradle daemon on ENTER KEY.
diff --git a/modules/jooby-gradle-setup/pom.xml b/modules/jooby-gradle-setup/pom.xml
index 9294c037b..c4450ea11 100644
--- a/modules/jooby-gradle-setup/pom.xml
+++ b/modules/jooby-gradle-setup/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -43,20 +43,6 @@
             </configuration>
 
             <executions>
-              <execution>
-                <id>compilePlugin</id>
-                <goals>
-                  <goal>exec</goal>
-                </goals>
-                <phase>compile</phase>
-                <configuration>
-                  <arguments>
-                    <argument>compileJava</argument>
-                    <argument>-PjoobyVersion=${project.version}</argument>
-                  </arguments>
-                </configuration>
-              </execution>
-
               <execution>
                 <id>publishToMavenLocal</id>
                 <goals>
diff --git a/modules/jooby-graphiql/pom.xml b/modules/jooby-graphiql/pom.xml
index 2750cf517..aacb669ab 100644
--- a/modules/jooby-graphiql/pom.xml
+++ b/modules/jooby-graphiql/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-graphql-playground/pom.xml b/modules/jooby-graphql-playground/pom.xml
index 9aa230abb..1b5968d76 100644
--- a/modules/jooby-graphql-playground/pom.xml
+++ b/modules/jooby-graphql-playground/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-graphql/pom.xml b/modules/jooby-graphql/pom.xml
index e2794db3b..87875bceb 100644
--- a/modules/jooby-graphql/pom.xml
+++ b/modules/jooby-graphql/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-gson/pom.xml b/modules/jooby-gson/pom.xml
index 4d1744c88..07d778f5f 100644
--- a/modules/jooby-gson/pom.xml
+++ b/modules/jooby-gson/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-guice/pom.xml b/modules/jooby-guice/pom.xml
index 79d018919..9df5c0be2 100644
--- a/modules/jooby-guice/pom.xml
+++ b/modules/jooby-guice/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-handlebars/pom.xml b/modules/jooby-handlebars/pom.xml
index 83e9620ef..6adf505e5 100644
--- a/modules/jooby-handlebars/pom.xml
+++ b/modules/jooby-handlebars/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-hibernate/pom.xml b/modules/jooby-hibernate/pom.xml
index 6100b73d6..e57dbf913 100644
--- a/modules/jooby-hibernate/pom.xml
+++ b/modules/jooby-hibernate/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-hikari/pom.xml b/modules/jooby-hikari/pom.xml
index 92f1b6259..6468798b6 100644
--- a/modules/jooby-hikari/pom.xml
+++ b/modules/jooby-hikari/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-http2-jetty/pom.xml b/modules/jooby-http2-jetty/pom.xml
index 1e7fcd351..68cbb7f50 100644
--- a/modules/jooby-http2-jetty/pom.xml
+++ b/modules/jooby-http2-jetty/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-http2-netty/pom.xml b/modules/jooby-http2-netty/pom.xml
index 5f9ee16ee..e2adecb20 100644
--- a/modules/jooby-http2-netty/pom.xml
+++ b/modules/jooby-http2-netty/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-http2-undertow/pom.xml b/modules/jooby-http2-undertow/pom.xml
index 86f4071a7..3d6cf22f5 100644
--- a/modules/jooby-http2-undertow/pom.xml
+++ b/modules/jooby-http2-undertow/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-jackson/pom.xml b/modules/jooby-jackson/pom.xml
index 790a2afe1..73b48401d 100644
--- a/modules/jooby-jackson/pom.xml
+++ b/modules/jooby-jackson/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-jasypt/pom.xml b/modules/jooby-jasypt/pom.xml
index ee601ecdd..0733b7d00 100644
--- a/modules/jooby-jasypt/pom.xml
+++ b/modules/jooby-jasypt/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-jdbi/pom.xml b/modules/jooby-jdbi/pom.xml
index 76b6bec6a..803c6cb45 100644
--- a/modules/jooby-jdbi/pom.xml
+++ b/modules/jooby-jdbi/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-jetty/pom.xml b/modules/jooby-jetty/pom.xml
index 9a5164e85..66f1d1280 100644
--- a/modules/jooby-jetty/pom.xml
+++ b/modules/jooby-jetty/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-jetty/src/main/java/io/jooby/internal/jetty/JettyWebSocket.java b/modules/jooby-jetty/src/main/java/io/jooby/internal/jetty/JettyWebSocket.java
index b34c06467..6b7d4432d 100644
--- a/modules/jooby-jetty/src/main/java/io/jooby/internal/jetty/JettyWebSocket.java
+++ b/modules/jooby-jetty/src/main/java/io/jooby/internal/jetty/JettyWebSocket.java
@@ -5,6 +5,20 @@
  */
 package io.jooby.internal.jetty;
 
+import io.jooby.Context;
+import io.jooby.Server;
+import io.jooby.SneakyThrows;
+import io.jooby.WebSocket;
+import io.jooby.WebSocketCloseStatus;
+import io.jooby.WebSocketConfigurer;
+import io.jooby.WebSocketMessage;
+import org.eclipse.jetty.websocket.api.CloseException;
+import org.eclipse.jetty.websocket.api.RemoteEndpoint;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.WebSocketListener;
+import org.eclipse.jetty.websocket.api.WriteCallback;
+
+import javax.annotation.Nonnull;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -13,24 +27,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.annotation.Nonnull;
-
-import org.eclipse.jetty.websocket.api.CloseException;
-import org.eclipse.jetty.websocket.api.RemoteEndpoint;
-import org.eclipse.jetty.websocket.api.Session;
-import org.eclipse.jetty.websocket.api.WebSocketListener;
-import org.eclipse.jetty.websocket.api.WriteCallback;
-
-import io.jooby.Context;
-import io.jooby.Server;
-import io.jooby.SneakyThrows;
-import io.jooby.WebSocket;
-import io.jooby.WebSocketCloseStatus;
-import io.jooby.WebSocketConfigurer;
-import io.jooby.WebSocketMessage;
 
 public class JettyWebSocket implements WebSocketListener, WebSocketConfigurer, WebSocket,
     WriteCallback {
@@ -45,9 +41,8 @@ public class JettyWebSocket implements WebSocketListener, WebSocketConfigurer, W
   private Session session;
   private WebSocket.OnConnect onConnectCallback;
   private WebSocket.OnMessage onMessageCallback;
-  private AtomicReference<WebSocket.OnClose> onCloseCallback = new AtomicReference<>();
+  private WebSocket.OnClose onCloseCallback;
   private WebSocket.OnError onErrorCallback;
-  private AtomicBoolean open = new AtomicBoolean(false);
 
   public JettyWebSocket(JettyContext ctx) {
     this.ctx = ctx;
@@ -78,7 +73,6 @@ public class JettyWebSocket implements WebSocketListener, WebSocketConfigurer, W
 
   @Override public void onWebSocketConnect(Session session) {
     try {
-      open.set(true);
       this.session = session;
       addSession(this);
       if (onConnectCallback != null) {
@@ -92,7 +86,7 @@ public class JettyWebSocket implements WebSocketListener, WebSocketConfigurer, W
   @Override public void onWebSocketError(Throwable x) {
     // should close?
     if (!isTimeout(x)) {
-      if (isOpen() && (Server.connectionLost(x) || SneakyThrows.isFatal(x))) {
+      if (Server.connectionLost(x) || SneakyThrows.isFatal(x)) {
         handleClose(WebSocketCloseStatus.SERVER_ERROR);
       }
 
@@ -137,7 +131,7 @@ public class JettyWebSocket implements WebSocketListener, WebSocketConfigurer, W
   }
 
   @Nonnull @Override public WebSocketConfigurer onClose(@Nonnull WebSocket.OnClose callback) {
-    onCloseCallback.set(callback);
+    onCloseCallback = callback;
     return this;
   }
 
@@ -156,7 +150,7 @@ public class JettyWebSocket implements WebSocketListener, WebSocketConfigurer, W
   }
 
   @Override public boolean isOpen() {
-    return open.get() && session.isOpen();
+    return session.isOpen();
   }
 
   @Nonnull @Override public WebSocket send(@Nonnull String message, boolean broadcast) {
@@ -173,8 +167,7 @@ public class JettyWebSocket implements WebSocketListener, WebSocketConfigurer, W
           onWebSocketError(x);
         }
       } else {
-        onWebSocketError(
-            new IllegalStateException("Attempt to send a message on closed web socket"));
+        onWebSocketError(new IllegalStateException("Attempt to send a message on closed web socket"));
       }
     }
     return this;
@@ -216,34 +209,14 @@ public class JettyWebSocket implements WebSocketListener, WebSocketConfigurer, W
   }
 
   private void handleClose(WebSocketCloseStatus closeStatus) {
-    WebSocket.OnClose callback = this.onCloseCallback.getAndSet(null);
-    Throwable cause = null;
-    // 1. close socket
     try {
-      if (isOpen()) {
-        open.set(false);
-        session.close(closeStatus.getCode(), closeStatus.getReason());
+      if (onCloseCallback != null) {
+        onCloseCallback.onClose(this, closeStatus);
       }
     } catch (Throwable x) {
-      cause = x;
-    }
-    // fire callback:
-    if (callback != null) {
-      try {
-        callback.onClose(this, closeStatus);
-      } catch (Throwable x) {
-        if (cause != null) {
-          x.addSuppressed(cause);
-        }
-        cause = x;
-      }
-    }
-    // clear from active sessions:
-    removeSession(this);
-
-    if (cause != null) {
-      // fire error:
-      onWebSocketError(cause);
+      onWebSocketError(x);
+    } finally {
+      removeSession(this);
     }
   }
 
diff --git a/modules/jooby-jetty/src/main/java/io/jooby/jetty/Jetty.java b/modules/jooby-jetty/src/main/java/io/jooby/jetty/Jetty.java
index 7f3a574e4..9d66ae30b 100644
--- a/modules/jooby-jetty/src/main/java/io/jooby/jetty/Jetty.java
+++ b/modules/jooby-jetty/src/main/java/io/jooby/jetty/Jetty.java
@@ -125,14 +125,12 @@ public class Jetty extends io.jooby.Server.Base {
       Optional.ofNullable(http2)
           .ifPresent(extension -> connectionFactories.addAll(extension.configure(httpConf)));
 
-      if (!options.isHttpsOnly()) {
-        ServerConnector http = new ServerConnector(server,
-            connectionFactories.toArray(new ConnectionFactory[0]));
-        http.setPort(options.getPort());
-        http.setHost(options.getHost());
+      ServerConnector http = new ServerConnector(server,
+          connectionFactories.toArray(new ConnectionFactory[0]));
+      http.setPort(options.getPort());
+      http.setHost(options.getHost());
 
-        server.addConnector(http);
-      }
+      server.addConnector(http);
 
       if (options.isSSLEnabled()) {
         SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
@@ -172,8 +170,6 @@ public class Jetty extends io.jooby.Server.Base {
         secureConnector.setHost(options.getHost());
 
         server.addConnector(secureConnector);
-      } else if (options.isHttpsOnly()) {
-        throw new IllegalArgumentException("Server configured for httpsOnly, but ssl options not set");
       }
 
       ContextHandler context = new ContextHandler();
@@ -190,14 +186,11 @@ public class Jetty extends io.jooby.Server.Base {
         context.setHandler(handler);
       }
       /* ********************************* WebSocket *************************************/
-      Config conf = application.getConfig();
-      int maxSize = conf.hasPath("websocket.maxSize")
-          ? conf.getBytes("websocket.maxSize").intValue()
-          : WebSocket.MAX_BUFFER_SIZE;
       context.setAttribute(DecoratedObjectFactory.ATTR, new DecoratedObjectFactory());
       WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
-      policy.setMaxTextMessageBufferSize(maxSize);
-      policy.setMaxTextMessageSize(maxSize);
+      policy.setMaxTextMessageBufferSize(WebSocket.MAX_BUFFER_SIZE);
+      policy.setMaxTextMessageSize(WebSocket.MAX_BUFFER_SIZE);
+      Config conf = application.getConfig();
       long timeout = conf.hasPath("websocket.idleTimeout")
           ? conf.getDuration("websocket.idleTimeout", TimeUnit.MILLISECONDS)
           : TimeUnit.MINUTES.toMillis(5);
diff --git a/modules/jooby-jwt/pom.xml b/modules/jooby-jwt/pom.xml
index b967ae155..4e41620be 100644
--- a/modules/jooby-jwt/pom.xml
+++ b/modules/jooby-jwt/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-kafka/pom.xml b/modules/jooby-kafka/pom.xml
index d6d9e5e50..16b93a0ef 100644
--- a/modules/jooby-kafka/pom.xml
+++ b/modules/jooby-kafka/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-maven-plugin/pom.xml b/modules/jooby-maven-plugin/pom.xml
index f3161b59c..a5fff201b 100644
--- a/modules/jooby-maven-plugin/pom.xml
+++ b/modules/jooby-maven-plugin/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-maven-plugin/src/main/java/io/jooby/maven/RunMojo.java b/modules/jooby-maven-plugin/src/main/java/io/jooby/maven/RunMojo.java
index 8753b1029..56975364d 100644
--- a/modules/jooby-maven-plugin/src/main/java/io/jooby/maven/RunMojo.java
+++ b/modules/jooby-maven-plugin/src/main/java/io/jooby/maven/RunMojo.java
@@ -62,12 +62,6 @@ public class RunMojo extends BaseMojo {
   @Parameter(property = "jooby.port")
   private Integer port;
 
-  /**
-   * How long to wait after last file change to restart. Default is: <code>500</code> milliseconds.
-   */
-  @Parameter(property = "jooby.waitTimeBeforeRestart")
-  private Long waitTimeBeforeRestart;
-
   @Override protected void doExecute(List<MavenProject> projects, String mainClass)
       throws Throwable {
     Maven maven = getMaven();
@@ -128,7 +122,6 @@ public class RunMojo extends BaseMojo {
       options.setCompileExtensions(compileExtensions);
     }
     options.setPort(port);
-    options.setWaitTimeBeforeRestart(waitTimeBeforeRestart);
     options.setProjectName(session.getCurrentProject().getArtifactId());
     if (restartExtensions != null) {
       options.setRestartExtensions(restartExtensions);
diff --git a/modules/jooby-metrics/pom.xml b/modules/jooby-metrics/pom.xml
index d056b699a..bdb88e8b9 100644
--- a/modules/jooby-metrics/pom.xml
+++ b/modules/jooby-metrics/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-netty/pom.xml b/modules/jooby-netty/pom.xml
index a63c89e28..2f563541a 100644
--- a/modules/jooby-netty/pom.xml
+++ b/modules/jooby-netty/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyContext.java b/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyContext.java
index 713ba5cb9..639192b43 100644
--- a/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyContext.java
+++ b/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyContext.java
@@ -373,16 +373,12 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
   @Nonnull @Override public Context upgrade(WebSocket.Initializer handler) {
     try {
       responseStarted = true;
-      Config conf = getRouter().getConfig();
-      int maxSize = conf.hasPath("websocket.maxSize")
-          ? conf.getBytes("websocket.maxSize").intValue()
-          : WebSocket.MAX_BUFFER_SIZE;
       String webSocketURL = getProtocol() + "://" + req.headers().get(HttpHeaderNames.HOST) + path;
       WebSocketDecoderConfig config = WebSocketDecoderConfig.newBuilder()
           .allowExtensions(true)
           .allowMaskMismatch(false)
           .withUTF8Validator(false)
-          .maxFramePayloadLength(maxSize)
+          .maxFramePayloadLength(WebSocket.MAX_BUFFER_SIZE)
           .build();
       webSocket = new NettyWebSocket(this);
       handler.init(Context.readOnly(this), webSocket);
@@ -393,6 +389,7 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
       WebSocketServerHandshaker handshaker = factory.newHandshaker(webSocketRequest);
       handshaker.handshake(ctx.channel(), webSocketRequest);
       webSocket.fireConnect();
+      Config conf = getRouter().getConfig();
       long timeout = conf.hasPath("websocket.idleTimeout")
           ? conf.getDuration("websocket.idleTimeout", MILLISECONDS)
           : MINUTES.toMillis(5);
@@ -546,21 +543,17 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
   }
 
   private Context send(@Nonnull ByteBuf data) {
-    try {
-      responseStarted = true;
-      setHeaders.set(CONTENT_LENGTH, Long.toString(data.readableBytes()));
-      DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status,
-          data, setHeaders, NO_TRAILING);
-      if (ctx.channel().eventLoop().inEventLoop()) {
-        needsFlush = true;
-        ctx.write(response, promise(this));
-      } else {
-        ctx.writeAndFlush(response, promise(this));
-      }
-      return this;
-    } finally {
-      requestComplete();
+    responseStarted = true;
+    setHeaders.set(CONTENT_LENGTH, Long.toString(data.readableBytes()));
+    DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status,
+        data, setHeaders, NO_TRAILING);
+    if (ctx.channel().eventLoop().inEventLoop()) {
+      needsFlush = true;
+      ctx.write(response, promise(this));
+    } else {
+      ctx.writeAndFlush(response, promise(this));
     }
+    return this;
   }
 
   public void flush() {
@@ -571,23 +564,19 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
   }
 
   @Nonnull @Override public Context send(@Nonnull ReadableByteChannel channel) {
-    try {
-      prepareChunked();
-      DefaultHttpResponse rsp = new DefaultHttpResponse(HTTP_1_1, status, setHeaders);
-      responseStarted = true;
-      int bufferSize = contentLength > 0 ? (int) contentLength : this.bufferSize;
-      ctx.channel().eventLoop().execute(() -> {
-        // Headers
-        ctx.write(rsp, ctx.voidPromise());
-        // Body
-        ctx.write(new ChunkedNioStream(channel, bufferSize), ctx.voidPromise());
-        // Finish
-        ctx.writeAndFlush(EMPTY_LAST_CONTENT, promise(this));
-      });
-      return this;
-    }finally {
-      requestComplete();
-    }
+    prepareChunked();
+    DefaultHttpResponse rsp = new DefaultHttpResponse(HTTP_1_1, status, setHeaders);
+    responseStarted = true;
+    int bufferSize = contentLength > 0 ? (int) contentLength : this.bufferSize;
+    ctx.channel().eventLoop().execute(() -> {
+      // Headers
+      ctx.write(rsp, ctx.voidPromise());
+      // Body
+      ctx.write(new ChunkedNioStream(channel, bufferSize), ctx.voidPromise());
+      // Finish
+      ctx.writeAndFlush(EMPTY_LAST_CONTENT, promise(this));
+    });
+    return this;
   }
 
   @Nonnull @Override public Context send(@Nonnull InputStream in) {
@@ -615,8 +604,6 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
       return this;
     } catch (Exception x) {
       throw SneakyThrows.propagate(x);
-    } finally {
-      requestComplete();
     }
   }
 
@@ -656,8 +643,6 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
       }
     } catch (IOException x) {
       throw SneakyThrows.propagate(x);
-    } finally {
-      requestComplete();
     }
     return this;
   }
@@ -678,29 +663,22 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
   }
 
   @Nonnull @Override public Context send(StatusCode statusCode) {
-    try {
-      setResponseCode(statusCode);
-      responseStarted = true;
-      if (!setHeaders.contains(CONTENT_LENGTH)) {
-        setHeaders.set(CONTENT_LENGTH, "0");
-      }
-      DefaultFullHttpResponse rsp = new DefaultFullHttpResponse(HTTP_1_1,
-          status, Unpooled.EMPTY_BUFFER, setHeaders,
-          NO_TRAILING);
-      ctx.writeAndFlush(rsp, promise(this));
-      return this;
-    } finally {
-      requestComplete();
+    setResponseCode(statusCode);
+    responseStarted = true;
+    if (!setHeaders.contains(CONTENT_LENGTH)) {
+      setHeaders.set(CONTENT_LENGTH, "0");
     }
-  }
-
-  void requestComplete() {
-    fireCompleteEvent();
-    ifSaveSession();
+    DefaultFullHttpResponse rsp = new DefaultFullHttpResponse(HTTP_1_1,
+        status, Unpooled.EMPTY_BUFFER, setHeaders,
+        NO_TRAILING);
+    ctx.writeAndFlush(rsp, promise(this));
+    return this;
   }
 
   @Override public void operationComplete(ChannelFuture future) {
     try {
+      fireCompleteEvent();
+      ifSaveSession();
       destroy(future.cause());
     } finally {
       if (!isKeepAlive(req)) {
@@ -777,8 +755,8 @@ public class NettyContext implements DefaultContext, ChannelFutureListener {
 
   private NettyOutputStream newOutputStream() {
     prepareChunked();
-    return new NettyOutputStream(this, ctx, bufferSize,
-        new DefaultHttpResponse(req.protocolVersion(), status, setHeaders));
+    return new NettyOutputStream(ctx, bufferSize,
+        new DefaultHttpResponse(req.protocolVersion(), status, setHeaders), this);
   }
 
   private FileUpload register(FileUpload upload) {
diff --git a/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyOutputStream.java b/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyOutputStream.java
index c5837a25d..caf4d5934 100644
--- a/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyOutputStream.java
+++ b/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyOutputStream.java
@@ -18,18 +18,16 @@ import java.io.OutputStream;
 
 public class NettyOutputStream extends OutputStream {
   private final ByteBuf buffer;
-  private final NettyContext ctx;
-  private final ChannelHandlerContext context;
+  private final ChannelHandlerContext ctx;
   private final ChannelFutureListener closeListener;
   private HttpResponse headers;
 
-  public NettyOutputStream(NettyContext ctx, ChannelHandlerContext context, int bufferSize,
-      HttpResponse headers) {
+  public NettyOutputStream(ChannelHandlerContext ctx, int bufferSize, HttpResponse headers,
+      ChannelFutureListener closeListener) {
+    this.buffer = ctx.alloc().buffer(0, bufferSize);
     this.ctx = ctx;
-    this.buffer = context.alloc().buffer(0, bufferSize);
-    this.context = context;
     this.headers = headers;
-    this.closeListener = ctx;
+    this.closeListener = closeListener;
   }
 
   @Override
@@ -67,7 +65,7 @@ public class NettyOutputStream extends OutputStream {
 
   private void writeHeaders() {
     if (headers != null) {
-      context.write(headers, context.voidPromise());
+      ctx.write(headers, ctx.voidPromise());
       headers = null;
     }
   }
@@ -81,22 +79,22 @@ public class NettyOutputStream extends OutputStream {
     if (chunkSize > 0) {
       if (listener != null) {
         if (callback == null) {
-          context.write(new DefaultHttpContent(buffer.copy()), context.voidPromise());
+          ctx.write(new DefaultHttpContent(buffer.copy()), ctx.voidPromise());
         } else {
-          context.write(new DefaultHttpContent(buffer.copy())).addListener(callback);
+          ctx.write(new DefaultHttpContent(buffer.copy())).addListener(callback);
         }
-        context.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(listener);
+        ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(listener);
         buffer.release();
       } else {
         if (callback == null) {
-          context.write(new DefaultHttpContent(buffer.copy()), context.voidPromise());
+          ctx.write(new DefaultHttpContent(buffer.copy()), ctx.voidPromise());
         } else {
-          context.write(new DefaultHttpContent(buffer.copy())).addListener(callback);
+          ctx.write(new DefaultHttpContent(buffer.copy())).addListener(callback);
         }
         buffer.clear();
       }
     } else {
-      ChannelFuture future = context.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
+      ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
       if (listener != null) {
         future.addListener(listener);
       }
@@ -105,10 +103,6 @@ public class NettyOutputStream extends OutputStream {
 
   @Override
   public void close() {
-    try {
-      flush(null, closeListener);
-    } finally {
-      ctx.requestComplete();
-    }
+    flush(null, closeListener);
   }
 }
diff --git a/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettySender.java b/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettySender.java
index d86dd0757..9cc5bfde4 100644
--- a/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettySender.java
+++ b/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettySender.java
@@ -32,7 +32,6 @@ public class NettySender implements Sender {
 
   @Override public void close() {
     context.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ctx);
-    ctx.requestComplete();
   }
 
   private static ChannelFutureListener newChannelFutureListener(NettyContext ctx,
diff --git a/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyWebSocket.java b/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyWebSocket.java
index d30f8cfd7..c05b98912 100644
--- a/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyWebSocket.java
+++ b/modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyWebSocket.java
@@ -13,8 +13,6 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
 
 import javax.annotation.Nonnull;
 
@@ -50,10 +48,9 @@ public class NettyWebSocket implements WebSocketConfigurer, WebSocket, ChannelFu
   private ByteBuf buffer;
   private WebSocket.OnConnect connectCallback;
   private WebSocket.OnMessage messageCallback;
-  private AtomicReference<OnClose> onCloseCallback = new AtomicReference<>();
+  private OnClose onCloseCallback;
   private OnError onErrorCallback;
   private CountDownLatch ready = new CountDownLatch(1);
-  private AtomicBoolean open = new AtomicBoolean(false);
 
   public NettyWebSocket(NettyContext ctx) {
     this.netty = ctx;
@@ -115,7 +112,7 @@ public class NettyWebSocket implements WebSocketConfigurer, WebSocket, ChannelFu
   }
 
   public boolean isOpen() {
-    return open.get() && netty.ctx.channel().isOpen();
+    return netty.ctx.channel().isOpen();
   }
 
   @Override public WebSocketConfigurer onConnect(WebSocket.OnConnect callback) {
@@ -129,7 +126,7 @@ public class NettyWebSocket implements WebSocketConfigurer, WebSocket, ChannelFu
   }
 
   @Override public WebSocketConfigurer onClose(WebSocket.OnClose callback) {
-    onCloseCallback.set(callback);
+    onCloseCallback = callback;
     return this;
   }
 
@@ -182,22 +179,23 @@ public class NettyWebSocket implements WebSocketConfigurer, WebSocket, ChannelFu
   }
 
   private void handleClose(WebSocketCloseStatus closeStatus) {
-    OnClose callback = onCloseCallback.getAndSet(null);
-    if (isOpen()) {
-      open.set(false);
-      // close socket:
-      netty.ctx.channel()
-          .writeAndFlush(
-              new CloseWebSocketFrame(closeStatus.getCode(), closeStatus.getReason()))
-          .addListener(ChannelFutureListener.CLOSE);
-    }
     try {
-      if (callback != null) {
-        // fire callback:
-        fireCallback(webSocketTask(() -> callback.onClose(this, closeStatus), false));
+      if (isOpen()) {
+        if (onCloseCallback != null) {
+          Runnable closeCallback = () -> {
+            try {
+              webSocketTask(() -> onCloseCallback.onClose(this, closeStatus), false).run();
+            } finally {
+              netty.ctx.channel()
+                  .writeAndFlush(
+                      new CloseWebSocketFrame(closeStatus.getCode(), closeStatus.getReason()))
+                  .addListener(ChannelFutureListener.CLOSE);
+            }
+          };
+          fireCallback(closeCallback);
+        }
       }
     } finally {
-      // clear from active sessions:
       this.netty.ctx.channel().attr(WS).set(null);
       removeSession(this);
     }
@@ -223,7 +221,6 @@ public class NettyWebSocket implements WebSocketConfigurer, WebSocket, ChannelFu
   }
 
   void fireConnect() {
-    open.set(true);
     addSession(this);
     if (connectCallback != null) {
       fireCallback(webSocketTask(() -> {
diff --git a/modules/jooby-netty/src/main/java/io/jooby/netty/Netty.java b/modules/jooby-netty/src/main/java/io/jooby/netty/Netty.java
index c40dc4afe..dabe56717 100644
--- a/modules/jooby-netty/src/main/java/io/jooby/netty/Netty.java
+++ b/modules/jooby-netty/src/main/java/io/jooby/netty/Netty.java
@@ -127,13 +127,12 @@ public class Netty extends Server.Base {
       }
 
       /** Bootstrap: */
-      if (!options.isHttpsOnly()) {
-        ServerBootstrap http = transport.configure(acceptorloop, eventloop)
-            .childHandler(newPipeline(factory, null, http2))
-            .childOption(ChannelOption.SO_REUSEADDR, true)
-            .childOption(ChannelOption.TCP_NODELAY, true);
-        http.bind(options.getHost(), options.getPort()).get();
-      }
+      ServerBootstrap http = transport.configure(acceptorloop, eventloop)
+          .childHandler(newPipeline(factory, null, http2))
+          .childOption(ChannelOption.SO_REUSEADDR, true)
+          .childOption(ChannelOption.TCP_NODELAY, true);
+
+      http.bind(options.getHost(), options.getPort()).get();
 
       if (options.isSSLEnabled()) {
         SSLContext javaSslContext = options
@@ -153,8 +152,6 @@ public class Netty extends Server.Base {
             .childOption(ChannelOption.TCP_NODELAY, true);
 
         https.bind(options.getHost(), options.getSecurePort()).get();
-      } else if (options.isHttpsOnly()) {
-        throw new IllegalArgumentException("Server configured for httpsOnly, but ssl options not set");
       }
 
       fireReady(applications);
diff --git a/modules/jooby-node/pom.xml b/modules/jooby-node/pom.xml
index 265d5d006..c560c90d9 100644
--- a/modules/jooby-node/pom.xml
+++ b/modules/jooby-node/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-openapi/pom.xml b/modules/jooby-openapi/pom.xml
index 416fef647..275da479c 100644
--- a/modules/jooby-openapi/pom.xml
+++ b/modules/jooby-openapi/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/ParserContext.java b/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/ParserContext.java
index a85ba9a7c..42712f28f 100644
--- a/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/ParserContext.java
+++ b/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/ParserContext.java
@@ -37,7 +37,6 @@ import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
diff --git a/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/ReturnTypeParser.java b/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/ReturnTypeParser.java
index f5f5a84f5..c3fa4438c 100644
--- a/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/ReturnTypeParser.java
+++ b/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/ReturnTypeParser.java
@@ -31,17 +31,13 @@ import org.objectweb.asm.tree.MethodNode;
 import org.objectweb.asm.tree.TypeInsnNode;
 import org.objectweb.asm.tree.VarInsnNode;
 
-import io.jooby.Context;
-
 public class ReturnTypeParser {
 
   public static List<String> parse(ParserContext ctx, MethodNode node) {
     Type returnType = Type.getReturnType(node.desc);
     boolean notSynthetic = (node.access & Opcodes.ACC_SYNTHETIC) == 0;
-    if (notSynthetic
-        && !TypeFactory.OBJECT.equals(returnType)
-        && !TypeFactory.VOID.equals(returnType)
-        && !TypeFactory.JOOBY.equals(returnType)) {
+    if (notSynthetic && !TypeFactory.OBJECT.equals(returnType) && !TypeFactory.VOID
+        .equals(returnType)) {
       if (node.signature == null) {
         return Collections.singletonList(ASMType.parse(returnType.getDescriptor()));
       } else {
@@ -57,166 +53,172 @@ public class ReturnTypeParser {
   }
 
   public static List<String> parseIgnoreSignature(ParserContext ctx, MethodNode node) {
+    Type returnType = Type.getReturnType(node.desc);
     List<String> result = InsnSupport.next(node.instructions.getFirst())
         .filter(it -> it.getOpcode() == Opcodes.ARETURN || it.getOpcode() == Opcodes.IRETURN
             || it.getOpcode() == Opcodes.RETURN)
-        .map(it -> handleReturnType(ctx, node, it))
-        .map(Object::toString)
-        .distinct()
-        .collect(Collectors.toList());
-    return result;
-  }
-
-  private static String handleReturnType(ParserContext ctx, MethodNode node, AbstractInsnNode it) {
-    Type returnType = Type.getReturnType(node.desc);
-
-    if (it.getOpcode() == Opcodes.RETURN) {
-      return returnType.getClassName();
-    }
-    /** IRETURN */
-    if (it.getOpcode() == Opcodes.IRETURN) {
-      if (it instanceof InsnNode) {
-        AbstractInsnNode prev = it.getPrevious();
-        if (prev instanceof IntInsnNode) {
-          return Integer.class.getName();
-        }
-        if (prev instanceof InsnNode) {
-          if (prev.getOpcode() == Opcodes.ICONST_0
-              || prev.getOpcode() == Opcodes.ICONST_1) {
-            return Boolean.class.getName();
+        .map(it -> {
+          if (it.getOpcode() == Opcodes.RETURN) {
+            return returnType.getClassName();
+          }
+          /** IRETURN */
+          if (it.getOpcode() == Opcodes.IRETURN) {
+            if (it instanceof InsnNode) {
+              AbstractInsnNode prev = it.getPrevious();
+              if (prev instanceof IntInsnNode) {
+                return Integer.class.getName();
+              }
+              if (prev instanceof InsnNode) {
+                if (prev.getOpcode() == Opcodes.ICONST_0
+                    || prev.getOpcode() == Opcodes.ICONST_1) {
+                  return Boolean.class.getName();
+                }
+              }
+            }
           }
-        }
-      }
-    }
 
-    for (Iterator<AbstractInsnNode> iterator = InsnSupport
-        .prevIterator(it.getPrevious()); iterator.hasNext(); ) {
-      AbstractInsnNode i = iterator.next();
-      if (i instanceof MethodInsnNode && (((MethodInsnNode) i).owner
-          .equals("kotlin/jvm/internal/Intrinsics"))) {
-        // skip Ldc and load var
-        // dup or aload
-        // visitLdcInsn("$receiver");
-        // visitMethodInsn(INVOKESTATIC, "kotlin/jvm/internal/Intrinsics", "checkParameterIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V", false);
-        iterator.next();
-        iterator.next();
-        continue;
-      }
-      if (i instanceof MethodInsnNode && (((MethodInsnNode) i).owner
-          .equals("kotlin/TypeCastException"))) {
-        continue;
-      }
-      if (i instanceof LineNumberNode || i instanceof LabelNode) {
-        continue;
-      }
-      /** return 1; return true; return new Foo(); */
-      if (i instanceof MethodInsnNode) {
-        MethodInsnNode minnsn = (MethodInsnNode) i;
-        if (minnsn.name.equals("<init>")) {
-          return Type.getObjectType(minnsn.owner).getClassName();
-        } else {
-          return fromMethodCall(ctx, minnsn);
-        }
-      }
-      /** return "String" | int | double */
-      if (i instanceof LdcInsnNode) {
-        Object cst = ((LdcInsnNode) i).cst;
-        if (cst instanceof Type) {
-          return ((Type) cst).getClassName();
-        }
-        return cst.getClass().getName();
-      }
+          for (Iterator<AbstractInsnNode> iterator = InsnSupport
+              .prevIterator(it.getPrevious()); iterator.hasNext(); ) {
+            AbstractInsnNode i = iterator.next();
+            if (i instanceof MethodInsnNode && (((MethodInsnNode) i).owner
+                .equals("kotlin/jvm/internal/Intrinsics"))) {
+              // skip Ldc and load var
+              // dup or aload
+              // visitLdcInsn("$receiver");
+              // visitMethodInsn(INVOKESTATIC, "kotlin/jvm/internal/Intrinsics", "checkParameterIsNotNull", "(Ljava/lang/Object;Ljava/lang/String;)V", false);
+              iterator.next();
+              iterator.next();
+              continue;
+            }
+            if (i instanceof MethodInsnNode && (((MethodInsnNode) i).owner
+                .equals("kotlin/TypeCastException"))) {
+              continue;
+            }
+            if (i instanceof LineNumberNode || i instanceof LabelNode) {
+              continue;
+            }
+            String sourcedesc = null;
+            /** return 1; return true; return new Foo(); */
+            if (i instanceof MethodInsnNode) {
+              MethodInsnNode minnsn = (MethodInsnNode) i;
+              if (minnsn.name.equals("<init>")) {
+                return Type.getObjectType(minnsn.owner).getClassName();
+              }
+              if (i.getOpcode() == Opcodes.INVOKEVIRTUAL) {
+                AbstractInsnNode invokeDynamic = InsnSupport.prev(i)
+                    .filter(InvokeDynamicInsnNode.class::isInstance)
+                    .findFirst()
+                    .orElse(null);
+                if (invokeDynamic != null) {
+                  sourcedesc = minnsn.desc;
+                  i = invokeDynamic;
+                } else {
+                  return fromMethodCall(ctx, minnsn);
+                }
+              } else {
+                return fromMethodCall(ctx, minnsn);
+              }
+            }
+            /** return "String" | int | double */
+            if (i instanceof LdcInsnNode) {
+              Object cst = ((LdcInsnNode) i).cst;
+              if (cst instanceof Type) {
+                return ((Type) cst).getClassName();
+              }
+              return cst.getClass().getName();
+            }
 
-      /** return variable */
-      if (i instanceof VarInsnNode) {
-        VarInsnNode varInsn = (VarInsnNode) i;
-        String varType = localVariable(ctx, node, varInsn);
-        // Is there local variable?
-        if (varType != null) {
-          return varType;
-        }
-      }
-      /** Invoke dynamic: */
-      if (i instanceof InvokeDynamicInsnNode) {
-        InvokeDynamicInsnNode invokeDynamic = (InvokeDynamicInsnNode) i;
-        String handleDescriptor = Stream.of(invokeDynamic.bsmArgs)
-            .filter(Handle.class::isInstance)
-            .map(Handle.class::cast)
-            .findFirst()
-            .map(h -> {
-              String desc = Type.getReturnType(h.getDesc()).getDescriptor();
-              return "V".equals(desc) ? "java/lang/Object" : desc;
-            })
-            .orElse(null);
-        String descriptor = Type
-            .getReturnType(invokeDynamic.desc)
-            .getDescriptor();
-        if (handleDescriptor != null && !handleDescriptor.equals("java/lang/Object")) {
-          if (descriptor.endsWith(";")) {
-            descriptor = descriptor.substring(0, descriptor.length() - 1);
-          }
-          descriptor += "<" + handleDescriptor + ">;";
-        }
-        return ASMType.parse(descriptor);
-      }
-      /** array literal: */
-      if (i.getOpcode() == Opcodes.NEWARRAY) {
-        // empty primitive array
-        if (i instanceof IntInsnNode) {
-          switch (((IntInsnNode) i).operand) {
-            case Opcodes.T_BOOLEAN:
-              return boolean[].class.getName();
-            case Opcodes.T_CHAR:
-              return char[].class.getName();
-            case Opcodes.T_BYTE:
-              return byte[].class.getName();
-            case Opcodes.T_SHORT:
-              return short[].class.getName();
-            case Opcodes.T_INT:
-              return int[].class.getName();
-            case Opcodes.T_LONG:
-              return long[].class.getName();
-            case Opcodes.T_FLOAT:
-              return float[].class.getName();
-            case Opcodes.T_DOUBLE:
-              return double[].class.getName();
+            /** return variable */
+            if (i instanceof VarInsnNode) {
+              VarInsnNode varInsn = (VarInsnNode) i;
+              return localVariable(ctx, node, varInsn);
+            }
+            /** Invoke dynamic: */
+            if (i instanceof InvokeDynamicInsnNode) {
+              InvokeDynamicInsnNode invokeDynamic = (InvokeDynamicInsnNode) i;
+              String handleDescriptor = Stream.of(invokeDynamic.bsmArgs)
+                  .filter(Handle.class::isInstance)
+                  .map(Handle.class::cast)
+                  .findFirst()
+                  .map(h -> {
+                    String desc = Type.getReturnType(h.getDesc()).getDescriptor();
+                    return "V".equals(desc) ? "java/lang/Object" : desc;
+                  })
+                  .orElse(null);
+              String descriptor = Type
+                  .getReturnType(Optional.ofNullable(sourcedesc).orElse(invokeDynamic.desc))
+                  .getDescriptor();
+              if (handleDescriptor != null && !handleDescriptor.equals("java/lang/Object")) {
+                if (descriptor.endsWith(";")) {
+                  descriptor = descriptor.substring(0, descriptor.length() - 1);
+                }
+                descriptor += "<" + handleDescriptor + ">;";
+              }
+              return ASMType.parse(descriptor);
+            }
+            /** array literal: */
+            if (i.getOpcode() == Opcodes.NEWARRAY) {
+              // empty primitive array
+              if (i instanceof IntInsnNode) {
+                switch (((IntInsnNode) i).operand) {
+                  case Opcodes.T_BOOLEAN:
+                    return boolean[].class.getName();
+                  case Opcodes.T_CHAR:
+                    return char[].class.getName();
+                  case Opcodes.T_BYTE:
+                    return byte[].class.getName();
+                  case Opcodes.T_SHORT:
+                    return short[].class.getName();
+                  case Opcodes.T_INT:
+                    return int[].class.getName();
+                  case Opcodes.T_LONG:
+                    return long[].class.getName();
+                  case Opcodes.T_FLOAT:
+                    return float[].class.getName();
+                  case Opcodes.T_DOUBLE:
+                    return double[].class.getName();
+                }
+              }
+            }
+            // empty array of objects
+            if (i.getOpcode() == Opcodes.ANEWARRAY) {
+              TypeInsnNode typeInsn = (TypeInsnNode) i;
+              return ASMType.parse("[L" + typeInsn.desc + ";");
+            }
+            // non empty array
+            switch (i.getOpcode()) {
+              case Opcodes.BASTORE:
+                return boolean[].class.getName();
+              case Opcodes.CASTORE:
+                return char[].class.getName();
+              case Opcodes.SASTORE:
+                return short[].class.getName();
+              case Opcodes.IASTORE:
+                return int[].class.getName();
+              case Opcodes.LASTORE:
+                return long[].class.getName();
+              case Opcodes.FASTORE:
+                return float[].class.getName();
+              case Opcodes.DASTORE:
+                return double[].class.getName();
+              case Opcodes.AASTORE:
+                return InsnSupport.prev(i)
+                    .filter(e -> e.getOpcode() == Opcodes.ANEWARRAY)
+                    .findFirst()
+                    .map(e -> {
+                      TypeInsnNode typeInsn = (TypeInsnNode) e;
+                      return ASMType.parse("[L" + typeInsn.desc + ";");
+                    })
+                    .orElse(Object.class.getName());
+            }
           }
-        }
-      }
-      // empty array of objects
-      if (i.getOpcode() == Opcodes.ANEWARRAY) {
-        TypeInsnNode typeInsn = (TypeInsnNode) i;
-        return ASMType.parse("[L" + typeInsn.desc + ";");
-      }
-      // non empty array
-      switch (i.getOpcode()) {
-        case Opcodes.BASTORE:
-          return boolean[].class.getName();
-        case Opcodes.CASTORE:
-          return char[].class.getName();
-        case Opcodes.SASTORE:
-          return short[].class.getName();
-        case Opcodes.IASTORE:
-          return int[].class.getName();
-        case Opcodes.LASTORE:
-          return long[].class.getName();
-        case Opcodes.FASTORE:
-          return float[].class.getName();
-        case Opcodes.DASTORE:
-          return double[].class.getName();
-        case Opcodes.AASTORE:
-          return InsnSupport.prev(i)
-              .filter(e -> e.getOpcode() == Opcodes.ANEWARRAY)
-              .findFirst()
-              .map(e -> {
-                TypeInsnNode typeInsn = (TypeInsnNode) e;
-                return ASMType.parse("[L" + typeInsn.desc + ";");
-              })
-              .orElse(Object.class.getName());
-      }
-    }
 
-    return returnType.getClassName();
+          return returnType.getClassName();
+        })
+        .map(Object::toString)
+        .distinct()
+        .collect(Collectors.toList());
+    return result;
   }
 
   private static String fromMethodCall(ParserContext ctx, MethodInsnNode node) {
@@ -237,13 +239,8 @@ public class ReturnTypeParser {
       return Object.class.getName();
     }
     Type returnType = Type.getReturnType(node.desc);
-    // Since Kotlin 1.6+
-    String methodName = node.name.startsWith("access$invoke$")
-        ? node.name.substring("access$".length())
-        : node.name;
-    List<MethodNode> methodNodes = classMethods(ctx, node.owner);
-    return methodNodes.stream()
-        .filter(m -> m.name.equals(methodName) && m.desc.equals(node.desc))
+    return classMethods(ctx, node.owner).stream()
+        .filter(m -> m.name.equals(node.name) && m.desc.equals(node.desc))
         .findFirst()
         .map(m -> Optional.ofNullable(m.signature)
             .map(s -> {
@@ -324,31 +321,17 @@ public class ReturnTypeParser {
                 String returnType = fromMethodCall(ctx, methodCall);
                 if (!returnType.equals(Object.class.getName()) && !returnType
                     .equals(void.class.getName())) {
-                  type = returnType;
+                  return returnType;
                 }
               }
             }
           }
-          if (type.equals(Context.class.getName())) {
-            // No var, look for last STORE matching index var
-            VarInsnNode store = InsnSupport.prev(varInsn.getPrevious())
-                .filter(
-                    it -> (it.getOpcode() >= Opcodes.ISTORE && it.getOpcode() <= Opcodes.SASTORE))
-                .filter(VarInsnNode.class::isInstance)
-                .map(VarInsnNode.class::cast)
-                .filter(it -> it.var == varInsn.var)
-                .findFirst()
-                .orElse(null);
-            if (store != null) {
-              type = handleReturnType(ctx, m, store);
-            }
-          }
           return type;
         }
         return ASMType.parse(var.signature);
       }
     }
-    return null;//Object.class.getName();
+    return Object.class.getName();
   }
 
   private static Predicate<AbstractInsnNode> kotlinIntrinsics() {
diff --git a/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/RouteParser.java b/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/RouteParser.java
index d6fd10f3f..53328992c 100644
--- a/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/RouteParser.java
+++ b/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/RouteParser.java
@@ -19,7 +19,6 @@ import java.io.IOException;
 import java.lang.reflect.Modifier;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -75,13 +74,6 @@ public class RouteParser {
   public List<OperationExt> parse(ParserContext ctx) {
     List<OperationExt> operations = parse(ctx, null, ctx.classNode(ctx.getRouter()));
 
-    // Checkout controllers without explicit mapping, just META-INF
-    Set<String> controllers = operations.stream()
-        .map(OperationExt::getControllerName)
-        .filter(Objects::nonNull)
-        .collect(Collectors.toSet());
-    operations.addAll(metaInf(ctx, null, name -> !controllers.contains(name)));
-
     String applicationName = Optional.ofNullable(ctx.getMainClass())
         .orElse(ctx.getRouter().getClassName());
     ClassNode application = ctx.classNode(Type.getObjectType(applicationName.replace(".", "/")));
@@ -281,6 +273,12 @@ public class RouteParser {
     for (MethodNode method : node.methods) {
       handlerList.addAll(routeHandler(ctx, prefix, method));
     }
+    Set<String> controllers = handlerList.stream()
+        .map(OperationExt::getControllerName)
+        .filter(Objects::nonNull)
+        .collect(Collectors.toSet());
+
+    handlerList.addAll(metaInf(ctx, prefix, name -> !controllers.contains(name)));
     return handlerList;
   }
 
@@ -668,124 +666,69 @@ public class RouteParser {
   private List<OperationExt> kotlinHandler(ParserContext ctx, String httpMethod,
       String prefix, MethodInsnNode node) {
     List<OperationExt> handlerList = new ArrayList<>();
-    // [0] - Owner
-    // [1] - Method name. Optional
-    // [2] - Method descriptor. Optional
-    List<String> lookup = InsnSupport.prev(node.getPrevious())
-        .map(it -> {
-          if (it instanceof InvokeDynamicInsnNode) {
-            InvokeDynamicInsnNode invokeDynamic = (InvokeDynamicInsnNode) it;
-            Object[] args = invokeDynamic.bsmArgs;
-            if (args.length > 1 && args[1] instanceof Handle) {
-              Handle handle = (Handle) args[1];
-              return Arrays.asList(handle.getOwner(), handle.getName(), handle.getDesc());
-            }
-          }
+    String owner = InsnSupport.prev(node.getPrevious())
+        .filter(it -> {
           if (it instanceof FieldInsnNode) {
-            return Collections.singletonList(((FieldInsnNode) it).owner);
+            return true;
           }
           if (it instanceof MethodInsnNode) {
-            Signature signature = Signature.create((MethodInsnNode) it);
-            if (!signature.matches("<init>", KT_FUN_1)) {
-              return Collections.singletonList(((MethodInsnNode) it).owner);
-            }
+            return !Signature.create((MethodInsnNode) it).matches("<init>", KT_FUN_1);
           }
-          return null;
+          return false;
         })
-        .filter(Objects::nonNull)
         .findFirst()
+        .map(e -> {
+          if (e instanceof FieldInsnNode) {
+            return ((FieldInsnNode) e).owner;
+          }
+          return ((MethodInsnNode) e).owner;
+        })
         .orElseThrow(() -> new IllegalStateException(
             "Kotlin lambda not found: " + InsnSupport.toString(node)));
 
-    ClassNode classNode = ctx.classNode(Type.getObjectType(lookup.get(0)));
+    ClassNode classNode = ctx.classNode(Type.getObjectType(owner));
     MethodNode apply = null;
-    if (lookup.size() > 1) {
-      MethodNode method = classNode.methods.stream()
-          .filter(it -> it.name.equals(lookup.get(1)) && it.desc.equals(lookup.get(2)))
-          .findFirst()
-          .orElseThrow(() -> new IllegalStateException(
-              "Kotlin lambda not found: " + InsnSupport.toString(node)));
-      ctx.debugHandlerLink(method);
-      boolean synthetic = (method.access & Opcodes.ACC_PRIVATE) != 0;
-      if (synthetic && method.name.startsWith("invoke$")) {
-        method = ktFunRef160(ctx, method);
-      }
-      if (httpMethod == null) {
+    for (MethodNode method : classNode.methods) {
+      Signature signature = Signature.create(method);
+      if (signature.matches("invoke", TypeFactory.KOOBY)) {
+        ctx.debugHandlerLink(method);
         handlerList.addAll(routeHandler(ctx, prefix, method));
-      } else {
+      } else if (signature.matches("invoke", TypeFactory.COROUTINE_ROUTER)) {
+        ctx.debugHandlerLink(method);
+        handlerList.addAll(routeHandler(ctx, prefix, method));
+      } else if (signature.matches("invoke", TypeFactory.HANDLER_CONTEXT)) {
+        ctx.debugHandler(method);
         handlerList.add(newRouteDescriptor(ctx, method, httpMethod, prefix));
-      }
-    } else {
-      for (MethodNode method : classNode.methods) {
-        Signature signature = Signature.create(method);
-        if (signature.matches("invoke", TypeFactory.KOOBY)) {
-          ctx.debugHandlerLink(method);
-          handlerList.addAll(routeHandler(ctx, prefix, method));
-        } else if (signature.matches("invoke", TypeFactory.COROUTINE_ROUTER)) {
-          ctx.debugHandlerLink(method);
-          handlerList.addAll(routeHandler(ctx, prefix, method));
-        } else if (signature.matches("invoke", TypeFactory.HANDLER_CONTEXT)) {
-          ctx.debugHandler(method);
-          handlerList.add(newRouteDescriptor(ctx, method, httpMethod, prefix));
-        } else if (signature.matches("invoke", TypeFactory.CONTEXT)) {
-          // fun reference
-          MethodNode ref = kotlinFunctionReference(ctx, classNode, method);
-          ctx.debugHandler(ref);
-          handlerList.add(newRouteDescriptor(ctx, ref, httpMethod, prefix));
-        } else if (signature.matches("invokeSuspend", Object.class)) {
-          ctx.debugHandler(method);
-          handlerList.add(newRouteDescriptor(ctx, method, httpMethod, prefix));
-        } else if (signature.matches("apply", TypeFactory.CONTEXT)) {
-          if (apply == null) {
+      } else if (signature.matches("invoke", TypeFactory.CONTEXT)) {
+        // fun reference
+        MethodNode ref = kotlinFunctionReference(ctx, classNode, method);
+        ctx.debugHandler(ref);
+        handlerList.add(newRouteDescriptor(ctx, ref, httpMethod, prefix));
+      } else if (signature.matches("invokeSuspend", Object.class)) {
+        ctx.debugHandler(method);
+        handlerList.add(newRouteDescriptor(ctx, method, httpMethod, prefix));
+      } else if (signature.matches("apply", TypeFactory.CONTEXT)) {
+        if (apply == null) {
+          apply = method;
+        } else {
+          // Should be a more specific apply method
+          if (returnTypePrecedence(method) > returnTypePrecedence(apply)) {
             apply = method;
-          } else {
-            // Should be a more specific apply method
-            if (returnTypePrecedence(method) > returnTypePrecedence(apply)) {
-              apply = method;
-            }
           }
-        } else if (signature.matches("run")) {
-          ctx.debugHandlerLink(method);
-          handlerList.addAll(routeHandler(ctx, prefix, method));
         }
+      } else if (signature.matches("run")) {
+        ctx.debugHandlerLink(method);
+        handlerList.addAll(routeHandler(ctx, prefix, method));
       }
     }
     if (apply != null) {
       // almost there can be one of two: 1) lambda itself or 2) method reference
-      Signature signature = Signature.create(node);
-      if (signature.matches(String.class, Route.Handler.class)) {
-        apply = ktFunRef160(ctx, apply);
-      }
       ctx.debugHandler(apply);
       handlerList.add(newRouteDescriptor(ctx, apply, httpMethod, prefix));
     }
     return handlerList;
   }
 
-  private MethodNode ktFunRef160(ParserContext ctx, MethodNode method) {
-    AbstractInsnNode ref = InsnSupport.prev(method.instructions.getLast())
-        .filter(MethodInsnNode.class::isInstance)
-        .map(MethodInsnNode.class::cast)
-        .filter(it -> Signature.create(it).matches(Context.class))
-        .findFirst()
-        .orElse(null);
-    if (ref != null) {
-      MethodInsnNode call = (MethodInsnNode) ref;
-      ClassNode owner = ctx.classNodeOrNull(Type.getObjectType(call.owner));
-      if (owner != null) {
-        MethodNode methodRef = owner.methods.stream()
-            .filter(it -> it.name.equals(call.name) && it.desc.equals(call.desc))
-            .findFirst()
-            .orElse(null);
-        if (methodRef != null) {
-          return methodRef;
-        }
-      }
-    }
-    // fallback to what we found previously
-    return method;
-  }
-
   private MethodNode kotlinFunctionReference(ParserContext ctx, ClassNode classNode,
       MethodNode node) {
     MethodInsnNode ref = InsnSupport.prev(node.instructions.getLast())
@@ -824,8 +767,7 @@ public class RouteParser {
     OperationExt operation = new OperationExt(node, httpMethod, prefix, arguments, response);
 
     boolean notSynthetic = (node.access & Opcodes.ACC_SYNTHETIC) == 0;
-    boolean lambda =
-        node.name.equals("apply") || node.name.equals("invoke") || node.name.startsWith("invoke$");
+    boolean lambda = node.name.equals("apply") || node.name.equals("invoke");
     if (notSynthetic && !lambda) {
       operation.setOperationId(node.name);
     }
@@ -873,7 +815,7 @@ public class RouteParser {
       return contextReference(handle);
     } else {
       return ctx.classNode(owner).methods.stream()
-          .filter(n -> n.name.equals(handle.getName()) && n.desc.equals(handle.getDesc()))
+          .filter(n -> n.name.equals(handle.getName()))
           .findFirst()
           .orElseThrow(() ->
               new IllegalStateException("Handler not found: " + InsnSupport.toString(node))
diff --git a/modules/jooby-pac4j/pom.xml b/modules/jooby-pac4j/pom.xml
index 6f605c7b5..d33c85ae9 100644
--- a/modules/jooby-pac4j/pom.xml
+++ b/modules/jooby-pac4j/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-pebble/pom.xml b/modules/jooby-pebble/pom.xml
index 45a558257..ccb344ff7 100644
--- a/modules/jooby-pebble/pom.xml
+++ b/modules/jooby-pebble/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-quartz/pom.xml b/modules/jooby-quartz/pom.xml
index 2a87e6519..d6466a775 100644
--- a/modules/jooby-quartz/pom.xml
+++ b/modules/jooby-quartz/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-redis/pom.xml b/modules/jooby-redis/pom.xml
index 9640a9b3b..99641e1dd 100644
--- a/modules/jooby-redis/pom.xml
+++ b/modules/jooby-redis/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -54,7 +54,7 @@
     <dependency>
       <groupId>io.lettuce</groupId>
       <artifactId>lettuce-core</artifactId>
-      <version>6.1.8.RELEASE</version>
+      <version>6.1.6.RELEASE</version>
     </dependency>
 
     <!-- Test dependencies -->
diff --git a/modules/jooby-redoc/pom.xml b/modules/jooby-redoc/pom.xml
index d0c86947f..4620523f5 100644
--- a/modules/jooby-redoc/pom.xml
+++ b/modules/jooby-redoc/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-rocker/pom.xml b/modules/jooby-rocker/pom.xml
index e57fa947b..363527dec 100644
--- a/modules/jooby-rocker/pom.xml
+++ b/modules/jooby-rocker/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-run/pom.xml b/modules/jooby-run/pom.xml
index cc6a91e54..88a072d32 100644
--- a/modules/jooby-run/pom.xml
+++ b/modules/jooby-run/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -30,7 +30,7 @@
     <dependency>
       <groupId>io.methvin</groupId>
       <artifactId>directory-watcher</artifactId>
-      <version>0.15.1</version>
+      <version>0.15.0</version>
     </dependency>
 
 
diff --git a/modules/jooby-run/src/main/java/io/jooby/run/JoobyRun.java b/modules/jooby-run/src/main/java/io/jooby/run/JoobyRun.java
index 732ba0a79..57fa29b79 100644
--- a/modules/jooby-run/src/main/java/io/jooby/run/JoobyRun.java
+++ b/modules/jooby-run/src/main/java/io/jooby/run/JoobyRun.java
@@ -19,18 +19,12 @@ import java.lang.reflect.InvocationTargetException;
 import java.nio.file.ClosedWatchServiceException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.time.Clock;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
 
 /**
@@ -45,13 +39,6 @@ import java.util.function.BiConsumer;
  */
 public class JoobyRun {
 
-  private static class Event {
-    private final long time;
-    Event(long time) {
-      this.time = time;
-    }
-  }
-
   private static class ExtModuleLoader extends ModuleLoader {
 
     ExtModuleLoader(ModuleFinder... finders) {
@@ -70,13 +57,6 @@ public class JoobyRun {
     private Module module;
     private ClassLoader contextClassLoader;
     private int counter;
-    private final AtomicInteger state = new AtomicInteger(CLOSED);
-    private static final int CLOSED = 1 << 0;
-    private static final int UNLOADING = 1 << 1;
-    private static final int UNLOADED = 1 << 2;
-    private static final int STARTING = 1 << 3;
-    private static final int RESTART = 1 << 4;
-    private static final int RUNNING = 1 << 5;
 
     AppModule(Logger logger, ExtModuleLoader loader, ClassLoader contextClassLoader,
         JoobyRunOptions conf) {
@@ -87,11 +67,6 @@ public class JoobyRun {
     }
 
     public Exception start() {
-      if (!(state.compareAndSet(CLOSED, STARTING)
-          || state.compareAndSet(UNLOADED, STARTING))) {
-        debugState("Jooby already starting.");
-        return null;
-      }
       try {
         module = loader.loadModule(conf.getProjectName());
         ModuleClassLoader classLoader = module.getClassLoader();
@@ -127,9 +102,6 @@ public class JoobyRun {
       } catch (Throwable x) {
         printErr(x);
       } finally {
-        if (state.compareAndSet(STARTING, RUNNING)) {
-          debugState("Jooby is now");
-        }
         Thread.currentThread().setContextClassLoader(contextClassLoader);
       }
       // In theory: application started successfully, then something went wrong. Still, users
@@ -167,19 +139,10 @@ public class JoobyRun {
           || cause instanceof VirtualMachineError;
     }
 
-    public boolean isStarting() {
-      long s = state.longValue();
-      return s > CLOSED && s < RUNNING;
-    }
-
     public void restart() {
-      if (state.compareAndSet(RUNNING, RESTART)) {
-        closeServer();
-        unloadModule();
-        start();
-      } else {
-        debugState("Already restarting.");
-      }
+      closeServer();
+      unloadModule();
+      start();
     }
 
     public void close() {
@@ -197,10 +160,6 @@ public class JoobyRun {
     }
 
     private void unloadModule() {
-      if (!state.compareAndSet(CLOSED, UNLOADING)) {
-        debugState("Cannot unload as server isn't closed.");
-        return;
-      }
       try {
         if (module != null) {
           loader.unload(conf.getProjectName(), module);
@@ -208,54 +167,20 @@ public class JoobyRun {
       } catch (Exception x) {
         logger.debug("unload module resulted in exception", x);
       } finally {
-        state.compareAndSet(UNLOADING, UNLOADED);
         module = null;
       }
     }
 
     private void closeServer() {
       try {
-        debugState("Closing server.");
-        Class<?> ref = module.getClassLoader().loadClass(SERVER_REF);
+        Class ref = module.getClassLoader().loadClass(SERVER_REF);
         ref.getDeclaredMethod(SERVER_REF_STOP).invoke(null);
       } catch (Exception x) {
         logger.error("Application shutdown resulted in exception", withoutReflection(x));
-      } finally {
-        state.set(CLOSED);
-      }
-    }
-
-    private void debugState(String message) {
-      if (logger.isDebugEnabled()) {
-        String name;
-        switch (state.get()) {
-          case CLOSED:
-            name = "CLOSED";
-            break;
-          case UNLOADING:
-            name = "UNLOADING";
-            break;
-          case UNLOADED:
-            name = "UNLOADED";
-            break;
-          case STARTING:
-            name = "STARTING";
-            break;
-          case RESTART:
-            name = "RESTART";
-            break;
-          case RUNNING:
-            name = "RUNNING";
-            break;
-          default:
-            throw new IllegalStateException("BUG");
-        }
-        logger.debug(message + " state: {}", name);
       }
     }
   }
 
-
   static final String SERVER_REF = "io.jooby.run.ServerRef";
 
   static final String SERVER_REF_STOP = "stop";
@@ -274,17 +199,6 @@ public class JoobyRun {
 
   private AppModule module;
 
-  private final Clock clock;
-
-  private final ConcurrentLinkedQueue<Event> queue = new ConcurrentLinkedQueue<>();
-
-  /*
-   * How long we wait after the last change before restart
-   */
-  private final long waitTimeBeforeRestartMillis;
-
-  private final long initialDelayBeforeFirstRestartMillis;
-
   /**
    * Creates a new instances with the given options.
    *
@@ -292,10 +206,6 @@ public class JoobyRun {
    */
   public JoobyRun(JoobyRunOptions options) {
     this.options = options;
-    clock = Clock.systemUTC(); // Possibly change for unit test
-    waitTimeBeforeRestartMillis = options.getWaitTimeBeforeRestart();
-    // this might not need to be configurable
-    initialDelayBeforeFirstRestartMillis = JoobyRunOptions.INITIAL_DELAY_BEFORE_FIRST_RESTART;
   }
 
   /**
@@ -340,30 +250,20 @@ public class JoobyRun {
    *
    * @throws Throwable If something goes wrong.
    */
-  @SuppressWarnings("FutureReturnValueIgnored")
   public void start() throws Throwable {
     this.watcher = newWatcher();
     try {
-
       logger.debug("project: {}", toString());
 
-      ModuleFinder[] finders =
-          {new FlattenClasspath(options.getProjectName(), resources, dependencies)};
+      ModuleFinder[] finders = {
+          new FlattenClasspath(options.getProjectName(), resources, dependencies)};
 
       ExtModuleLoader loader = new ExtModuleLoader(finders);
-      module =
-          new AppModule(logger, loader, Thread.currentThread().getContextClassLoader(), options);
-      ScheduledExecutorService se;
+      module = new AppModule(logger, loader, Thread.currentThread().getContextClassLoader(),
+          options);
       Exception error = module.start();
       if (error == null) {
-        se = Executors.newScheduledThreadPool(1);
-        se.scheduleAtFixedRate(this::actualRestart, initialDelayBeforeFirstRestartMillis,
-            waitTimeBeforeRestartMillis, TimeUnit.MILLISECONDS);
-        try {
-          watcher.watch();
-        } finally {
-          se.shutdownNow();
-        }
+        watcher.watch();
       } else {
         // exit
         shutdown();
@@ -378,25 +278,7 @@ public class JoobyRun {
    * Restart the application.
    */
   public void restart() {
-    queue.offer(new Event(clock.millis()));
-  }
-
-  private synchronized void actualRestart() {
-    if (module.isStarting()) {
-      return; // We don't empty the queue. This is the case a change was made while starting.
-    }
-    long t = clock.millis();
-    Event e = queue.peek();
-    if (e == null) {
-      return; // queue was empty
-    }
-    for (; e != null && (t - e.time) > waitTimeBeforeRestartMillis; e = queue.peek()) {
-      queue.poll();
-    }
-    // e will be null if the queue is empty which means all events were old enough
-    if (e == null) {
-      module.restart();
-    }
+    module.restart();
   }
 
   /**
diff --git a/modules/jooby-run/src/main/java/io/jooby/run/JoobyRunOptions.java b/modules/jooby-run/src/main/java/io/jooby/run/JoobyRunOptions.java
index 956f3ba69..47455f437 100644
--- a/modules/jooby-run/src/main/java/io/jooby/run/JoobyRunOptions.java
+++ b/modules/jooby-run/src/main/java/io/jooby/run/JoobyRunOptions.java
@@ -28,12 +28,6 @@ public class JoobyRunOptions {
 
   private Integer port = null;
 
-  private Long waitTimeBeforeRestart = DEFAULT_WAIT_TIME_BEFORE_RESTART;
-
-  private static final long DEFAULT_WAIT_TIME_BEFORE_RESTART = 500L;
-
-  static final long INITIAL_DELAY_BEFORE_FIRST_RESTART = 5000L;
-
   /**
    * Project name.
    *
@@ -88,26 +82,6 @@ public class JoobyRunOptions {
     this.port = port;
   }
 
-  /**
-   * How long to wait after last file change to restart. Default is: <code>500</code> milliseconds.
-   *
-   * @return Wait time in milliseconds.
-   */
-  public Long getWaitTimeBeforeRestart() {
-    return waitTimeBeforeRestart;
-  }
-
-  /**
-   * Set wait time before restart on file change.
-   *
-   * @param waitTimeBeforeRestart the time in milliseconds.
-   */
-  public void setWaitTimeBeforeRestart(Long waitTimeBeforeRestart) {
-    if (waitTimeBeforeRestart != null) {
-      this.waitTimeBeforeRestart = waitTimeBeforeRestart;
-    }
-  }
-
   /**
    * List of file extensions that trigger an application restart. Default is: <code>conf</code>,
    * <code>properties</code> and <code>class</code>.
diff --git a/modules/jooby-spring/pom.xml b/modules/jooby-spring/pom.xml
index dfffd3182..ed23b503c 100644
--- a/modules/jooby-spring/pom.xml
+++ b/modules/jooby-spring/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-stork/pom.xml b/modules/jooby-stork/pom.xml
index 2eff97177..484cefc00 100644
--- a/modules/jooby-stork/pom.xml
+++ b/modules/jooby-stork/pom.xml
@@ -5,7 +5,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>jooby-stork</artifactId>
diff --git a/modules/jooby-swagger-ui/pom.xml b/modules/jooby-swagger-ui/pom.xml
index 81c34084e..8c1fd15bc 100644
--- a/modules/jooby-swagger-ui/pom.xml
+++ b/modules/jooby-swagger-ui/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-thymeleaf/pom.xml b/modules/jooby-thymeleaf/pom.xml
index e208d5c20..df8ab16e8 100644
--- a/modules/jooby-thymeleaf/pom.xml
+++ b/modules/jooby-thymeleaf/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-utow/pom.xml b/modules/jooby-utow/pom.xml
index 56ef46bee..cff3022b7 100644
--- a/modules/jooby-utow/pom.xml
+++ b/modules/jooby-utow/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -26,13 +26,6 @@
     </dependency>
 
     <!-- utow -->
-    <dependency>
-      <!-- remove xnio once we upgrade to 2.2.18 +  There is a Invalida Path on Windows-->
-      <groupId> org.jboss.xnio</groupId>
-      <artifactId>xnio-api</artifactId>
-      <version>3.8.7.Final</version>
-    </dependency>
-
     <dependency>
       <groupId>io.undertow</groupId>
       <artifactId>undertow-core</artifactId>
diff --git a/modules/jooby-utow/src/main/java/io/jooby/internal/utow/UtowWebSocket.java b/modules/jooby-utow/src/main/java/io/jooby/internal/utow/UtowWebSocket.java
index 666e03579..dde109d29 100644
--- a/modules/jooby-utow/src/main/java/io/jooby/internal/utow/UtowWebSocket.java
+++ b/modules/jooby-utow/src/main/java/io/jooby/internal/utow/UtowWebSocket.java
@@ -5,26 +5,6 @@
  */
 package io.jooby.internal.utow;
 
-import static io.undertow.websockets.core.WebSockets.sendClose;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import javax.annotation.Nonnull;
-
-import org.xnio.IoUtils;
-
 import com.typesafe.config.Config;
 import io.jooby.Context;
 import io.jooby.Server;
@@ -40,6 +20,19 @@ import io.undertow.websockets.core.WebSocketCallback;
 import io.undertow.websockets.core.WebSocketChannel;
 import io.undertow.websockets.core.WebSockets;
 
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 public class UtowWebSocket extends AbstractReceiveListener
     implements WebSocketConfigurer, WebSocket, WebSocketCallback<Void> {
 
@@ -51,31 +44,24 @@ public class UtowWebSocket extends AbstractReceiveListener
   private final boolean dispatch;
   private OnConnect onConnectCallback;
   private OnMessage onMessageCallback;
-  private AtomicReference<OnClose> onCloseCallback = new AtomicReference<>();
+  private OnClose onCloseCallback;
   private OnError onErrorCallback;
   private String key;
   private CountDownLatch ready = new CountDownLatch(1);
-  private AtomicBoolean open = new AtomicBoolean(false);
-  private int maxSize;
 
   public UtowWebSocket(UtowContext ctx, WebSocketChannel channel) {
     this.ctx = ctx;
     this.channel = channel;
     this.dispatch = !ctx.isInIoThread();
     this.key = ctx.getRoute().getPattern();
-
-    Config conf = ctx.getRouter().getConfig();
-    maxSize = conf.hasPath("websocket.maxSize")
-        ? conf.getBytes("websocket.maxSize").intValue()
-        : WebSocket.MAX_BUFFER_SIZE;
   }
 
   @Override protected long getMaxTextBufferSize() {
-    return maxSize;
+    return MAX_BUFFER_SIZE;
   }
 
   @Override protected long getMaxBinaryBufferSize() {
-    return maxSize;
+    return MAX_BUFFER_SIZE;
   }
 
   @Nonnull @Override public Context getContext() {
@@ -93,7 +79,7 @@ public class UtowWebSocket extends AbstractReceiveListener
   }
 
   @Override public boolean isOpen() {
-    return open.get() && channel.isOpen();
+    return channel.isOpen();
   }
 
   @Nonnull @Override public WebSocket send(@Nonnull String message, boolean broadcast) {
@@ -156,14 +142,13 @@ public class UtowWebSocket extends AbstractReceiveListener
   }
 
   @Nonnull @Override public WebSocketConfigurer onClose(@Nonnull OnClose callback) {
-    onCloseCallback.set(callback);
+    onCloseCallback = callback;
     return this;
   }
 
   void fireConnect() {
     // fire only once
     try {
-      open.set(true);
       addSession(this);
       Config conf = ctx.getRouter().getConfig();
       long timeout = conf.hasPath("websocket.idleTimeout")
@@ -213,7 +198,7 @@ public class UtowWebSocket extends AbstractReceiveListener
   @Override protected void onError(WebSocketChannel channel, Throwable x) {
     // should close?
     if (Server.connectionLost(x) || SneakyThrows.isFatal(x)) {
-      if (isOpen()) {
+      if (channel.isOpen()) {
         handleClose(WebSocketCloseStatus.SERVER_ERROR);
       }
     }
@@ -237,42 +222,20 @@ public class UtowWebSocket extends AbstractReceiveListener
 
   @Override protected void onCloseMessage(CloseMessage cm,
       WebSocketChannel channel) {
-    if (isOpen()) {
+    if (channel.isOpen()) {
       handleClose(WebSocketCloseStatus.valueOf(cm.getCode())
           .orElseGet(() -> new WebSocketCloseStatus(cm.getCode(), cm.getReason())));
     }
   }
 
-  private void handleClose(WebSocketCloseStatus status) {
-    OnClose callback = onCloseCallback.getAndSet(null);
-    if (isOpen()) {
-      open.set(false);
-      // close socket:
-      sendClose(status.getCode(), status.getReason(), channel,
-          new WebSocketCallback<UtowWebSocket>() {
-            @Override
-            public void onError(final WebSocketChannel channel, final UtowWebSocket ws,
-                final Throwable throwable) {
-              IoUtils.safeClose(channel);
-              ws.onError(channel, throwable);
-            }
-
-            @Override
-            public void complete(final WebSocketChannel channel, final UtowWebSocket ws) {
-              IoUtils.safeClose(channel);
-            }
-          }, this);
-    }
+  private void handleClose(WebSocketCloseStatus closeStatus) {
     try {
-      // fire callback:
-      if (callback != null) {
-        callback.onClose(this, status);
+      if (onCloseCallback != null) {
+        onCloseCallback.onClose(this, closeStatus);
       }
     } catch (Throwable x) {
-      // fire error:
       onError(channel, x);
     } finally {
-      // clear from active sessions:
       removeSession(this);
     }
   }
diff --git a/modules/jooby-utow/src/main/java/io/jooby/utow/Utow.java b/modules/jooby-utow/src/main/java/io/jooby/utow/Utow.java
index ba196c094..1d782b35a 100644
--- a/modules/jooby-utow/src/main/java/io/jooby/utow/Utow.java
+++ b/modules/jooby-utow/src/main/java/io/jooby/utow/Utow.java
@@ -61,8 +61,7 @@ public class Utow extends Server.Base {
       .setIoThreads(ServerOptions.IO_THREADS)
       .setServer("utow");
 
-  @Nonnull
-  @Override public Utow setOptions(@Nonnull ServerOptions options) {
+  @Nonnull @Override public Utow setOptions(@Nonnull ServerOptions options) {
     this.options = options
         .setIoThreads(options.getIoThreads());
     return this;
@@ -94,6 +93,7 @@ public class Utow extends Server.Base {
       }
 
       Undertow.Builder builder = Undertow.builder()
+          .addHttpListener(options.getPort(), options.getHost())
           .setBufferSize(options.getBufferSize())
           /** Socket : */
           .setSocketOption(Options.BACKLOG, BACKLOG)
@@ -110,14 +110,10 @@ public class Utow extends Server.Base {
           .setWorkerThreads(options.getWorkerThreads())
           .setHandler(handler);
 
-      if (!options.isHttpsOnly()) {
-        builder.addHttpListener(options.getPort(), options.getHost());
-      }
-
       if (options.isHttp2() == null || options.isHttp2() == Boolean.TRUE) {
         stream(spliteratorUnknownSize(
-                ServiceLoader.load(Http2Configurer.class).iterator(),
-                Spliterator.ORDERED),
+            ServiceLoader.load(Http2Configurer.class).iterator(),
+            Spliterator.ORDERED),
             false
         )
             .filter(it -> it.support(Undertow.Builder.class))
@@ -138,8 +134,6 @@ public class Utow extends Server.Base {
             .map(this::toSslClientAuthMode)
             .ifPresent(
                 clientAuth -> builder.setSocketOption(Options.SSL_CLIENT_AUTH_MODE, clientAuth));
-      } else if (options.isHttpsOnly()) {
-        throw new IllegalArgumentException("Server configured for httpsOnly, but ssl options not set");
       }
 
       server = builder.build();
diff --git a/modules/jooby-weld/pom.xml b/modules/jooby-weld/pom.xml
index 5a5c9dc52..7fcf01642 100644
--- a/modules/jooby-weld/pom.xml
+++ b/modules/jooby-weld/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/jooby-whoops/pom.xml b/modules/jooby-whoops/pom.xml
index 4e32d3d2e..d089807e2 100644
--- a/modules/jooby-whoops/pom.xml
+++ b/modules/jooby-whoops/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -42,7 +42,7 @@
     <dependency>
       <groupId>org.jsoup</groupId>
       <artifactId>jsoup</artifactId>
-      <version>1.15.3</version>
+      <version>1.14.2</version>
       <scope>test</scope>
     </dependency>
 
diff --git a/modules/jooby-yasson/pom.xml b/modules/jooby-yasson/pom.xml
index fd7b949db..34e9f992f 100644
--- a/modules/jooby-yasson/pom.xml
+++ b/modules/jooby-yasson/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/modules/pom.xml b/modules/pom.xml
index d73a3eef8..d695a5dbf 100644
--- a/modules/pom.xml
+++ b/modules/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>jooby-project</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <artifactId>modules</artifactId>
diff --git a/pom.xml b/pom.xml
index 56dae1ead..e5a87eeff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>io.jooby</groupId>
   <artifactId>jooby-project</artifactId>
-  <version>2.15.2-SNAPSHOT</version>
+  <version>2.13.1-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>jooby-project</name>
@@ -20,12 +20,11 @@
 
     <!-- Parser and Renderer -->
     <freemarker.version>2.3.31</freemarker.version>
-    <handlebars.version>4.3.0</handlebars.version>
+    <handlebars.version>4.2.0</handlebars.version>
     <pebble.version>3.1.5</pebble.version>
-    <jackson.version>2.13.4</jackson.version>
-    <jackson-dataformat-yaml.version>${jackson.version}</jackson-dataformat-yaml.version>
-    <jackson-databind.version>${jackson.version}</jackson-databind.version>
-    <gson.version>2.9.1</gson.version>
+    <jackson.version>2.13.2</jackson.version>
+    <jackson-databind.version>2.13.2.2</jackson-databind.version>
+    <gson.version>2.9.0</gson.version>
     <jakarta.json.bind-api.version>1.0.2</jakarta.json.bind-api.version>
     <yasson.version>1.0.9</yasson.version>
     <rocker.version>1.3.0</rocker.version>
@@ -33,56 +32,56 @@
 
     <!-- data -->
     <HikariCP.version>4.0.3</HikariCP.version>
-    <mysql-connector-java.version>8.0.30</mysql-connector-java.version>
+    <mysql-connector-java.version>8.0.27</mysql-connector-java.version>
     <log4jdbc.version>1.2</log4jdbc.version>
-    <h2.version>2.1.212</h2.version>
+    <h2.version>2.1.210</h2.version>
     <hibernate.version>5.6.3.Final</hibernate.version>
     <ebean.version>12.16.0</ebean.version>
-    <jdbi.version>3.32.0</jdbi.version>
+    <jdbi.version>3.28.0</jdbi.version>
     <flyway.version>7.5.1</flyway.version>
     <graphql-java.version>16.2</graphql-java.version>
-    <lettuce.version>6.1.8.RELEASE</lettuce.version>
+    <lettuce.version>6.1.6.RELEASE</lettuce.version>
     <commons-pool2.version>2.11.1</commons-pool2.version>
     <kafka.version>2.7.0</kafka.version>
     <caffeine.version>2.8.8</caffeine.version>
 
-    <config.version>1.4.2</config.version>
+    <config.version>1.4.1</config.version>
 
     <!-- dependency injection -->
     <weld.version>3.1.5.Final</weld.version>
-    <spring.version>5.3.20</spring.version>
-    <guice.version>5.1.0</guice.version>
+    <spring.version>5.3.7</spring.version>
+    <guice.version>5.0.1</guice.version>
 
     <!-- logging -->
-    <logback-classic.version>1.2.11</logback-classic.version>
-    <log4j.version>2.17.2</log4j.version>
-    <slf4j.version>1.7.36</slf4j.version>
+    <logback-classic.version>1.2.9</logback-classic.version>
+    <log4j.version>2.17.1</log4j.version>
+    <slf4j.version>1.7.32</slf4j.version>
 
     <!-- communication -->
     <commons-email.version>1.5</commons-email.version>
 
     <!-- util -->
-    <jfiglet.version>0.0.9</jfiglet.version>
-    <metrics.version>4.2.9</metrics.version>
+    <jfiglet.version>0.0.8</jfiglet.version>
+    <metrics.version>4.2.8</metrics.version>
 
     <!-- jboss modules -->
     <jboss-modules.version>1.11.0.Final</jboss-modules.version>
 
     <!-- ASM -->
-    <asm.version>9.3</asm.version>
+    <asm.version>9.1</asm.version>
 
     <!-- Server -->
-    <undertow.version>2.2.19.Final</undertow.version>
-    <jetty.version>9.4.48.v20220622</jetty.version>
-    <netty.version>4.1.80.Final</netty.version>
+    <undertow.version>2.2.16.Final</undertow.version>
+    <jetty.version>9.4.45.v20220203</jetty.version>
+    <netty.version>4.1.75.Final</netty.version>
 
     <!-- Reactive -->
     <rxjava.version>2.2.21</rxjava.version>
-    <reactor.version>3.4.18</reactor.version>
+    <reactor.version>3.4.14</reactor.version>
 
     <!--OpenAPI-->
-    <swagger.version>2.2.0</swagger.version>
-    <swagger-parser.version>2.0.33</swagger-parser.version>
+    <swagger.version>2.1.13</swagger.version>
+    <swagger-parser.version>2.0.30</swagger-parser.version>
     <redoc.version>2.0.0-rc.20</redoc.version>
 
     <!-- SSL -->
@@ -95,35 +94,35 @@
 
     <!-- Misc -->
     <okhttp.version>4.9.3</okhttp.version>
-    <jsonwebtoken.version>0.11.5</jsonwebtoken.version>
+      <jsonwebtoken.version>0.11.2</jsonwebtoken.version>
     <pac4j.version>4.5.6</pac4j.version>
     <quartz.version>2.3.2</quartz.version>
     <cron-utils.version>9.1.6</cron-utils.version>
     <bucket4j-core.version>6.4.1</bucket4j-core.version>
-    <aws-java-sdk.version>1.12.230</aws-java-sdk.version>
+    <aws-java-sdk.version>1.12.191</aws-java-sdk.version>
     <jmespath-java.version>${aws-java-sdk.version}</jmespath-java.version>
-    <frontend-plugin-core.version>1.12.1</frontend-plugin-core.version>
+    <frontend-plugin-core.version>1.12.0</frontend-plugin-core.version>
     <jasypt.version>1.9.3</jasypt.version>
 
-    <commons-io.version>2.11.0</commons-io.version>
+    <commons-io.version>2.8.0</commons-io.version>
     <unbescape.version>1.1.6.RELEASE</unbescape.version>
 
     <!-- Kotlin -->
-    <kotlin.version>1.6.21</kotlin.version>
-    <kotlinx-coroutines-core.version>1.6.2</kotlinx-coroutines-core.version>
+    <kotlin.version>1.4.32</kotlin.version>
+    <kotlinx-coroutines-core.version>1.4.1</kotlinx-coroutines-core.version>
     <kotlin.compiler.incremental>true</kotlin.compiler.incremental>
 
     <!-- Test dependencies -->
-    <jacoco.version>0.8.8</jacoco.version>
-    <junit.version>5.8.2</junit.version>
-    <rest-assured.version>5.1.0</rest-assured.version>
+    <jacoco.version>0.8.7</jacoco.version>
+    <junit.version>5.8.1</junit.version>
+    <rest-assured.version>4.3.3</rest-assured.version>
     <checkstyle.version>8.42</checkstyle.version>
-    <mockito.version>4.6.0</mockito.version>
-    <guava.version>31.1-jre</guava.version>
+    <mockito.version>4.4.0</mockito.version>
+    <guava.version>30.0-jre</guava.version>
 
-    <auto-service.version>1.0.1</auto-service.version>
+    <auto-service.version>1.0</auto-service.version>
     <compile-testing.version>0.19</compile-testing.version>
-    <truth.version>1.1.3</truth.version>
+    <truth.version>1.1.2</truth.version>
 
     <!-- Gradle plugins -->
     <gradle-core.version>2.6</gradle-core.version>
@@ -139,25 +138,25 @@
     <maven-tiles-plugin.version>2.24</maven-tiles-plugin.version>
     <maven-checkstyle-plugin.version>3.1.2</maven-checkstyle-plugin.version>
     <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
-    <maven-core.version>3.8.5</maven-core.version>
+    <maven-core.version>3.8.1</maven-core.version>
     <maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
     <maven-enforcer-plugin.version>3.0.0-M3</maven-enforcer-plugin.version>
     <maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
-    <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
+    <maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
     <maven-javadoc-plugin.version>3.3.0</maven-javadoc-plugin.version>
     <archetype-packaging.version>3.2.0</archetype-packaging.version>
-    <maven-plugin-annotations.version>3.6.4</maven-plugin-annotations.version>
-    <maven-plugin-api.version>3.8.5</maven-plugin-api.version>
+    <maven-plugin-annotations.version>3.6.0</maven-plugin-annotations.version>
+    <maven-plugin-api.version>3.8.1</maven-plugin-api.version>
     <maven-plugin-plugin.version>3.6.0</maven-plugin-plugin.version>
     <maven-project.version>2.2.1</maven-project.version>
     <maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
     <maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
     <maven-site-plugin.version>3.8.2</maven-site-plugin.version>
     <maven-source-plugin.version>3.2.1</maven-source-plugin.version>
-    <maven-surefire-plugin.version>3.0.0-M6</maven-surefire-plugin.version>
+    <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
     <mojo-executor.version>2.3.1</mojo-executor.version>
     <nexus-staging-maven-plugin.version>1.6.8</nexus-staging-maven-plugin.version>
-    <plexus-utils.version>3.4.1</plexus-utils.version>
+    <plexus-utils.version>3.3.0</plexus-utils.version>
     <stork-maven-plugin.version>3.1.1</stork-maven-plugin.version>
     <versions-maven-plugin.version>2.8.1</versions-maven-plugin.version>
     <exec-maven-plugin.version>3.0.0</exec-maven-plugin.version>
@@ -629,7 +628,7 @@
       <dependency>
         <groupId>com.fasterxml.jackson.dataformat</groupId>
         <artifactId>jackson-dataformat-yaml</artifactId>
-        <version>${jackson-dataformat-yaml.version}</version>
+        <version>${jackson.version}</version>
       </dependency>
 
       <dependency>
@@ -1440,7 +1439,7 @@
             <dependency>
               <groupId>org.apache.ant</groupId>
               <artifactId>ant</artifactId>
-              <version>1.10.12</version>
+              <version>1.10.11</version>
             </dependency>
           </dependencies>
         </plugin>


Test patch:
diff --git a/jooby/src/test/java/io/jooby/ServerOptionsTest.java b/jooby/src/test/java/io/jooby/ServerOptionsTest.java
index 7d091d95b..e8921cf88 100644
--- a/jooby/src/test/java/io/jooby/ServerOptionsTest.java
+++ b/jooby/src/test/java/io/jooby/ServerOptionsTest.java
@@ -22,7 +22,6 @@ public class ServerOptionsTest {
         .withValue("server.maxRequestSize", fromAnyRef(2048))
         .withValue("server.workerThreads", fromAnyRef(32))
         .withValue("server.host", fromAnyRef("0.0.0.0"))
-        .withValue("server.httpsOnly", fromAnyRef(true))
         .resolve()
     ).get();
     assertEquals(9090, options.getPort());
@@ -35,18 +34,5 @@ public class ServerOptionsTest {
     assertEquals(2048, options.getMaxRequestSize());
     assertEquals(32, options.getWorkerThreads());
     assertEquals("0.0.0.0", options.getHost());
-    assertEquals(true, options.isHttpsOnly());
-  }
-
-  @Test
-  public void shouldSetCorrectLocalHost() {
-    ServerOptions options = new ServerOptions();
-    assertEquals("0.0.0.0", options.getHost());
-    options.setHost("localhost");
-    assertEquals("0.0.0.0", options.getHost());
-    options.setHost(null);
-    assertEquals("0.0.0.0", options.getHost());
-    options.setHost("");
-    assertEquals("0.0.0.0", options.getHost());
   }
 }
diff --git a/jooby/src/test/java/io/jooby/internal/ReturnTypeTest.java b/jooby/src/test/java/io/jooby/internal/ReturnTypeTest.java
index e5534813d..34ef199b8 100644
--- a/jooby/src/test/java/io/jooby/internal/ReturnTypeTest.java
+++ b/jooby/src/test/java/io/jooby/internal/ReturnTypeTest.java
@@ -104,7 +104,7 @@ public class ReturnTypeTest {
       return future;
     });
 
-    assertType(CompletableFuture.class, ctx ->
+    assertType(Reified.completableFuture(String.class), ctx ->
         CompletableFuture.supplyAsync(() -> 4)
             .thenApply(x -> x * 42)
             .thenApply(x -> x * 53)
diff --git a/modules/jooby-apt/src/test/java/output/MyController.java b/modules/jooby-apt/src/test/java/output/MyController.java
index 0b00e40bf..ad5723bf6 100644
--- a/modules/jooby-apt/src/test/java/output/MyController.java
+++ b/modules/jooby-apt/src/test/java/output/MyController.java
@@ -1,7 +1,6 @@
 package output;
 
 import io.jooby.Context;
-import io.jooby.StatusCode;
 import io.jooby.annotations.GET;
 import io.jooby.annotations.Path;
 import io.jooby.annotations.PathParam;
@@ -12,7 +11,6 @@ import java.util.Map;
 public class MyController {
 
   @GET("/default")
-  public StatusCode controllerMethod() {
-    return StatusCode.CREATED;
+  public void controllerMethod() {
   }
 }
diff --git a/modules/jooby-apt/src/test/java/output/MyControllerHandler.java b/modules/jooby-apt/src/test/java/output/MyControllerHandler.java
index b50bd6185..4c3128454 100644
--- a/modules/jooby-apt/src/test/java/output/MyControllerHandler.java
+++ b/modules/jooby-apt/src/test/java/output/MyControllerHandler.java
@@ -1,11 +1,15 @@
 package output;
 
-import javax.annotation.Nonnull;
-import javax.inject.Provider;
-
 import io.jooby.Context;
+import io.jooby.Reified;
 import io.jooby.Route;
 import io.jooby.StatusCode;
+import io.jooby.ValueNode;
+
+import javax.annotation.Nonnull;
+import javax.inject.Provider;
+import java.lang.reflect.Type;
+import java.util.Map;
 
 public class MyControllerHandler implements Route.Handler {
 
@@ -16,8 +20,12 @@ public class MyControllerHandler implements Route.Handler {
   }
 
   @Nonnull @Override public Object apply(@Nonnull Context ctx) throws Exception {
-    StatusCode statusCode = provider.get().controllerMethod();
-    ctx.setResponseCode(statusCode);
-    return statusCode;
+    ctx.setResponseCode(StatusCode.NO_CONTENT);
+    provider.get().controllerMethod();
+    if (ctx.isResponseStarted()) {
+      return ctx;
+    } else {
+      return ctx.send(ctx.getResponseCode());
+    }
   }
 }
diff --git a/modules/jooby-apt/src/test/java/tests/HandlerCompilerTest.java b/modules/jooby-apt/src/test/java/tests/HandlerCompilerTest.java
index 0c316e506..e3ef6fa6b 100644
--- a/modules/jooby-apt/src/test/java/tests/HandlerCompilerTest.java
+++ b/modules/jooby-apt/src/test/java/tests/HandlerCompilerTest.java
@@ -324,21 +324,21 @@ public class HandlerCompilerTest {
               router.get("/p/returnChar", MockContextHelper.mockContext()).value());
 
           MockContext ctx = MockContextHelper.mockContext();
-          assertEquals(StatusCode.NO_CONTENT,
+          assertEquals(ctx,
               router.get("/p/returnStatusCode", ctx).value());
           assertEquals(StatusCode.NO_CONTENT, ctx.getResponseCode());
 
-          assertEquals(StatusCode.OK,
+          assertEquals(ctx,
               router.get("/p/statusCode", ctx.setQueryString("?statusCode=200&q=*:*")).value());
           assertEquals(StatusCode.OK, ctx.getResponseCode());
 
           ctx = MockContextHelper.mockContext();
-          assertEquals(StatusCode.NO_CONTENT,
+          assertEquals(ctx,
               router.delete("/p/noContent", ctx.setQueryString(null)).value());
           assertEquals(StatusCode.NO_CONTENT, ctx.getResponseCode());
 
           ctx = MockContextHelper.mockContext();
-          assertEquals(StatusCode.CREATED, router.get("/p/sideEffect", ctx).value());
+          assertEquals(ctx, router.get("/p/sideEffect", ctx).value());
           assertEquals(StatusCode.CREATED, ctx.getResponseCode());
         });
   }
diff --git a/modules/jooby-openapi/src/test/java/issues/i2594/App2594.java b/modules/jooby-openapi/src/test/java/issues/i2594/App2594.java
deleted file mode 100644
index e51651d76..000000000
--- a/modules/jooby-openapi/src/test/java/issues/i2594/App2594.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package issues.i2594;
-
-import io.jooby.Jooby;
-import io.jooby.OpenAPIModule;
-
-public class App2594 extends Jooby {
-  {
-
-    install(new OpenAPIModule());
-
-    mvc(HealthController2594.class);
-
-    mount("/api/v1", new ControllersAppV12594());
-    mount("/api/v2", new ControllersAppV22594());
-  }
-}
diff --git a/modules/jooby-openapi/src/test/java/issues/i2594/ControllerV12594.java b/modules/jooby-openapi/src/test/java/issues/i2594/ControllerV12594.java
deleted file mode 100644
index 07c1bf2b3..000000000
--- a/modules/jooby-openapi/src/test/java/issues/i2594/ControllerV12594.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package issues.i2594;
-
-import javax.inject.Inject;
-
-import io.jooby.annotations.GET;
-import io.jooby.annotations.Path;
-
-@Path("/")
-public class ControllerV12594 {
-
-  @Inject
-  private WelcomeService2594 welcomeService;
-
-  @GET("/welcome")
-  public String sayHi() {
-    return welcomeService.welcome("v1");
-  }
-
-  @GET("/should-not-be-duplicated-under-v2")
-  public String demo() {
-    return welcomeService.welcome("v1");
-  }
-}
diff --git a/modules/jooby-openapi/src/test/java/issues/i2594/ControllerV22594.java b/modules/jooby-openapi/src/test/java/issues/i2594/ControllerV22594.java
deleted file mode 100644
index 3af262d1d..000000000
--- a/modules/jooby-openapi/src/test/java/issues/i2594/ControllerV22594.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package issues.i2594;
-
-import javax.inject.Inject;
-
-import io.jooby.annotations.GET;
-import io.jooby.annotations.Path;
-
-@Path("/")
-public class ControllerV22594 {
-
-  @Inject
-  private WelcomeService2594 welcomeService;
-
-  @GET("/welcome")
-  public String sayHi() {
-    return welcomeService.welcome("v2");
-  }
-}
diff --git a/modules/jooby-openapi/src/test/java/issues/i2594/ControllersAppV12594.java b/modules/jooby-openapi/src/test/java/issues/i2594/ControllersAppV12594.java
deleted file mode 100644
index a2e0b900d..000000000
--- a/modules/jooby-openapi/src/test/java/issues/i2594/ControllersAppV12594.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package issues.i2594;
-
-import io.jooby.Jooby;
-
-public class ControllersAppV12594 extends Jooby {
-
-  public ControllersAppV12594() {
-    mvc(ControllerV12594.class);
-  }
-}
diff --git a/modules/jooby-openapi/src/test/java/issues/i2594/ControllersAppV22594.java b/modules/jooby-openapi/src/test/java/issues/i2594/ControllersAppV22594.java
deleted file mode 100644
index e09d4f3f0..000000000
--- a/modules/jooby-openapi/src/test/java/issues/i2594/ControllersAppV22594.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package issues.i2594;
-
-import io.jooby.Jooby;
-
-public class ControllersAppV22594 extends Jooby {
-
-  public ControllersAppV22594() {
-    mvc(ControllerV22594.class);
-  }
-}
diff --git a/modules/jooby-openapi/src/test/java/issues/i2594/HealthController2594.java b/modules/jooby-openapi/src/test/java/issues/i2594/HealthController2594.java
deleted file mode 100644
index 785d75e75..000000000
--- a/modules/jooby-openapi/src/test/java/issues/i2594/HealthController2594.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package issues.i2594;
-
-import javax.inject.Inject;
-
-import com.google.common.collect.ImmutableMap;
-import io.jooby.Context;
-import io.jooby.annotations.GET;
-import io.jooby.annotations.Path;
-
-@Path("/")
-public class HealthController2594 {
-
-  @Inject
-  private WelcomeService2594 welcomeService;
-
-  @GET("/healthcheck")
-  public void healthCheck(Context ctx) {
-    String welcome = welcomeService.welcome("healthcheck");
-    ctx.setResponseCode(200)
-        .render(ImmutableMap.of(
-            "status", "Ok",
-            "welcome", welcome)
-        );
-  }
-}
diff --git a/modules/jooby-openapi/src/test/java/issues/i2594/Issue2594.java b/modules/jooby-openapi/src/test/java/issues/i2594/Issue2594.java
deleted file mode 100644
index d9e804f25..000000000
--- a/modules/jooby-openapi/src/test/java/issues/i2594/Issue2594.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package issues.i2594;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import io.jooby.openapi.OpenAPIResult;
-import io.jooby.openapi.OpenAPITest;
-
-public class Issue2594 {
-
-  @OpenAPITest(value = App2594.class)
-  public void shouldNotDuplicateMountedApp(OpenAPIResult result) {
-    assertEquals("openapi: 3.0.1\n"
-        + "info:\n"
-        + "  title: 2594 API\n"
-        + "  description: 2594 API description\n"
-        + "  version: \"1.0\"\n"
-        + "paths:\n"
-        + "  /healthcheck:\n"
-        + "    get:\n"
-        + "      operationId: healthCheck\n"
-        + "      responses:\n"
-        + "        \"200\":\n"
-        + "          description: Success\n"
-        + "  /api/v1/welcome:\n"
-        + "    get:\n"
-        + "      operationId: sayHi\n"
-        + "      responses:\n"
-        + "        \"200\":\n"
-        + "          description: Success\n"
-        + "          content:\n"
-        + "            application/json:\n"
-        + "              schema:\n"
-        + "                type: string\n"
-        + "  /api/v1/should-not-be-duplicated-under-v2:\n"
-        + "    get:\n"
-        + "      operationId: demo\n"
-        + "      responses:\n"
-        + "        \"200\":\n"
-        + "          description: Success\n"
-        + "          content:\n"
-        + "            application/json:\n"
-        + "              schema:\n"
-        + "                type: string\n"
-        + "  /api/v2/welcome:\n"
-        + "    get:\n"
-        + "      operationId: sayHi2\n"
-        + "      responses:\n"
-        + "        \"200\":\n"
-        + "          description: Success\n"
-        + "          content:\n"
-        + "            application/json:\n"
-        + "              schema:\n"
-        + "                type: string\n", result.toYaml());
-  }
-}
diff --git a/modules/jooby-openapi/src/test/java/issues/i2594/WelcomeService2594.java b/modules/jooby-openapi/src/test/java/issues/i2594/WelcomeService2594.java
deleted file mode 100644
index fb6c1387e..000000000
--- a/modules/jooby-openapi/src/test/java/issues/i2594/WelcomeService2594.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package issues.i2594;
-
-public class WelcomeService2594 {
-
-  public String welcome(String version) {
-    return "[API " + version + "] Welcome Jooby!";
-  }
-}
diff --git a/modules/jooby-openapi/src/test/kotlin/kt/i2598/App2598.kt b/modules/jooby-openapi/src/test/kotlin/kt/i2598/App2598.kt
deleted file mode 100644
index 33dca6b58..000000000
--- a/modules/jooby-openapi/src/test/kotlin/kt/i2598/App2598.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package kt.i2598
-
-import io.jooby.Kooby
-
-class App2598 : Kooby({
-  get("/2598") {
-    val sign = mutableListOf<Int>()
-    ctx.send("{\"success\":\"true\"}")
-    //some imaginary long running operation here
-    sign.removeIf { it == 1 }
-    return@get ctx
-  }
-})
diff --git a/modules/jooby-openapi/src/test/kotlin/kt/i2598/Issue2598.kt b/modules/jooby-openapi/src/test/kotlin/kt/i2598/Issue2598.kt
deleted file mode 100644
index 5bc4fa501..000000000
--- a/modules/jooby-openapi/src/test/kotlin/kt/i2598/Issue2598.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package kt.i2598
-
-import io.jooby.Context
-import io.jooby.openapi.OpenAPITest
-import io.jooby.openapi.RouteIterator
-import org.junit.jupiter.api.Assertions.assertEquals
-
-class Issue2598 {
-
-  @OpenAPITest(value = App2598::class)
-  fun shouldParseTypeWithoutError(iterator: RouteIterator) {
-    iterator.next  {route ->
-       assertEquals(Context::class.java.name, route.defaultResponse.javaType)
-    }.verify()
-  }
-}
diff --git a/modules/jooby-openapi/src/test/resources/issues/i2594/App2594 b/modules/jooby-openapi/src/test/resources/issues/i2594/App2594
deleted file mode 100644
index 9395075a6..000000000
--- a/modules/jooby-openapi/src/test/resources/issues/i2594/App2594
+++ /dev/null
@@ -1,4 +0,0 @@
-issues.i2594.ControllerV12594$Module
-issues.i2594.ControllerV22594$Module
-issues.i2594.HealthController2594$Module
-
diff --git a/modules/jooby-test/pom.xml b/modules/jooby-test/pom.xml
index a35c3a4d5..87567df5c 100644
--- a/modules/jooby-test/pom.xml
+++ b/modules/jooby-test/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>modules</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
diff --git a/tests/pom.xml b/tests/pom.xml
index 3505a13e9..209a72541 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>io.jooby</groupId>
     <artifactId>jooby-project</artifactId>
-    <version>2.15.2-SNAPSHOT</version>
+    <version>2.13.1-SNAPSHOT</version>
   </parent>
 
   <modelVersion>4.0.0</modelVersion>
@@ -157,7 +157,7 @@
     <dependency>
       <groupId>org.webjars.npm</groupId>
       <artifactId>vue</artifactId>
-      <version>3.2.33</version>
+      <version>3.0.3</version>
     </dependency>
 
     <dependency>
diff --git a/tests/src/test/java/examples/InstanceRouter.java b/tests/src/test/java/examples/InstanceRouter.java
index 4d80d9374..69eddd5c3 100644
--- a/tests/src/test/java/examples/InstanceRouter.java
+++ b/tests/src/test/java/examples/InstanceRouter.java
@@ -30,6 +30,7 @@ public class InstanceRouter {
   @DELETE
   @Path("/void")
   public void noContent() {
+
   }
 
   @GET
diff --git a/tests/src/test/java/io/jooby/HttpsTest.java b/tests/src/test/java/io/jooby/HttpsTest.java
index a64010428..640bcea6d 100644
--- a/tests/src/test/java/io/jooby/HttpsTest.java
+++ b/tests/src/test/java/io/jooby/HttpsTest.java
@@ -3,10 +3,7 @@ package io.jooby;
 import io.jooby.junit.ServerTest;
 import io.jooby.junit.ServerTestRunner;
 
-import java.net.ConnectException;
-
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 public class HttpsTest {
 
@@ -154,17 +151,4 @@ public class HttpsTest {
       });
     });
   }
-
-  @ServerTest
-  public void httpsOnly(ServerTestRunner runner) {
-    runner.define(app -> {
-      app.setServerOptions(new ServerOptions().setSecurePort(8443).setHttpsOnly(true));
-
-      app.get("/test", ctx -> "test");
-    }).ready((http, https) -> {
-      assertThrows(ConnectException.class, () -> http.get("/test", null));
-
-      https.get("/test", rsp -> assertEquals("test", rsp.body().string()));
-    });
-  }
 }
diff --git a/tests/src/test/java/io/jooby/Issue2611.java b/tests/src/test/java/io/jooby/Issue2611.java
deleted file mode 100644
index 2683237ea..000000000
--- a/tests/src/test/java/io/jooby/Issue2611.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package io.jooby;
-
-import io.jooby.junit.ServerTest;
-import io.jooby.junit.ServerTestRunner;
-import io.restassured.http.ContentType;
-
-import static io.restassured.RestAssured.given;
-import static org.hamcrest.Matchers.equalTo;
-
-public class Issue2611 {
-
-  @ServerTest
-  public void shouldReturnBadRequestIfFileFormFieldIsMissing(ServerTestRunner runner) {
-    runner.define(app ->
-        app.post("/2611/files", ctx -> ctx.file("nonExistentField"))
-    ).ready(client ->
-        given()
-            .port(client.getPort())
-            .multiPart("testKey", "testValue")
-            .accept(ContentType.JSON)
-            .when()
-            .post("/2611/files")
-            .then()
-            .assertThat()
-            .statusCode(StatusCode.BAD_REQUEST_CODE)
-            .body("message", equalTo("Field 'nonExistentField' is missing"))
-    );
-  }
-}
diff --git a/tests/src/test/java/io/jooby/MvcTest.java b/tests/src/test/java/io/jooby/MvcTest.java
index ebaf3eb75..a842e30c6 100644
--- a/tests/src/test/java/io/jooby/MvcTest.java
+++ b/tests/src/test/java/io/jooby/MvcTest.java
@@ -14,6 +14,7 @@ import examples.TopDispatch;
 import io.jooby.json.JacksonModule;
 import io.jooby.junit.ServerTest;
 import io.jooby.junit.ServerTestRunner;
+import io.jooby.netty.Netty;
 import okhttp3.FormBody;
 import okhttp3.MediaType;
 import okhttp3.MultipartBody;
diff --git a/tests/src/test/java/io/jooby/TestSupport.java b/tests/src/test/java/io/jooby/TestSupport.java
deleted file mode 100644
index 803c491a4..000000000
--- a/tests/src/test/java/io/jooby/TestSupport.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.jooby;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-public class TestSupport {
-
-  public static Path userdir(String... segments) {
-    Path path = Paths.get(System.getProperty("user.dir"));
-    for (String segment : segments) {
-      path = path.resolve(segment);
-    }
-    return path;
-  }
-}
diff --git a/tests/src/test/java/io/jooby/WebClient.java b/tests/src/test/java/io/jooby/WebClient.java
index 5d51789bd..0ea4cafcd 100644
--- a/tests/src/test/java/io/jooby/WebClient.java
+++ b/tests/src/test/java/io/jooby/WebClient.java
@@ -100,10 +100,6 @@ public class WebClient implements AutoCloseable {
 
     public String send(String message) {
       ws.send(message);
-      return lastMessage();
-    }
-
-    public String lastMessage() {
       return listener.lastMessage();
     }
 
diff --git a/tests/src/test/java/io/jooby/i2477/Controller2477.java b/tests/src/test/java/io/jooby/i2477/Controller2477.java
deleted file mode 100644
index 4c8f3ea14..000000000
--- a/tests/src/test/java/io/jooby/i2477/Controller2477.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package io.jooby.i2477;
-
-import io.jooby.StatusCode;
-import io.jooby.annotations.POST;
-import io.jooby.annotations.PUT;
-import io.jooby.annotations.Path;
-
-public class Controller2477 {
-  @PUT
-  @Path("/2477")
-  public void doPut() {
-  }
-
-  @POST
-  @Path("/2477")
-  public StatusCode doPost() {
-    return StatusCode.CREATED;
-  }
-}
diff --git a/tests/src/test/java/io/jooby/i2477/Issue2477.java b/tests/src/test/java/io/jooby/i2477/Issue2477.java
deleted file mode 100644
index f5bd15520..000000000
--- a/tests/src/test/java/io/jooby/i2477/Issue2477.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package io.jooby.i2477;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import io.jooby.junit.ServerTest;
-import io.jooby.junit.ServerTestRunner;
-
-public class Issue2477 {
-  @ServerTest
-  public void shouldNotGet200WhenFilterFailPostControllerExecution(ServerTestRunner runner) {
-    runner.define(app -> {
-      app.decorator(next -> ctx -> {
-        Object value = next.apply(ctx);
-        if (ctx.query("failure").booleanValue(true)) {
-          throw new IllegalStateException("Intentional error");
-        }
-        return value;
-      });
-      app.mvc(new Controller2477());
-    }).ready(http -> {
-      http.put("/2477", rsp -> {
-        assertEquals(500, rsp.code());
-      });
-      http.post("/2477", rsp -> {
-        assertEquals(500, rsp.code());
-      });
-    });
-  }
-}
diff --git a/tests/src/test/java/io/jooby/i2570/Controller2570.java b/tests/src/test/java/io/jooby/i2570/Controller2570.java
deleted file mode 100644
index 45aa180f7..000000000
--- a/tests/src/test/java/io/jooby/i2570/Controller2570.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.jooby.i2570;
-
-import io.jooby.annotations.Consumes;
-import io.jooby.annotations.FormParam;
-import io.jooby.annotations.POST;
-
-public class Controller2570 {
-  @POST("/2570")
-  @Consumes("multipart/form-data")
-  public String createFile(@FormParam Document2570 document) {
-    return document.getName() + ": " + document.getFile().getFileName();
-  }
-}
diff --git a/tests/src/test/java/io/jooby/i2570/Document2570.java b/tests/src/test/java/io/jooby/i2570/Document2570.java
deleted file mode 100644
index 7c613e40e..000000000
--- a/tests/src/test/java/io/jooby/i2570/Document2570.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package io.jooby.i2570;
-
-import io.jooby.FileUpload;
-
-public class Document2570 {
-  private String name;
-  private FileUpload file;
-
-  public String getName() {
-    return name;
-  }
-
-  public void setName(String name) {
-    this.name = name;
-  }
-
-  public FileUpload getFile() {
-    return file;
-  }
-
-  public void setFile(FileUpload file) {
-    this.file = file;
-  }
-}
diff --git a/tests/src/test/java/io/jooby/i2570/Issue2570.java b/tests/src/test/java/io/jooby/i2570/Issue2570.java
deleted file mode 100644
index 57ac85041..000000000
--- a/tests/src/test/java/io/jooby/i2570/Issue2570.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package io.jooby.i2570;
-
-import static io.jooby.TestSupport.userdir;
-import static okhttp3.RequestBody.create;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import io.jooby.junit.ServerTest;
-import io.jooby.junit.ServerTestRunner;
-import okhttp3.MediaType;
-import okhttp3.MultipartBody;
-
-public class Issue2570 {
-  @ServerTest
-  public void shouldSetFileOnBean(ServerTestRunner runner) {
-    runner.define(app -> {
-      app.mvc(new Controller2570());
-    }).ready(http -> {
-      http.post("/2570", new MultipartBody.Builder()
-          .setType(MultipartBody.FORM)
-          .addFormDataPart("name", "file name")
-          .addFormDataPart("file", "fileupload.js",
-              create(userdir("src", "test", "resources", "files", "fileupload.js").toFile(),
-                  MediaType.parse("application/javascript")))
-          .build(), rsp -> {
-        assertEquals("file name: fileupload.js", rsp.body().string());
-      });
-    });
-  }
-}
diff --git a/tests/src/test/java/io/jooby/i2572/Issue2572.java b/tests/src/test/java/io/jooby/i2572/Issue2572.java
deleted file mode 100644
index bc8be7c0a..000000000
--- a/tests/src/test/java/io/jooby/i2572/Issue2572.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package io.jooby.i2572;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-
-import org.json.JSONObject;
-
-import io.jooby.json.JacksonModule;
-import io.jooby.junit.ServerTest;
-import io.jooby.junit.ServerTestRunner;
-
-public class Issue2572 {
-
-  @ServerTest
-  public void onCompleteShouldRunOnCallerThread(ServerTestRunner runner) {
-    runner.define(app -> {
-      Map<String, Object> state = new ConcurrentHashMap<>();
-
-      CountDownLatch latch = new CountDownLatch(1);
-      app.install(new JacksonModule());
-
-      app.get("/2572/state", ctx -> {
-        latch.await();
-        return state;
-      });
-
-      app.decorator(next -> ctx -> {
-        state.put("caller", Thread.currentThread().getName());
-        ctx.onComplete(context -> {
-          state.put("onComplete", Thread.currentThread().getName());
-          latch.countDown();
-        });
-        return next.apply(ctx);
-      });
-
-      app.get("/2572/init", ctx -> "Initialized");
-    }).ready(http -> {
-      http.get("/2572/init", rsp -> {
-        System.out.println(Thread.currentThread());
-        assertEquals("Initialized", rsp.body().string());
-      });
-
-      http.get("/2572/state", rsp -> {
-        System.out.println(Thread.currentThread());
-        JSONObject json = new JSONObject(rsp.body().string());
-        System.out.println(json.get("caller") + " = " + json.get("onComplete"));
-        assertEquals(json.get("caller"), json.get("onComplete"));
-      });
-    });
-  }
-}
diff --git a/tests/src/test/java/io/jooby/i2613/Issue2613.java b/tests/src/test/java/io/jooby/i2613/Issue2613.java
deleted file mode 100644
index 8623409fd..000000000
--- a/tests/src/test/java/io/jooby/i2613/Issue2613.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package io.jooby.i2613;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import java.nio.charset.StandardCharsets;
-
-import org.json.JSONObject;
-
-import com.google.common.collect.ImmutableMap;
-import io.jooby.Context;
-import io.jooby.MediaType;
-import io.jooby.MessageEncoder;
-import io.jooby.json.JacksonModule;
-import io.jooby.junit.ServerTest;
-import io.jooby.junit.ServerTestRunner;
-
-public class Issue2613 {
-
-  public static class ThemeResult {
-    public String html() {
-      return "<html></html>";
-    }
-  }
-
-  public static class ThemeResultEncoder implements MessageEncoder {
-
-    @Override public byte[] encode(Context ctx, Object value) throws Exception {
-      if (value instanceof ThemeResult) {
-        ctx.setDefaultResponseType(MediaType.html);
-        return ((ThemeResult) value).html().getBytes(StandardCharsets.UTF_8);
-      }
-      return null;
-    }
-
-  }
-
-  @ServerTest public void shouldConsiderRouteProduces(ServerTestRunner runner) {
-    runner.define(app -> {
-      app.encoder(MediaType.html, new ThemeResultEncoder());
-      app.install(new JacksonModule());
-
-      app.get("/2613/json", ctx -> ImmutableMap.of("foo", "bar"))
-          .produces(MediaType.json);
-    }).ready(http -> {
-      http.get("/2613/json", rsp -> {
-        assertEquals("{\"foo\":\"bar\"}", rsp.body().string());
-      });
-    });
-  }
-}
diff --git a/tests/src/test/java/io/jooby/i2858/Issue2858.java b/tests/src/test/java/io/jooby/i2858/Issue2858.java
deleted file mode 100644
index 0dff8ccfe..000000000
--- a/tests/src/test/java/io/jooby/i2858/Issue2858.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package io.jooby.i2858;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.json.JSONObject;
-
-import io.jooby.Jooby;
-import io.jooby.junit.ServerTest;
-import io.jooby.junit.ServerTestRunner;
-
-public class Issue2858 {
-
-  public static class App2858 extends Jooby {
-
-    public static volatile boolean error = false;
-
-    {
-      ws("/2858", (ctx, initializer) -> {
-        initializer.onConnect(ws -> {
-          try {
-            ws.send(new JSONObject().put("connected", true).toString());
-          } catch (Exception x) {
-            error = true;
-          }
-        });
-        initializer.onMessage((ws, message) -> {
-          ws.send(new JSONObject().put(message.value(), error).toString());
-        });
-
-        initializer.onError((ws, cause) -> {
-          error = true;
-          getLog().error("error ", cause);
-        });
-      });
-
-      error((ctx, cause, code) -> {
-        error = true;
-        getLog().error("error ", cause);
-      });
-    }
-  }
-
-  @ServerTest
-  public void shouldBeAbleToSendMessageOnConnect(ServerTestRunner runner) {
-    App2858 app = new App2858();
-    runner.use(() -> app)
-        .ready(client -> {
-          client.syncWebSocket("/2858", ws -> {
-            assertEquals("{\"connected\":true}",  ws.lastMessage());
-            assertEquals("{\"error\":false}", ws.send("error"));
-          });
-        });
-
-    assertEquals(false, app.error);
-  }
-}
diff --git a/tests/src/test/kotlin/i2553/App2553.kt b/tests/src/test/kotlin/i2553/App2553.kt
deleted file mode 100644
index 7eaf0b393..000000000
--- a/tests/src/test/kotlin/i2553/App2553.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package i2553
-
-import io.jooby.Kooby
-import java.util.concurrent.CompletableFuture
-
-class App2553 : Kooby({
-  get("/2553/line") {
-    CompletableFuture.supplyAsync { "line" }
-  }
-
-  get("/2553/var") {
-    val future: CompletableFuture<String> = CompletableFuture.supplyAsync { "var" }
-    future
-  }
-})
-
diff --git a/tests/src/test/kotlin/i2553/Issue2553.kt b/tests/src/test/kotlin/i2553/Issue2553.kt
deleted file mode 100644
index f94ddff08..000000000
--- a/tests/src/test/kotlin/i2553/Issue2553.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-package i2553
-
-import io.jooby.internal.RouteAnalyzer
-import io.jooby.internal.asm.ClassSource
-import io.jooby.jetty.Jetty
-import io.jooby.junit.ServerTest
-import io.jooby.junit.ServerTestRunner
-import org.junit.jupiter.api.Assertions.assertEquals
-import java.util.concurrent.CompletableFuture
-
-class Issue2553 {
-
-  @ServerTest(server = [Jetty::class])
-  fun analyzerShouldDetectCompletableFuture(runner: ServerTestRunner) {
-    val analyzer = RouteAnalyzer(ClassSource(javaClass.classLoader), false)
-
-    val app = App2553()
-    runner.use {
-      app
-    }.ready { _ ->
-      val router = app.router
-      val route = router.routes[0]
-      val type = analyzer.returnType(route.handle)
-
-      assertEquals(CompletableFuture::class.java, type)
-    }
-  }
-
-  @ServerTest
-  fun shouldDetectCompletableFuture(runner: ServerTestRunner) {
-    runner.use {
-      App2553()
-    }.ready { http ->
-      http.get("/2553/var") { rsp ->
-        assertEquals("var", rsp.body!!.string())
-      }
-
-      http.get("/2553/line") { rsp ->
-        assertEquals("line", rsp.body!!.string())
-      }
-    }
-  }
-
-}
-
diff --git a/tests/src/test/kotlin/i2598/App2598.kt b/tests/src/test/kotlin/i2598/App2598.kt
deleted file mode 100644
index 65bba4116..000000000
--- a/tests/src/test/kotlin/i2598/App2598.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package i2598
-
-import io.jooby.Kooby
-
-class App2598 : Kooby({
-  get("/2598") {
-    val sign = mutableListOf<Int>()
-    ctx.send("{\"success\":\"true\"}")
-    //some imaginary long running operation here
-    sign.removeIf { it == 1 }
-    return@get ctx
-  }
-})
-
diff --git a/tests/src/test/kotlin/i2598/Issue2598.kt b/tests/src/test/kotlin/i2598/Issue2598.kt
deleted file mode 100644
index 4e107ca8e..000000000
--- a/tests/src/test/kotlin/i2598/Issue2598.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package i2598
-
-import io.jooby.Context
-import io.jooby.internal.RouteAnalyzer
-import io.jooby.internal.asm.ClassSource
-import io.jooby.jetty.Jetty
-import io.jooby.junit.ServerTest
-import io.jooby.junit.ServerTestRunner
-import org.junit.jupiter.api.Assertions.assertEquals
-
-class Issue2598 {
-
-  @ServerTest(server = [Jetty::class])
-  fun analyzerShouldDetectCompletableFuture(runner: ServerTestRunner) {
-    val analyzer = RouteAnalyzer(ClassSource(javaClass.classLoader), false)
-
-    val app = App2598()
-    runner.use {
-      app
-    }.ready { _ ->
-      val router = app.router
-      val route = router.routes[0]
-      val type = analyzer.returnType(route.handle)
-
-      assertEquals(Context::class.java, type)
-    }
-  }
-
-}
-