Repository: tipsy/javalin
Pull request #562 (2019-04-19 10:54:56)

[core] Rework package structure
New package structure:

```
io.javalin/
├── apibuilder/
├── core/                  // things that concern both http and websockets
│   ├── security/
│   ├── util/
│   └── validation/
├── http/                  // everything related to http requests
│   ├── sse/
│   ├── staticfiles/
│   └── util/
├── plugin/                // all plugins
│   ├── json/
│   ├── metrics/
│   └── rendering/
├── websocket/             // everything related to websockets
└── Javalin.java
```

Old package structure:

```
io.javalin/                // i don't even know
├── apibuilder/
├── cookie/
├── core/
├── json/
├── metrics/
├── rendering/
├── security/
├── serversentevent/
├── staticfiles/
├── validation/
├── _DataClasses.kt
├── Context.kt
├── ErrorHandler.java
├── ExceptionHandler.java
├── Extension.java
├── Handler.java
├── HttpResponseException.kt
├── Javalin.java
└── RequestLogger.java
```

ernestas2k: Great work!

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e1add818..dfa8f05b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -25,36 +25,38 @@ Every PR will be considered.
 
 ## Project overview
 
-Javalin is a fairly small project (less than 4k LoC, including comments).
-
 ### Package overview
 
 The following ASCII file structure diagram shows the most important packages and files, with comments.
 
 ```
 io.javalin
-├── apibuilder/               // Convenience methods for when you're creating applications with a lot of routes
-├── core/
-│   ├── util/
-│   │   ├── JettyServerUtil   // Responsible for setting up the Jetty Server
-│   ├── JavalinServlet.kt     // Responsible for the request lifecycle (writes the response to the client)
-│   └── PathMatcher.kt        // Responsible for matching requests and parsing URLs
-├── json/                     // JSON mapping (functional interfaces + optional Jackson implementation)
-├── rendering/                // Template engines and markdown renderer
-├── security/                 // All functionality related to access management for routes
-├── staticfiles/              // All functionality related to static files
-├── websocket/
-│   ├── WsSession.kt          // Similar to Context.kt, but for WebSockets
-│   └── WsPathMatcher.kt      // Similar to PathMatcher.kt, but for WebSockets
-├── Context.kt                // Wrapper class for request/reponse, contains everything needed to fulfill a request
-└── Javalin.java              // Main public API, responsible for setting up and configuring the server
+├── apibuilder/                 // Convenience methods for when declaring large APIs
+├── core/                       // Things that concern both HTTP and WebSockets
+│   ├── security/               // AccessManager, Role interface, security utils
+│   ├── util/                   // Misc utils - if you don't know where to place something, this is the place...
+│   └── validation/             // Validation for parameters
+├── http/                       // Everything related to http requests
+│   ├── sse/                    // Server-sent events
+│   ├── staticfiles/            // Static file handling
+│   ├── util/                   // Misc utils for HTTP, mainly for Context
+│   ├── Context.kt              // Wrapper class for request/response, contains everything needed to fulfill a request
+│   └── JavalinServlet.kt       // Responsible for the request lifecycle (writes the response to the client)
+├── plugin/                     // All plugins (functionality that requires optional dependencies)
+│   ├── json/                   // Interfaces for JSON, as well as a Jackson based implementation
+│   ├── metrics/                // Metric plugins
+│   └── rendering/              // Interface for file rendering, and several template engine implementations
+├── websocket/                  // Everything related to WebSockets
+│   ├── WsContext.kt            // Wrapper class for WebSockets
+│   ├── JavalinWsServlet.kt     // Responsible for WebSocket upgrade
+│   └── WsHandlerController.kt  // Responsible for Websocket request lifecycle (before, endpoint, after, logging)
+└── Javalin.java                // Main public API, responsible for setting up and configuring the server
 ```
 
-Most of the codebase is Kotlin (~2.5k lines), and the rest is Java (~1k lines). 
 Any public API which takes a `@FunctionalInterface` as a parameter has to be Java 
 to achieve the best interoperability between Java and Kotlin. 
 Every `@FunctionalInterface` is also Java for the same reasons. 
-All tests are written in Kotlin.
+Almost all tests are written in Kotlin, but it's okay to contribute a Java test.
 
 ### Test setup
 
diff --git a/src/main/java/io/javalin/Javalin.java b/src/main/java/io/javalin/Javalin.java
index 1bbdf59f..fca5e5dc 100644
--- a/src/main/java/io/javalin/Javalin.java
+++ b/src/main/java/io/javalin/Javalin.java
@@ -11,48 +11,49 @@ import io.javalin.apibuilder.ApiBuilder;
 import io.javalin.apibuilder.EndpointGroup;
 import io.javalin.core.EventAttacher;
 import io.javalin.core.EventManager;
+import io.javalin.core.Extension;
 import io.javalin.core.HandlerMetaInfo;
-import io.javalin.core.HandlerType;
 import io.javalin.core.JavalinConfig;
 import io.javalin.core.JavalinEvent;
 import io.javalin.core.JavalinServer;
-import io.javalin.core.JavalinServlet;
-import io.javalin.core.JavalinWsServlet;
 import io.javalin.core.WsHandlerMetaInfo;
-import io.javalin.core.WsHandlerType;
-import io.javalin.core.util.CorsBeforeHandler;
-import io.javalin.core.util.CorsOptionsHandler;
+import io.javalin.core.security.AccessManager;
+import io.javalin.core.security.CoreRoles;
+import io.javalin.core.security.Role;
+import io.javalin.core.security.SecurityUtil;
 import io.javalin.core.util.RouteOverviewRenderer;
 import io.javalin.core.util.Util;
-import io.javalin.security.AccessManager;
-import io.javalin.security.CoreRoles;
-import io.javalin.security.Role;
-import io.javalin.security.SecurityUtil;
-import io.javalin.serversentevent.SseClient;
-import io.javalin.serversentevent.SseHandler;
+import io.javalin.http.Context;
+import io.javalin.http.ErrorHandler;
+import io.javalin.http.ExceptionHandler;
+import io.javalin.http.Handler;
+import io.javalin.http.HandlerType;
+import io.javalin.http.JavalinServlet;
+import io.javalin.http.sse.SseClient;
+import io.javalin.http.sse.SseHandler;
+import io.javalin.http.util.CorsBeforeHandler;
+import io.javalin.http.util.CorsOptionsHandler;
+import io.javalin.websocket.JavalinWsServlet;
 import io.javalin.websocket.WsExceptionHandler;
 import io.javalin.websocket.WsHandler;
-import java.util.HashMap;
+import io.javalin.websocket.WsHandlerType;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import static io.javalin.security.SecurityUtil.roles;
+import static io.javalin.core.security.SecurityUtil.roles;
 
 @SuppressWarnings("unchecked")
 public class Javalin {
 
     public static Logger log = LoggerFactory.getLogger(Javalin.class);
 
-    protected Map<Class<?>, Object> appAttributes = new HashMap<>();
-
     protected JavalinConfig config = new JavalinConfig();
 
     protected JavalinServer server = new JavalinServer(config);
-    protected JavalinServlet servlet = new JavalinServlet(appAttributes, config);
+    protected JavalinServlet servlet = new JavalinServlet(config);
     protected JavalinWsServlet wsServlet = new JavalinWsServlet(config);
 
     protected EventManager eventManager = new EventManager();
@@ -179,7 +180,7 @@ public class Javalin {
      * The method must be called before {@link Javalin#start()}.
      */
     public Javalin attribute(Class clazz, Object obj) {
-        appAttributes.put(clazz, obj);
+        config.inner.appAttributes.put(clazz, obj);
         return this;
     }
 
@@ -190,7 +191,7 @@ public class Javalin {
      * Ex: ctx.appAttribute(MyExt.class).myMethod()
      */
     public <T> T attribute(Class<T> clazz) {
-        return (T) appAttributes.get(clazz);
+        return (T) config.inner.appAttributes.get(clazz);
     }
 
     /**
diff --git a/src/main/java/io/javalin/apibuilder/ApiBuilder.java b/src/main/java/io/javalin/apibuilder/ApiBuilder.java
index 9170c6dd..d3ad0b62 100644
--- a/src/main/java/io/javalin/apibuilder/ApiBuilder.java
+++ b/src/main/java/io/javalin/apibuilder/ApiBuilder.java
@@ -6,11 +6,11 @@
 
 package io.javalin.apibuilder;
 
-import io.javalin.Handler;
 import io.javalin.Javalin;
-import io.javalin.security.AccessManager;
-import io.javalin.security.Role;
-import io.javalin.serversentevent.SseClient;
+import io.javalin.core.security.AccessManager;
+import io.javalin.core.security.Role;
+import io.javalin.http.Handler;
+import io.javalin.http.sse.SseClient;
 import io.javalin.websocket.WsHandler;
 import java.util.ArrayDeque;
 import java.util.Deque;
diff --git a/src/main/java/io/javalin/apibuilder/CrudHandler.kt b/src/main/java/io/javalin/apibuilder/CrudHandler.kt
index f7e03c2c..5470484d 100644
--- a/src/main/java/io/javalin/apibuilder/CrudHandler.kt
+++ b/src/main/java/io/javalin/apibuilder/CrudHandler.kt
@@ -6,7 +6,7 @@
 
 package io.javalin.apibuilder
 
-import io.javalin.Context
+import io.javalin.http.Context
 
 /**
  * The CrudHandler is an interface for handling the five most
diff --git a/src/main/java/io/javalin/core/EventManager.kt b/src/main/java/io/javalin/core/EventManager.kt
index 862290ce..9f26b0fa 100644
--- a/src/main/java/io/javalin/core/EventManager.kt
+++ b/src/main/java/io/javalin/core/EventManager.kt
@@ -6,7 +6,9 @@
 
 package io.javalin.core
 
-import io.javalin.security.Role
+import io.javalin.core.security.Role
+import io.javalin.http.HandlerType
+import io.javalin.websocket.WsHandlerType
 import java.util.*
 import java.util.function.Consumer
 
diff --git a/src/main/java/io/javalin/Extension.java b/src/main/java/io/javalin/core/Extension.java
similarity index 87%
rename from src/main/java/io/javalin/Extension.java
rename to src/main/java/io/javalin/core/Extension.java
index 32ab7154..1f5c652b 100644
--- a/src/main/java/io/javalin/Extension.java
+++ b/src/main/java/io/javalin/core/Extension.java
@@ -2,10 +2,11 @@
  * Javalin - https://javalin.io
  * Copyright 2017 David Åse
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
- *
  */
 
-package io.javalin;
+package io.javalin.core;
+
+import io.javalin.Javalin;
 
 /**
  * An extension is a modular way of adding functionality to a Javalin instance.
diff --git a/src/main/java/io/javalin/core/JavalinConfig.java b/src/main/java/io/javalin/core/JavalinConfig.java
index 29dcc345..c9ac70e3 100644
--- a/src/main/java/io/javalin/core/JavalinConfig.java
+++ b/src/main/java/io/javalin/core/JavalinConfig.java
@@ -6,22 +6,25 @@
 
 package io.javalin.core;
 
-import io.javalin.RequestLogger;
+import io.javalin.core.JettyUtil;
+import io.javalin.core.security.AccessManager;
+import io.javalin.core.security.Role;
+import io.javalin.core.security.SecurityUtil;
 import io.javalin.core.util.LogUtil;
 import io.javalin.core.util.RouteOverviewConfig;
-import io.javalin.core.util.SinglePageHandler;
-import io.javalin.security.AccessManager;
-import io.javalin.security.Role;
-import io.javalin.security.SecurityUtil;
-import io.javalin.staticfiles.JettyResourceHandler;
-import io.javalin.staticfiles.Location;
-import io.javalin.staticfiles.ResourceHandler;
-import io.javalin.staticfiles.StaticFileConfig;
+import io.javalin.http.RequestLogger;
+import io.javalin.http.SinglePageHandler;
+import io.javalin.http.staticfiles.JettyResourceHandler;
+import io.javalin.http.staticfiles.Location;
+import io.javalin.http.staticfiles.ResourceHandler;
+import io.javalin.http.staticfiles.StaticFileConfig;
 import io.javalin.websocket.WsHandler;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -49,6 +52,7 @@ public class JavalinConfig {
     // it's not bad to access this, the main reason it's hidden
     // is to provide a cleaner API with dedicated setters
     public class Inner {
+        @NotNull public Map<Class<?>, Object> appAttributes = new HashMap<>();
         @NotNull public List<String> corsOrigins = new ArrayList<>();
         @Nullable public RouteOverviewConfig routeOverview;
         @Nullable public RequestLogger requestLogger;
diff --git a/src/main/java/io/javalin/core/JavalinServer.kt b/src/main/java/io/javalin/core/JavalinServer.kt
index 883750c9..33b85598 100644
--- a/src/main/java/io/javalin/core/JavalinServer.kt
+++ b/src/main/java/io/javalin/core/JavalinServer.kt
@@ -7,6 +7,9 @@
 package io.javalin.core
 
 import io.javalin.Javalin
+import io.javalin.http.JavalinServlet
+import io.javalin.websocket.JavalinWsServlet
+import io.javalin.websocket.isWebSocket
 import org.eclipse.jetty.server.*
 import org.eclipse.jetty.server.handler.HandlerCollection
 import org.eclipse.jetty.server.handler.HandlerList
diff --git a/src/main/java/io/javalin/core/PathMatcher.kt b/src/main/java/io/javalin/core/PathParser.kt
similarity index 53%
rename from src/main/java/io/javalin/core/PathMatcher.kt
rename to src/main/java/io/javalin/core/PathParser.kt
index 03a9b1d8..36441e7d 100644
--- a/src/main/java/io/javalin/core/PathMatcher.kt
+++ b/src/main/java/io/javalin/core/PathParser.kt
@@ -6,15 +6,7 @@
 
 package io.javalin.core
 
-import io.javalin.Handler
-import io.javalin.core.util.ContextUtil.urlDecode
-import java.util.*
-
-data class HandlerEntry(val type: HandlerType, val path: String, val handler: Handler, val rawHandler: Handler) {
-    private val pathParser = PathParser(path)
-    fun matches(requestUri: String) = pathParser.matches(requestUri)
-    fun extractPathParams(requestUri: String) = pathParser.extractPathParams(requestUri)
-}
+import io.javalin.http.util.ContextUtil
 
 class PathParser(
         path: String,
@@ -36,38 +28,10 @@ class PathParser(
     fun matches(url: String) = url matches matchRegex
 
     fun extractPathParams(url: String) = pathParamNames.zip(values(pathParamRegex, url)) { name, value ->
-        name to urlDecode(value)
+        name to ContextUtil.urlDecode(value)
     }.toMap()
 
     // Match and group values, then drop first element (the input string)
     private fun values(regex: Regex, url: String) = regex.matchEntire(url)?.groupValues?.drop(1) ?: emptyList()
 
 }
-
-class PathMatcher {
-
-    private val handlerEntries = HandlerType.values().associateTo(EnumMap<HandlerType, ArrayList<HandlerEntry>>(HandlerType::class.java)) {
-        it to arrayListOf()
-    }
-
-    fun add(entry: HandlerEntry) {
-        if (entry.type.isHttpMethod() && handlerEntries[entry.type]!!.find { it.type == entry.type && it.path == entry.path } != null) {
-            throw IllegalArgumentException("Handler with type='${entry.type}' and path='${entry.path}' already exists.")
-        }
-        handlerEntries[entry.type]!!.add(entry)
-    }
-
-    fun findEntries(handlerType: HandlerType, requestUri: String) =
-            handlerEntries[handlerType]!!.filter { he -> match(he, requestUri) }
-
-    private fun match(entry: HandlerEntry, requestPath: String): Boolean = when {
-        entry.path == "*" -> true
-        entry.path == requestPath -> true
-        else -> entry.matches(requestPath)
-    }
-
-    private fun slashMismatch(s1: String, s2: String) = (s1.endsWith('/') || s2.endsWith('/')) && (s1.last() != s2.last())
-
-    fun hasEntries() = handlerEntries.flatMap { it.value }.count() > 0
-
-}
diff --git a/src/main/java/io/javalin/security/AccessManager.java b/src/main/java/io/javalin/core/security/AccessManager.java
similarity index 85%
rename from src/main/java/io/javalin/security/AccessManager.java
rename to src/main/java/io/javalin/core/security/AccessManager.java
index a7806e60..cdaf55cd 100644
--- a/src/main/java/io/javalin/security/AccessManager.java
+++ b/src/main/java/io/javalin/core/security/AccessManager.java
@@ -4,11 +4,11 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.security;
+package io.javalin.core.security;
 
-import io.javalin.Context;
-import io.javalin.Handler;
-import io.javalin.core.HandlerType;
+import io.javalin.http.Context;
+import io.javalin.http.Handler;
+import io.javalin.http.HandlerType;
 import java.util.Set;
 import org.jetbrains.annotations.NotNull;
 
diff --git a/src/main/java/io/javalin/security/Role.java b/src/main/java/io/javalin/core/security/Role.java
similarity index 90%
rename from src/main/java/io/javalin/security/Role.java
rename to src/main/java/io/javalin/core/security/Role.java
index 59085386..51e8fe77 100644
--- a/src/main/java/io/javalin/security/Role.java
+++ b/src/main/java/io/javalin/core/security/Role.java
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.security;
+package io.javalin.core.security;
 
 /**
  * Empty interface for roles used in {@link AccessManager}.
diff --git a/src/main/java/io/javalin/security/SecurityUtil.kt b/src/main/java/io/javalin/core/security/SecurityUtil.kt
similarity index 70%
rename from src/main/java/io/javalin/security/SecurityUtil.kt
rename to src/main/java/io/javalin/core/security/SecurityUtil.kt
index 4753b337..f949e1f4 100644
--- a/src/main/java/io/javalin/security/SecurityUtil.kt
+++ b/src/main/java/io/javalin/core/security/SecurityUtil.kt
@@ -4,10 +4,10 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.security
+package io.javalin.core.security
 
-import io.javalin.Context
-import io.javalin.Handler
+import io.javalin.http.Context
+import io.javalin.http.Handler
 
 object SecurityUtil {
 
@@ -32,3 +32,10 @@ object SecurityUtil {
 }
 
 internal enum class CoreRoles : Role { NO_WRAP } // used to avoid wrapping CORS options
+
+/**
+ * Auth credentials for basic HTTP authorization.
+ * Contains the Base64 decoded [username] and [password] from the Authorization header.
+ * @see Context.basicAuthCredentials
+ */
+data class BasicAuthCredentials(val username: String, val password: String)
diff --git a/src/main/java/io/javalin/core/util/LogUtil.kt b/src/main/java/io/javalin/core/util/LogUtil.kt
index cd815047..9c1b2801 100644
--- a/src/main/java/io/javalin/core/util/LogUtil.kt
+++ b/src/main/java/io/javalin/core/util/LogUtil.kt
@@ -6,10 +6,10 @@
 
 package io.javalin.core.util
 
-import io.javalin.Context
 import io.javalin.Javalin
-import io.javalin.core.HandlerType
-import io.javalin.core.PathMatcher
+import io.javalin.http.Context
+import io.javalin.http.HandlerType
+import io.javalin.http.PathMatcher
 import io.javalin.websocket.WsContext
 import io.javalin.websocket.WsHandler
 import java.util.*
diff --git a/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt b/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
index 3a964f6b..1f5be772 100644
--- a/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
+++ b/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
@@ -6,12 +6,12 @@
 
 package io.javalin.core.util
 
-import io.javalin.Context
-import io.javalin.Handler
 import io.javalin.Javalin
 import io.javalin.core.HandlerMetaInfo
 import io.javalin.core.WsHandlerMetaInfo
-import io.javalin.security.Role
+import io.javalin.core.security.Role
+import io.javalin.http.Context
+import io.javalin.http.Handler
 
 data class RouteOverviewConfig(val path: String, val roles: Set<Role>)
 
diff --git a/src/main/java/io/javalin/core/util/SwaggerRenderer.kt b/src/main/java/io/javalin/core/util/SwaggerRenderer.kt
index 5e340104..9588b65e 100644
--- a/src/main/java/io/javalin/core/util/SwaggerRenderer.kt
+++ b/src/main/java/io/javalin/core/util/SwaggerRenderer.kt
@@ -6,10 +6,10 @@
 
 package io.javalin.core.util
 
-import io.javalin.Context
-import io.javalin.Handler
-import io.javalin.InternalServerErrorResponse
 import io.javalin.Javalin
+import io.javalin.http.Context
+import io.javalin.http.Handler
+import io.javalin.http.InternalServerErrorResponse
 
 class SwaggerRenderer(val filePath: String) : Handler {
 
diff --git a/src/main/java/io/javalin/core/util/Util.kt b/src/main/java/io/javalin/core/util/Util.kt
index 62b0c2ff..8f01245e 100644
--- a/src/main/java/io/javalin/core/util/Util.kt
+++ b/src/main/java/io/javalin/core/util/Util.kt
@@ -6,9 +6,9 @@
 
 package io.javalin.core.util
 
-import io.javalin.InternalServerErrorResponse
 import io.javalin.Javalin
 import io.javalin.core.JavalinServer
+import io.javalin.http.InternalServerErrorResponse
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
 import java.io.File
diff --git a/src/main/java/io/javalin/validation/JavalinValidation.kt b/src/main/java/io/javalin/core/validation/JavalinValidation.kt
similarity index 97%
rename from src/main/java/io/javalin/validation/JavalinValidation.kt
rename to src/main/java/io/javalin/core/validation/JavalinValidation.kt
index 26725c8b..fb284595 100644
--- a/src/main/java/io/javalin/validation/JavalinValidation.kt
+++ b/src/main/java/io/javalin/core/validation/JavalinValidation.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.validation
+package io.javalin.core.validation
 
 class MissingConverterException(className: String) : IllegalArgumentException("Can't convert to $className. Register a converter using JavalinValidation#register.")
 
diff --git a/src/main/java/io/javalin/validation/Validator.kt b/src/main/java/io/javalin/core/validation/Validator.kt
similarity index 94%
rename from src/main/java/io/javalin/validation/Validator.kt
rename to src/main/java/io/javalin/core/validation/Validator.kt
index af3bc297..71cf8603 100644
--- a/src/main/java/io/javalin/validation/Validator.kt
+++ b/src/main/java/io/javalin/core/validation/Validator.kt
@@ -4,9 +4,9 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.validation
+package io.javalin.core.validation
 
-import io.javalin.BadRequestResponse
+import io.javalin.http.BadRequestResponse
 
 open class Validator<T>(val value: T, val messagePrefix: String = "Value") {
 
diff --git a/src/main/java/io/javalin/Context.kt b/src/main/java/io/javalin/http/Context.kt
similarity index 98%
rename from src/main/java/io/javalin/Context.kt
rename to src/main/java/io/javalin/http/Context.kt
index 91fc8c7f..e68cf77d 100644
--- a/src/main/java/io/javalin/Context.kt
+++ b/src/main/java/io/javalin/http/Context.kt
@@ -4,16 +4,16 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin
+package io.javalin.http
 
-import io.javalin.cookie.CookieStore
-import io.javalin.core.HandlerType
-import io.javalin.core.util.ContextUtil
+import io.javalin.core.security.BasicAuthCredentials
 import io.javalin.core.util.Header
-import io.javalin.core.util.MultipartUtil
-import io.javalin.json.JavalinJson
-import io.javalin.rendering.JavalinRenderer
-import io.javalin.validation.Validator
+import io.javalin.core.validation.Validator
+import io.javalin.http.util.ContextUtil
+import io.javalin.http.util.CookieStore
+import io.javalin.http.util.MultipartUtil
+import io.javalin.plugin.json.JavalinJson
+import io.javalin.plugin.rendering.JavalinRenderer
 import java.io.InputStream
 import java.nio.charset.Charset
 import java.util.*
diff --git a/src/main/java/io/javalin/ErrorHandler.java b/src/main/java/io/javalin/http/ErrorHandler.java
similarity index 91%
rename from src/main/java/io/javalin/ErrorHandler.java
rename to src/main/java/io/javalin/http/ErrorHandler.java
index 1fe22bce..fa78aed3 100644
--- a/src/main/java/io/javalin/ErrorHandler.java
+++ b/src/main/java/io/javalin/http/ErrorHandler.java
@@ -4,8 +4,9 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin;
+package io.javalin.http;
 
+import io.javalin.Javalin;
 import org.jetbrains.annotations.NotNull;
 
 /**
diff --git a/src/main/java/io/javalin/core/ErrorMapper.kt b/src/main/java/io/javalin/http/ErrorMapper.kt
similarity index 80%
rename from src/main/java/io/javalin/core/ErrorMapper.kt
rename to src/main/java/io/javalin/http/ErrorMapper.kt
index ae55bc69..2f8aa274 100644
--- a/src/main/java/io/javalin/core/ErrorMapper.kt
+++ b/src/main/java/io/javalin/http/ErrorMapper.kt
@@ -4,10 +4,8 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core
+package io.javalin.http
 
-import io.javalin.Context
-import io.javalin.ErrorHandler
 import java.util.*
 
 class ErrorMapper {
diff --git a/src/main/java/io/javalin/ExceptionHandler.java b/src/main/java/io/javalin/http/ExceptionHandler.java
similarity index 92%
rename from src/main/java/io/javalin/ExceptionHandler.java
rename to src/main/java/io/javalin/http/ExceptionHandler.java
index a6722176..d7e953bc 100644
--- a/src/main/java/io/javalin/ExceptionHandler.java
+++ b/src/main/java/io/javalin/http/ExceptionHandler.java
@@ -4,8 +4,9 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin;
+package io.javalin.http;
 
+import io.javalin.Javalin;
 import org.jetbrains.annotations.NotNull;
 
 /**
diff --git a/src/main/java/io/javalin/core/ExceptionMapper.kt b/src/main/java/io/javalin/http/ExceptionMapper.kt
similarity index 93%
rename from src/main/java/io/javalin/core/ExceptionMapper.kt
rename to src/main/java/io/javalin/http/ExceptionMapper.kt
index f6ef33d8..1f8cfe51 100644
--- a/src/main/java/io/javalin/core/ExceptionMapper.kt
+++ b/src/main/java/io/javalin/http/ExceptionMapper.kt
@@ -4,10 +4,9 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core
+package io.javalin.http
 
-import io.javalin.*
-import io.javalin.core.util.HttpResponseExceptionMapper
+import io.javalin.Javalin
 import io.javalin.core.util.Util
 
 class ExceptionMapper {
diff --git a/src/main/java/io/javalin/Handler.java b/src/main/java/io/javalin/http/Handler.java
similarity index 95%
rename from src/main/java/io/javalin/Handler.java
rename to src/main/java/io/javalin/http/Handler.java
index 20810f90..0512b261 100644
--- a/src/main/java/io/javalin/Handler.java
+++ b/src/main/java/io/javalin/http/Handler.java
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin;
+package io.javalin.http;
 
 import org.jetbrains.annotations.NotNull;
 
diff --git a/src/main/java/io/javalin/core/HandlerType.kt b/src/main/java/io/javalin/http/HandlerType.kt
similarity index 97%
rename from src/main/java/io/javalin/core/HandlerType.kt
rename to src/main/java/io/javalin/http/HandlerType.kt
index 9ddb83bd..7b145679 100644
--- a/src/main/java/io/javalin/core/HandlerType.kt
+++ b/src/main/java/io/javalin/http/HandlerType.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core
+package io.javalin.http
 
 import io.javalin.core.util.Header
 import javax.servlet.http.HttpServletRequest
diff --git a/src/main/java/io/javalin/HttpResponseException.kt b/src/main/java/io/javalin/http/HttpResponseException.kt
similarity index 98%
rename from src/main/java/io/javalin/HttpResponseException.kt
rename to src/main/java/io/javalin/http/HttpResponseException.kt
index 3950cf01..91dcfc4f 100644
--- a/src/main/java/io/javalin/HttpResponseException.kt
+++ b/src/main/java/io/javalin/http/HttpResponseException.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin
+package io.javalin.http
 
 import org.eclipse.jetty.http.HttpStatus
 
diff --git a/src/main/java/io/javalin/core/util/HttpResponseExceptionMapper.kt b/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
similarity index 97%
rename from src/main/java/io/javalin/core/util/HttpResponseExceptionMapper.kt
rename to src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
index 8272157b..05633b05 100644
--- a/src/main/java/io/javalin/core/util/HttpResponseExceptionMapper.kt
+++ b/src/main/java/io/javalin/http/HttpResponseExceptionMapper.kt
@@ -4,9 +4,9 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core.util
+package io.javalin.http
 
-import io.javalin.*
+import io.javalin.core.util.Header
 import java.util.concurrent.CompletionException
 
 object HttpResponseExceptionMapper {
diff --git a/src/main/java/io/javalin/core/JavalinServlet.kt b/src/main/java/io/javalin/http/JavalinServlet.kt
similarity index 92%
rename from src/main/java/io/javalin/core/JavalinServlet.kt
rename to src/main/java/io/javalin/http/JavalinServlet.kt
index af0f0210..936a7b2f 100644
--- a/src/main/java/io/javalin/core/JavalinServlet.kt
+++ b/src/main/java/io/javalin/http/JavalinServlet.kt
@@ -4,21 +4,23 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core
+package io.javalin.http
 
-import io.javalin.Context
-import io.javalin.Handler
-import io.javalin.MethodNotAllowedResponse
-import io.javalin.NotFoundResponse
-import io.javalin.core.util.*
-import io.javalin.security.CoreRoles
-import io.javalin.security.Role
+import io.javalin.core.JavalinConfig
+import io.javalin.core.CachedRequestWrapper
+import io.javalin.core.security.CoreRoles
+import io.javalin.core.security.Role
+import io.javalin.core.util.Header
+import io.javalin.core.util.LogUtil
+import io.javalin.core.util.Util
+import io.javalin.http.util.ContextUtil
+import io.javalin.http.util.MethodNotAllowedUtil
 import java.io.InputStream
 import java.util.zip.GZIPOutputStream
 import javax.servlet.http.HttpServletRequest
 import javax.servlet.http.HttpServletResponse
 
-class JavalinServlet(private val appAttributes: Map<Class<*>, Any>, val config: JavalinConfig) {
+class JavalinServlet(val config: JavalinConfig) {
 
     val matcher = PathMatcher()
     val exceptionMapper = ExceptionMapper()
@@ -29,7 +31,7 @@ class JavalinServlet(private val appAttributes: Map<Class<*>, Any>, val config:
         val req = CachedRequestWrapper(servletRequest, config.requestCacheSize) // cached for reading multiple times
         val type = HandlerType.fromServletRequest(req)
         val requestUri = req.requestURI.removePrefix(req.contextPath)
-        val ctx = Context(req, res, appAttributes)
+        val ctx = Context(req, res, config.inner.appAttributes)
 
         fun tryWithExceptionMapper(func: () -> Unit) = exceptionMapper.catchException(ctx, func)
 
diff --git a/src/main/java/io/javalin/http/PathMatcher.kt b/src/main/java/io/javalin/http/PathMatcher.kt
new file mode 100644
index 00000000..e207faa0
--- /dev/null
+++ b/src/main/java/io/javalin/http/PathMatcher.kt
@@ -0,0 +1,40 @@
+/*
+ * Javalin - https://javalin.io
+ * Copyright 2017 David Åse
+ * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
+ */
+
+package io.javalin.http
+
+import io.javalin.core.PathParser
+import java.util.*
+
+data class HandlerEntry(val type: HandlerType, val path: String, val handler: Handler, val rawHandler: Handler) {
+    private val pathParser = PathParser(path)
+    fun matches(requestUri: String) = pathParser.matches(requestUri)
+    fun extractPathParams(requestUri: String) = pathParser.extractPathParams(requestUri)
+}
+
+class PathMatcher {
+
+    private val handlerEntries = HandlerType.values().associateTo(EnumMap<HandlerType, ArrayList<HandlerEntry>>(HandlerType::class.java)) {
+        it to arrayListOf()
+    }
+
+    fun add(entry: HandlerEntry) {
+        if (entry.type.isHttpMethod() && handlerEntries[entry.type]!!.find { it.type == entry.type && it.path == entry.path } != null) {
+            throw IllegalArgumentException("Handler with type='${entry.type}' and path='${entry.path}' already exists.")
+        }
+        handlerEntries[entry.type]!!.add(entry)
+    }
+
+    fun findEntries(handlerType: HandlerType, requestUri: String) =
+            handlerEntries[handlerType]!!.filter { he -> match(he, requestUri) }
+
+    private fun match(entry: HandlerEntry, requestPath: String): Boolean = when {
+        entry.path == "*" -> true
+        entry.path == requestPath -> true
+        else -> entry.matches(requestPath)
+    }
+
+}
diff --git a/src/main/java/io/javalin/RequestLogger.java b/src/main/java/io/javalin/http/RequestLogger.java
similarity index 95%
rename from src/main/java/io/javalin/RequestLogger.java
rename to src/main/java/io/javalin/http/RequestLogger.java
index a8aab44c..7c2e5ef0 100644
--- a/src/main/java/io/javalin/RequestLogger.java
+++ b/src/main/java/io/javalin/http/RequestLogger.java
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin;
+package io.javalin.http;
 
 import org.jetbrains.annotations.NotNull;
 
diff --git a/src/main/java/io/javalin/core/util/SinglePageHandler.kt b/src/main/java/io/javalin/http/SinglePageHandler.kt
similarity index 90%
rename from src/main/java/io/javalin/core/util/SinglePageHandler.kt
rename to src/main/java/io/javalin/http/SinglePageHandler.kt
index 4c8deced..4ac66077 100644
--- a/src/main/java/io/javalin/core/util/SinglePageHandler.kt
+++ b/src/main/java/io/javalin/http/SinglePageHandler.kt
@@ -4,10 +4,12 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core.util
+package io.javalin.http
 
-import io.javalin.Context
-import io.javalin.staticfiles.Location
+import io.javalin.core.util.Header
+import io.javalin.core.util.Util
+import io.javalin.http.staticfiles.Location
+import io.javalin.http.util.ContextUtil
 import java.net.URL
 
 /**
diff --git a/src/main/java/io/javalin/_DataClasses.kt b/src/main/java/io/javalin/http/UploadedFile.kt
similarity index 71%
rename from src/main/java/io/javalin/_DataClasses.kt
rename to src/main/java/io/javalin/http/UploadedFile.kt
index bbcc72b7..d7671711 100644
--- a/src/main/java/io/javalin/_DataClasses.kt
+++ b/src/main/java/io/javalin/http/UploadedFile.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin
+package io.javalin.http
 
 import java.io.InputStream
 
@@ -18,10 +18,3 @@ import java.io.InputStream
  * @see <a href="https://javalin.io/documentation#faq">Uploads in FAQ</a>
  */
 data class UploadedFile(val contentType: String, val content: InputStream, val name: String, val extension: String)
-
-/**
- * Auth credentials for basic HTTP authorization.
- * Contains the Base64 decoded [username] and [password] from the Authorization header.
- * @see Context.basicAuthCredentials
- */
-data class BasicAuthCredentials(val username: String, val password: String)
diff --git a/src/main/java/io/javalin/serversentevent/Emitter.kt b/src/main/java/io/javalin/http/sse/Emitter.kt
similarity index 96%
rename from src/main/java/io/javalin/serversentevent/Emitter.kt
rename to src/main/java/io/javalin/http/sse/Emitter.kt
index 298bdf21..e85061b0 100644
--- a/src/main/java/io/javalin/serversentevent/Emitter.kt
+++ b/src/main/java/io/javalin/http/sse/Emitter.kt
@@ -1,4 +1,4 @@
-package io.javalin.serversentevent
+package io.javalin.http.sse
 
 import java.io.IOException
 import javax.servlet.AsyncContext
diff --git a/src/main/java/io/javalin/serversentevent/SseClient.java b/src/main/java/io/javalin/http/sse/SseClient.java
similarity index 92%
rename from src/main/java/io/javalin/serversentevent/SseClient.java
rename to src/main/java/io/javalin/http/sse/SseClient.java
index d9503ecb..7330ddcf 100644
--- a/src/main/java/io/javalin/serversentevent/SseClient.java
+++ b/src/main/java/io/javalin/http/sse/SseClient.java
@@ -1,6 +1,6 @@
-package io.javalin.serversentevent;
+package io.javalin.http.sse;
 
-import io.javalin.Context;
+import io.javalin.http.Context;
 
 public class SseClient {
 
diff --git a/src/main/java/io/javalin/serversentevent/SseHandler.kt b/src/main/java/io/javalin/http/sse/SseHandler.kt
similarity index 88%
rename from src/main/java/io/javalin/serversentevent/SseHandler.kt
rename to src/main/java/io/javalin/http/sse/SseHandler.kt
index 3f773899..3b72f234 100644
--- a/src/main/java/io/javalin/serversentevent/SseHandler.kt
+++ b/src/main/java/io/javalin/http/sse/SseHandler.kt
@@ -1,8 +1,8 @@
-package io.javalin.serversentevent
+package io.javalin.http.sse
 
-import io.javalin.Context
-import io.javalin.Handler
 import io.javalin.core.util.Header
+import io.javalin.http.Context
+import io.javalin.http.Handler
 import java.util.function.Consumer
 
 class SseHandler(private val clientConsumer: Consumer<SseClient>) : Handler {
diff --git a/src/main/java/io/javalin/staticfiles/JettyResourceHandler.kt b/src/main/java/io/javalin/http/staticfiles/JettyResourceHandler.kt
similarity index 96%
rename from src/main/java/io/javalin/staticfiles/JettyResourceHandler.kt
rename to src/main/java/io/javalin/http/staticfiles/JettyResourceHandler.kt
index 4c6714e0..9a76fa58 100644
--- a/src/main/java/io/javalin/staticfiles/JettyResourceHandler.kt
+++ b/src/main/java/io/javalin/http/staticfiles/JettyResourceHandler.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.staticfiles
+package io.javalin.http.staticfiles
 
 import io.javalin.Javalin
 import io.javalin.core.util.Header
@@ -16,7 +16,7 @@ import java.io.File
 import javax.servlet.http.HttpServletRequest
 import javax.servlet.http.HttpServletResponse
 
-class JettyResourceHandler : io.javalin.staticfiles.ResourceHandler {
+class JettyResourceHandler : io.javalin.http.staticfiles.ResourceHandler {
 
     val handlers = mutableListOf<GzipHandler>()
 
diff --git a/src/main/java/io/javalin/staticfiles/ResourceHandler.kt b/src/main/java/io/javalin/http/staticfiles/ResourceHandler.kt
similarity index 91%
rename from src/main/java/io/javalin/staticfiles/ResourceHandler.kt
rename to src/main/java/io/javalin/http/staticfiles/ResourceHandler.kt
index 6f3ab13d..1bd48c8d 100644
--- a/src/main/java/io/javalin/staticfiles/ResourceHandler.kt
+++ b/src/main/java/io/javalin/http/staticfiles/ResourceHandler.kt
@@ -1,4 +1,4 @@
-package io.javalin.staticfiles
+package io.javalin.http.staticfiles
 
 import javax.servlet.http.HttpServletRequest
 import javax.servlet.http.HttpServletResponse
diff --git a/src/main/java/io/javalin/core/util/ContextUtil.kt b/src/main/java/io/javalin/http/util/ContextUtil.kt
similarity index 92%
rename from src/main/java/io/javalin/core/util/ContextUtil.kt
rename to src/main/java/io/javalin/http/util/ContextUtil.kt
index 097478ca..dbda3a8d 100644
--- a/src/main/java/io/javalin/core/util/ContextUtil.kt
+++ b/src/main/java/io/javalin/http/util/ContextUtil.kt
@@ -4,12 +4,13 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core.util
+package io.javalin.http.util
 
-import io.javalin.BasicAuthCredentials
-import io.javalin.Context
-import io.javalin.core.HandlerEntry
-import io.javalin.core.HandlerType
+import io.javalin.core.security.BasicAuthCredentials
+import io.javalin.core.util.Header
+import io.javalin.http.Context
+import io.javalin.http.HandlerEntry
+import io.javalin.http.HandlerType
 import java.net.URLDecoder
 import java.util.*
 import javax.servlet.http.HttpServletRequest
diff --git a/src/main/java/io/javalin/cookie/CookieStore.kt b/src/main/java/io/javalin/http/util/CookieStore.kt
similarity index 76%
rename from src/main/java/io/javalin/cookie/CookieStore.kt
rename to src/main/java/io/javalin/http/util/CookieStore.kt
index ba434533..bbd9e5a5 100644
--- a/src/main/java/io/javalin/cookie/CookieStore.kt
+++ b/src/main/java/io/javalin/http/util/CookieStore.kt
@@ -1,9 +1,16 @@
-package io.javalin.cookie
+/*
+ * Javalin - https://javalin.io
+ * Copyright 2017 David Åse
+ * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
+ */
 
-import io.javalin.json.JavalinJson
+package io.javalin.http.util
+
+import io.javalin.plugin.json.JavalinJson
 import java.util.*
 import javax.servlet.http.Cookie
 
+@Suppress("UNCHECKED_CAST")
 class CookieStore(cookie: String?) {
 
     companion object {
@@ -14,14 +21,12 @@ class CookieStore(cookie: String?) {
 
     fun serializeToCookie() = Cookie(COOKIE_NAME, serialize(cookieMap))
 
-    @Suppress("UNCHECKED_CAST")
     operator fun <T> get(key: String) = cookieMap[key] as T
 
     operator fun set(key: String, value: Any) = cookieMap.put(key, value)
 
     fun clear() = cookieMap.clear()
 
-    @Suppress("UNCHECKED_CAST")
     private fun deserialize(cookie: String?) = if (!cookie.isNullOrEmpty()) {
         JavalinJson.fromJson(String(Base64.getDecoder().decode(cookie)), Map::class.java) as MutableMap<String, Any>
     } else mutableMapOf()
diff --git a/src/main/java/io/javalin/core/util/CorsUtil.kt b/src/main/java/io/javalin/http/util/CorsUtil.kt
similarity index 88%
rename from src/main/java/io/javalin/core/util/CorsUtil.kt
rename to src/main/java/io/javalin/http/util/CorsUtil.kt
index 042c0571..a10326fe 100644
--- a/src/main/java/io/javalin/core/util/CorsUtil.kt
+++ b/src/main/java/io/javalin/http/util/CorsUtil.kt
@@ -4,10 +4,11 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core.util
+package io.javalin.http.util
 
-import io.javalin.Context
-import io.javalin.Handler
+import io.javalin.core.util.Header
+import io.javalin.http.Context
+import io.javalin.http.Handler
 
 class CorsBeforeHandler(private val origins: List<String>) : Handler {
     override fun handle(ctx: Context) {
diff --git a/src/main/java/io/javalin/core/util/MethodNotAllowedUtil.kt b/src/main/java/io/javalin/http/util/MethodNotAllowedUtil.kt
similarity index 63%
rename from src/main/java/io/javalin/core/util/MethodNotAllowedUtil.kt
rename to src/main/java/io/javalin/http/util/MethodNotAllowedUtil.kt
index d28463ce..6ef77b1d 100644
--- a/src/main/java/io/javalin/core/util/MethodNotAllowedUtil.kt
+++ b/src/main/java/io/javalin/http/util/MethodNotAllowedUtil.kt
@@ -1,8 +1,14 @@
-package io.javalin.core.util
+/*
+ * Javalin - https://javalin.io
+ * Copyright 2017 David Åse
+ * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
+ */
 
-import io.javalin.Context
-import io.javalin.core.HandlerType
-import io.javalin.core.PathMatcher
+package io.javalin.http.util
+
+import io.javalin.http.Context
+import io.javalin.http.HandlerType
+import io.javalin.http.PathMatcher
 
 object MethodNotAllowedUtil {
 
diff --git a/src/main/java/io/javalin/core/util/MultipartUtil.kt b/src/main/java/io/javalin/http/util/MultipartUtil.kt
similarity index 96%
rename from src/main/java/io/javalin/core/util/MultipartUtil.kt
rename to src/main/java/io/javalin/http/util/MultipartUtil.kt
index bdefea8e..a073667c 100644
--- a/src/main/java/io/javalin/core/util/MultipartUtil.kt
+++ b/src/main/java/io/javalin/http/util/MultipartUtil.kt
@@ -4,9 +4,9 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core.util
+package io.javalin.http.util
 
-import io.javalin.UploadedFile
+import io.javalin.http.UploadedFile
 import java.io.ByteArrayInputStream
 import java.nio.charset.Charset
 import javax.servlet.MultipartConfigElement
diff --git a/src/main/java/io/javalin/json/JavalinJackson.kt b/src/main/java/io/javalin/plugin/json/JavalinJackson.kt
similarity index 97%
rename from src/main/java/io/javalin/json/JavalinJackson.kt
rename to src/main/java/io/javalin/plugin/json/JavalinJackson.kt
index b59463c8..df45ef26 100644
--- a/src/main/java/io/javalin/json/JavalinJackson.kt
+++ b/src/main/java/io/javalin/plugin/json/JavalinJackson.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.json
+package io.javalin.plugin.json
 
 import com.fasterxml.jackson.databind.Module
 import com.fasterxml.jackson.databind.ObjectMapper
diff --git a/src/main/java/io/javalin/json/JavalinJson.kt b/src/main/java/io/javalin/plugin/json/JavalinJson.kt
similarity index 96%
rename from src/main/java/io/javalin/json/JavalinJson.kt
rename to src/main/java/io/javalin/plugin/json/JavalinJson.kt
index 84ede7fc..2e4d0d6d 100644
--- a/src/main/java/io/javalin/json/JavalinJson.kt
+++ b/src/main/java/io/javalin/plugin/json/JavalinJson.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.json
+package io.javalin.plugin.json
 
 @FunctionalInterface
 interface FromJsonMapper {
diff --git a/src/main/java/io/javalin/metrics/JavalinMicrometer.kt b/src/main/java/io/javalin/plugin/metrics/JavalinMicrometer.kt
similarity index 97%
rename from src/main/java/io/javalin/metrics/JavalinMicrometer.kt
rename to src/main/java/io/javalin/plugin/metrics/JavalinMicrometer.kt
index 2ac53097..fed9c33a 100644
--- a/src/main/java/io/javalin/metrics/JavalinMicrometer.kt
+++ b/src/main/java/io/javalin/plugin/metrics/JavalinMicrometer.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.metrics
+package io.javalin.plugin.metrics
 
 import io.javalin.core.util.OptionalDependency
 import io.javalin.core.util.Util
diff --git a/src/main/java/io/javalin/rendering/FileRenderer.java b/src/main/java/io/javalin/plugin/rendering/FileRenderer.java
similarity index 86%
rename from src/main/java/io/javalin/rendering/FileRenderer.java
rename to src/main/java/io/javalin/plugin/rendering/FileRenderer.java
index f7f2b61d..3c76a341 100644
--- a/src/main/java/io/javalin/rendering/FileRenderer.java
+++ b/src/main/java/io/javalin/plugin/rendering/FileRenderer.java
@@ -4,9 +4,9 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering;
+package io.javalin.plugin.rendering;
 
-import io.javalin.Context;
+import io.javalin.http.Context;
 import java.util.Map;
 import org.jetbrains.annotations.NotNull;
 
diff --git a/src/main/java/io/javalin/rendering/JavalinRenderer.kt b/src/main/java/io/javalin/plugin/rendering/JavalinRenderer.kt
similarity index 92%
rename from src/main/java/io/javalin/rendering/JavalinRenderer.kt
rename to src/main/java/io/javalin/plugin/rendering/JavalinRenderer.kt
index 8e684487..39acab9c 100644
--- a/src/main/java/io/javalin/rendering/JavalinRenderer.kt
+++ b/src/main/java/io/javalin/plugin/rendering/JavalinRenderer.kt
@@ -4,11 +4,11 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering
+package io.javalin.plugin.rendering
 
 import io.javalin.Javalin
-import io.javalin.rendering.markdown.JavalinCommonmark
-import io.javalin.rendering.template.*
+import io.javalin.plugin.rendering.markdown.JavalinCommonmark
+import io.javalin.plugin.rendering.template.*
 
 object JavalinRenderer {
 
diff --git a/src/main/java/io/javalin/rendering/markdown/Commonmark.kt b/src/main/java/io/javalin/plugin/rendering/markdown/Commonmark.kt
similarity index 91%
rename from src/main/java/io/javalin/rendering/markdown/Commonmark.kt
rename to src/main/java/io/javalin/plugin/rendering/markdown/Commonmark.kt
index 614b996b..8b8717d2 100644
--- a/src/main/java/io/javalin/rendering/markdown/Commonmark.kt
+++ b/src/main/java/io/javalin/plugin/rendering/markdown/Commonmark.kt
@@ -4,11 +4,11 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering.markdown
+package io.javalin.plugin.rendering.markdown
 
 import io.javalin.core.util.OptionalDependency
 import io.javalin.core.util.Util
-import io.javalin.rendering.FileRenderer
+import io.javalin.plugin.rendering.FileRenderer
 import org.commonmark.parser.Parser
 import org.commonmark.renderer.html.HtmlRenderer
 
diff --git a/src/main/java/io/javalin/rendering/template/JavalinFreemarker.kt b/src/main/java/io/javalin/plugin/rendering/template/JavalinFreemarker.kt
similarity index 92%
rename from src/main/java/io/javalin/rendering/template/JavalinFreemarker.kt
rename to src/main/java/io/javalin/plugin/rendering/template/JavalinFreemarker.kt
index 39253805..6280dcac 100644
--- a/src/main/java/io/javalin/rendering/template/JavalinFreemarker.kt
+++ b/src/main/java/io/javalin/plugin/rendering/template/JavalinFreemarker.kt
@@ -4,13 +4,13 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering.template
+package io.javalin.plugin.rendering.template
 
 import freemarker.template.Configuration
 import freemarker.template.Version
 import io.javalin.core.util.OptionalDependency
 import io.javalin.core.util.Util
-import io.javalin.rendering.FileRenderer
+import io.javalin.plugin.rendering.FileRenderer
 import java.io.StringWriter
 
 object JavalinFreemarker : FileRenderer {
diff --git a/src/main/java/io/javalin/rendering/template/JavalinJtwig.kt b/src/main/java/io/javalin/plugin/rendering/template/JavalinJtwig.kt
similarity index 91%
rename from src/main/java/io/javalin/rendering/template/JavalinJtwig.kt
rename to src/main/java/io/javalin/plugin/rendering/template/JavalinJtwig.kt
index d51add14..ad3894bc 100644
--- a/src/main/java/io/javalin/rendering/template/JavalinJtwig.kt
+++ b/src/main/java/io/javalin/plugin/rendering/template/JavalinJtwig.kt
@@ -4,11 +4,11 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering.template
+package io.javalin.plugin.rendering.template
 
 import io.javalin.core.util.OptionalDependency
 import io.javalin.core.util.Util
-import io.javalin.rendering.FileRenderer
+import io.javalin.plugin.rendering.FileRenderer
 import org.jtwig.JtwigModel
 import org.jtwig.JtwigTemplate
 import org.jtwig.environment.DefaultEnvironmentConfiguration
diff --git a/src/main/java/io/javalin/rendering/template/JavalinMustache.kt b/src/main/java/io/javalin/plugin/rendering/template/JavalinMustache.kt
similarity index 91%
rename from src/main/java/io/javalin/rendering/template/JavalinMustache.kt
rename to src/main/java/io/javalin/plugin/rendering/template/JavalinMustache.kt
index badcf757..f942a0b5 100644
--- a/src/main/java/io/javalin/rendering/template/JavalinMustache.kt
+++ b/src/main/java/io/javalin/plugin/rendering/template/JavalinMustache.kt
@@ -4,13 +4,13 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering.template
+package io.javalin.plugin.rendering.template
 
 import com.github.mustachejava.DefaultMustacheFactory
 import com.github.mustachejava.MustacheFactory
 import io.javalin.core.util.OptionalDependency
 import io.javalin.core.util.Util
-import io.javalin.rendering.FileRenderer
+import io.javalin.plugin.rendering.FileRenderer
 import java.io.StringWriter
 
 object JavalinMustache : FileRenderer {
diff --git a/src/main/java/io/javalin/rendering/template/JavalinPebble.kt b/src/main/java/io/javalin/plugin/rendering/template/JavalinPebble.kt
similarity index 92%
rename from src/main/java/io/javalin/rendering/template/JavalinPebble.kt
rename to src/main/java/io/javalin/plugin/rendering/template/JavalinPebble.kt
index 7cd1a70d..3c2c6575 100644
--- a/src/main/java/io/javalin/rendering/template/JavalinPebble.kt
+++ b/src/main/java/io/javalin/plugin/rendering/template/JavalinPebble.kt
@@ -4,13 +4,13 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering.template
+package io.javalin.plugin.rendering.template
 
 import com.mitchellbosecke.pebble.PebbleEngine
 import com.mitchellbosecke.pebble.loader.ClasspathLoader
 import io.javalin.core.util.OptionalDependency
 import io.javalin.core.util.Util
-import io.javalin.rendering.FileRenderer
+import io.javalin.plugin.rendering.FileRenderer
 import java.io.StringWriter
 
 object JavalinPebble : FileRenderer {
diff --git a/src/main/java/io/javalin/rendering/template/JavalinThymeleaf.kt b/src/main/java/io/javalin/plugin/rendering/template/JavalinThymeleaf.kt
similarity index 92%
rename from src/main/java/io/javalin/rendering/template/JavalinThymeleaf.kt
rename to src/main/java/io/javalin/plugin/rendering/template/JavalinThymeleaf.kt
index d09593ee..62e63285 100644
--- a/src/main/java/io/javalin/rendering/template/JavalinThymeleaf.kt
+++ b/src/main/java/io/javalin/plugin/rendering/template/JavalinThymeleaf.kt
@@ -4,11 +4,11 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering.template
+package io.javalin.plugin.rendering.template
 
 import io.javalin.core.util.OptionalDependency
 import io.javalin.core.util.Util
-import io.javalin.rendering.FileRenderer
+import io.javalin.plugin.rendering.FileRenderer
 import org.thymeleaf.TemplateEngine
 import org.thymeleaf.context.Context
 import org.thymeleaf.templatemode.TemplateMode
diff --git a/src/main/java/io/javalin/rendering/template/JavalinVelocity.kt b/src/main/java/io/javalin/plugin/rendering/template/JavalinVelocity.kt
similarity index 94%
rename from src/main/java/io/javalin/rendering/template/JavalinVelocity.kt
rename to src/main/java/io/javalin/plugin/rendering/template/JavalinVelocity.kt
index ec5ecedd..069d03c9 100644
--- a/src/main/java/io/javalin/rendering/template/JavalinVelocity.kt
+++ b/src/main/java/io/javalin/plugin/rendering/template/JavalinVelocity.kt
@@ -4,11 +4,11 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering.template
+package io.javalin.plugin.rendering.template
 
 import io.javalin.core.util.OptionalDependency
 import io.javalin.core.util.Util
-import io.javalin.rendering.FileRenderer
+import io.javalin.plugin.rendering.FileRenderer
 import org.apache.velocity.VelocityContext
 import org.apache.velocity.app.VelocityEngine
 import java.io.StringWriter
diff --git a/src/main/java/io/javalin/rendering/template/TemplateUtil.kt b/src/main/java/io/javalin/plugin/rendering/template/TemplateUtil.kt
similarity index 91%
rename from src/main/java/io/javalin/rendering/template/TemplateUtil.kt
rename to src/main/java/io/javalin/plugin/rendering/template/TemplateUtil.kt
index 42500874..56f71ab4 100644
--- a/src/main/java/io/javalin/rendering/template/TemplateUtil.kt
+++ b/src/main/java/io/javalin/plugin/rendering/template/TemplateUtil.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.rendering.template
+package io.javalin.plugin.rendering.template
 
 object TemplateUtil {
     @JvmStatic
diff --git a/src/main/java/io/javalin/core/JavalinWsServlet.kt b/src/main/java/io/javalin/websocket/JavalinWsServlet.kt
similarity index 93%
rename from src/main/java/io/javalin/core/JavalinWsServlet.kt
rename to src/main/java/io/javalin/websocket/JavalinWsServlet.kt
index b9e21773..5af01e0c 100644
--- a/src/main/java/io/javalin/core/JavalinWsServlet.kt
+++ b/src/main/java/io/javalin/websocket/JavalinWsServlet.kt
@@ -4,16 +4,16 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core
+package io.javalin.websocket
 
-import io.javalin.Context
 import io.javalin.Javalin
-import io.javalin.UnauthorizedResponse
-import io.javalin.core.util.ContextUtil
+import io.javalin.core.JavalinConfig
+import io.javalin.core.security.Role
 import io.javalin.core.util.Header
 import io.javalin.core.util.Util
-import io.javalin.security.Role
-import io.javalin.websocket.*
+import io.javalin.http.Context
+import io.javalin.http.UnauthorizedResponse
+import io.javalin.http.util.ContextUtil
 import org.eclipse.jetty.websocket.servlet.WebSocketCreator
 import org.eclipse.jetty.websocket.servlet.WebSocketServlet
 import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory
diff --git a/src/main/java/io/javalin/websocket/BinaryMessageHandler.java b/src/main/java/io/javalin/websocket/WsBinaryMessageHandler.java
similarity index 88%
rename from src/main/java/io/javalin/websocket/BinaryMessageHandler.java
rename to src/main/java/io/javalin/websocket/WsBinaryMessageHandler.java
index 6d0e50e9..a9db2c6e 100644
--- a/src/main/java/io/javalin/websocket/BinaryMessageHandler.java
+++ b/src/main/java/io/javalin/websocket/WsBinaryMessageHandler.java
@@ -9,6 +9,6 @@ package io.javalin.websocket;
 import org.jetbrains.annotations.NotNull;
 
 @FunctionalInterface
-public interface BinaryMessageHandler {
+public interface WsBinaryMessageHandler {
     void handleBinaryMessage(@NotNull WsBinaryMessageContext ctx) throws Exception;
 }
diff --git a/src/main/java/io/javalin/websocket/CloseHandler.java b/src/main/java/io/javalin/websocket/WsCloseHandler.java
similarity index 90%
rename from src/main/java/io/javalin/websocket/CloseHandler.java
rename to src/main/java/io/javalin/websocket/WsCloseHandler.java
index 949fe416..d70e6eb1 100644
--- a/src/main/java/io/javalin/websocket/CloseHandler.java
+++ b/src/main/java/io/javalin/websocket/WsCloseHandler.java
@@ -9,6 +9,6 @@ package io.javalin.websocket;
 import org.jetbrains.annotations.NotNull;
 
 @FunctionalInterface
-public interface CloseHandler {
+public interface WsCloseHandler {
     void handleClose(@NotNull WsCloseContext ctx) throws Exception;
 }
diff --git a/src/main/java/io/javalin/websocket/ConnectHandler.java b/src/main/java/io/javalin/websocket/WsConnectHandler.java
similarity index 89%
rename from src/main/java/io/javalin/websocket/ConnectHandler.java
rename to src/main/java/io/javalin/websocket/WsConnectHandler.java
index 9107c1e3..97a57cc7 100644
--- a/src/main/java/io/javalin/websocket/ConnectHandler.java
+++ b/src/main/java/io/javalin/websocket/WsConnectHandler.java
@@ -9,6 +9,6 @@ package io.javalin.websocket;
 import org.jetbrains.annotations.NotNull;
 
 @FunctionalInterface
-public interface ConnectHandler {
+public interface WsConnectHandler {
     void handleConnect(@NotNull WsConnectContext ctx) throws Exception;
 }
diff --git a/src/main/java/io/javalin/websocket/WsContext.kt b/src/main/java/io/javalin/websocket/WsContext.kt
index a085d3a9..b8e80331 100644
--- a/src/main/java/io/javalin/websocket/WsContext.kt
+++ b/src/main/java/io/javalin/websocket/WsContext.kt
@@ -6,8 +6,8 @@
 
 package io.javalin.websocket
 
-import io.javalin.Context
-import io.javalin.json.JavalinJson
+import io.javalin.http.Context
+import io.javalin.plugin.json.JavalinJson
 import org.eclipse.jetty.websocket.api.RemoteEndpoint
 import org.eclipse.jetty.websocket.api.Session
 import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest
diff --git a/src/main/java/io/javalin/websocket/ErrorHandler.java b/src/main/java/io/javalin/websocket/WsErrorHandler.java
similarity index 90%
rename from src/main/java/io/javalin/websocket/ErrorHandler.java
rename to src/main/java/io/javalin/websocket/WsErrorHandler.java
index 866fb8c5..a8c26f9f 100644
--- a/src/main/java/io/javalin/websocket/ErrorHandler.java
+++ b/src/main/java/io/javalin/websocket/WsErrorHandler.java
@@ -9,6 +9,6 @@ package io.javalin.websocket;
 import org.jetbrains.annotations.NotNull;
 
 @FunctionalInterface
-public interface ErrorHandler {
+public interface WsErrorHandler {
     void handleError(@NotNull WsErrorContext ctx) throws Exception;
 }
diff --git a/src/main/java/io/javalin/websocket/WsHandler.java b/src/main/java/io/javalin/websocket/WsHandler.java
index ff73d63f..63333813 100644
--- a/src/main/java/io/javalin/websocket/WsHandler.java
+++ b/src/main/java/io/javalin/websocket/WsHandler.java
@@ -10,55 +10,55 @@ import org.jetbrains.annotations.NotNull;
 
 public class WsHandler {
 
-    ConnectHandler connectHandler = null;
-    MessageHandler messageHandler = null;
-    BinaryMessageHandler binaryMessageHandler = null;
-    CloseHandler closeHandler = null;
-    ErrorHandler errorHandler = null;
+    WsConnectHandler wsConnectHandler = null;
+    WsMessageHandler wsMessageHandler = null;
+    WsBinaryMessageHandler wsBinaryMessageHandler = null;
+    WsCloseHandler wsCloseHandler = null;
+    WsErrorHandler wsErrorHandler = null;
 
     /**
-     * Add a ConnectHandler to the WsHandler.
+     * Add a WsConnectHandler to the WsHandler.
      * The handler is called when a WebSocket client connects.
      */
-    public void onConnect(@NotNull ConnectHandler connectHandler) {
-        this.connectHandler = connectHandler;
+    public void onConnect(@NotNull WsConnectHandler wsConnectHandler) {
+        this.wsConnectHandler = wsConnectHandler;
     }
 
     /**
-     * Add a MessageHandler to the WsHandler.
+     * Add a WsMessageHandler to the WsHandler.
      * The handler is called when a WebSocket client sends
      * a String message.
      */
-    public void onMessage(@NotNull MessageHandler messageHandler) {
-        this.messageHandler = messageHandler;
+    public void onMessage(@NotNull WsMessageHandler wsMessageHandler) {
+        this.wsMessageHandler = wsMessageHandler;
     }
 
     /**
-     * Add a {@link BinaryMessageHandler} to the WsHandler.
+     * Add a {@link WsBinaryMessageHandler} to the WsHandler.
      * The handler is called when a WebSocket client sends
      * a binary message.
      */
-    public void onBinaryMessage(@NotNull BinaryMessageHandler binaryMessageHandler) {
-        this.binaryMessageHandler = binaryMessageHandler;
+    public void onBinaryMessage(@NotNull WsBinaryMessageHandler wsBinaryMessageHandler) {
+        this.wsBinaryMessageHandler = wsBinaryMessageHandler;
     }
 
     /**
-     * Add a CloseHandler to the WsHandler.
+     * Add a WsCloseHandler to the WsHandler.
      * The handler is called when a WebSocket client closes
      * the connection. The handler is not called in case of
      * network issues, only when the client actively closes the
      * connection (or times out).
      */
-    public void onClose(@NotNull CloseHandler closeHandler) {
-        this.closeHandler = closeHandler;
+    public void onClose(@NotNull WsCloseHandler wsCloseHandler) {
+        this.wsCloseHandler = wsCloseHandler;
     }
 
     /**
-     * Add a errorHandler to the WsHandler.
+     * Add a wsErrorHandler to the WsHandler.
      * The handler is called when an error is detected.
      */
-    public void onError(@NotNull ErrorHandler errorHandler) {
-        this.errorHandler = errorHandler;
+    public void onError(@NotNull WsErrorHandler wsErrorHandler) {
+        this.wsErrorHandler = wsErrorHandler;
     }
 
 }
diff --git a/src/main/java/io/javalin/websocket/WsHandlerController.kt b/src/main/java/io/javalin/websocket/WsHandlerController.kt
index 517a71dd..101977bc 100644
--- a/src/main/java/io/javalin/websocket/WsHandlerController.kt
+++ b/src/main/java/io/javalin/websocket/WsHandlerController.kt
@@ -25,42 +25,42 @@ class WsHandlerController(val matcher: WsPathMatcher, val exceptionMapper: WsExc
     fun onConnect(session: Session) {
         sessionIds[session] = UUID.randomUUID().toString() // associate a unique ID with this new session
         val ctx = WsConnectContext(sessionIds[session]!!, session)
-        tryBeforeAndEndpointHandlers(ctx) { it.handler.connectHandler?.handleConnect(ctx) }
-        tryAfterHandlers(ctx) { it.handler.connectHandler?.handleConnect(ctx) }
-        wsLogger?.connectHandler?.handleConnect(ctx)
+        tryBeforeAndEndpointHandlers(ctx) { it.handler.wsConnectHandler?.handleConnect(ctx) }
+        tryAfterHandlers(ctx) { it.handler.wsConnectHandler?.handleConnect(ctx) }
+        wsLogger?.wsConnectHandler?.handleConnect(ctx)
     }
 
     @OnWebSocketMessage
     fun onMessage(session: Session, message: String) {
         val ctx = WsMessageContext(sessionIds[session]!!, session, message)
-        tryBeforeAndEndpointHandlers(ctx) { it.handler.messageHandler?.handleMessage(ctx) }
-        tryAfterHandlers(ctx) { it.handler.messageHandler?.handleMessage(ctx) }
-        wsLogger?.messageHandler?.handleMessage(ctx)
+        tryBeforeAndEndpointHandlers(ctx) { it.handler.wsMessageHandler?.handleMessage(ctx) }
+        tryAfterHandlers(ctx) { it.handler.wsMessageHandler?.handleMessage(ctx) }
+        wsLogger?.wsMessageHandler?.handleMessage(ctx)
     }
 
     @OnWebSocketMessage
     fun onMessage(session: Session, buffer: ByteArray, offset: Int, length: Int) {
         val ctx = WsBinaryMessageContext(sessionIds[session]!!, session, buffer.toTypedArray(), offset, length)
-        tryBeforeAndEndpointHandlers(ctx) { it.handler.binaryMessageHandler?.handleBinaryMessage(ctx) }
-        tryAfterHandlers(ctx) { it.handler.binaryMessageHandler?.handleBinaryMessage(ctx) }
-        wsLogger?.binaryMessageHandler?.handleBinaryMessage(ctx)
+        tryBeforeAndEndpointHandlers(ctx) { it.handler.wsBinaryMessageHandler?.handleBinaryMessage(ctx) }
+        tryAfterHandlers(ctx) { it.handler.wsBinaryMessageHandler?.handleBinaryMessage(ctx) }
+        wsLogger?.wsBinaryMessageHandler?.handleBinaryMessage(ctx)
     }
 
     @OnWebSocketClose
     fun onClose(session: Session, statusCode: Int, reason: String?) {
         val ctx = WsCloseContext(sessionIds[session]!!, session, statusCode, reason)
-        tryBeforeAndEndpointHandlers(ctx) { it.handler.closeHandler?.handleClose(ctx) }
-        tryAfterHandlers(ctx) { it.handler.closeHandler?.handleClose(ctx) }
-        wsLogger?.closeHandler?.handleClose(ctx)
+        tryBeforeAndEndpointHandlers(ctx) { it.handler.wsCloseHandler?.handleClose(ctx) }
+        tryAfterHandlers(ctx) { it.handler.wsCloseHandler?.handleClose(ctx) }
+        wsLogger?.wsCloseHandler?.handleClose(ctx)
         sessionIds.remove(session) // the socket has been closed, we no longer need to keep track of the session ID
     }
 
     @OnWebSocketError
     fun onError(session: Session, throwable: Throwable?) {
         val ctx = WsErrorContext(sessionIds[session]!!, session, throwable)
-        tryBeforeAndEndpointHandlers(ctx) { it.handler.errorHandler?.handleError(ctx) }
-        tryAfterHandlers(ctx) { it.handler.errorHandler?.handleError(ctx) }
-        wsLogger?.errorHandler?.handleError(ctx)
+        tryBeforeAndEndpointHandlers(ctx) { it.handler.wsErrorHandler?.handleError(ctx) }
+        tryAfterHandlers(ctx) { it.handler.wsErrorHandler?.handleError(ctx) }
+        wsLogger?.wsErrorHandler?.handleError(ctx)
     }
 
     private fun tryBeforeAndEndpointHandlers(ctx: WsContext, handle: (WsEntry) -> Unit) {
diff --git a/src/main/java/io/javalin/core/WsHandlerType.kt b/src/main/java/io/javalin/websocket/WsHandlerType.kt
similarity index 90%
rename from src/main/java/io/javalin/core/WsHandlerType.kt
rename to src/main/java/io/javalin/websocket/WsHandlerType.kt
index ffd421f8..6043156d 100644
--- a/src/main/java/io/javalin/core/WsHandlerType.kt
+++ b/src/main/java/io/javalin/websocket/WsHandlerType.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.core
+package io.javalin.websocket
 
 /**
  * The available WebSocket handler types.
diff --git a/src/main/java/io/javalin/websocket/MessageHandler.java b/src/main/java/io/javalin/websocket/WsMessageHandler.java
similarity index 89%
rename from src/main/java/io/javalin/websocket/MessageHandler.java
rename to src/main/java/io/javalin/websocket/WsMessageHandler.java
index b077930e..5e628dc9 100644
--- a/src/main/java/io/javalin/websocket/MessageHandler.java
+++ b/src/main/java/io/javalin/websocket/WsMessageHandler.java
@@ -9,6 +9,6 @@ package io.javalin.websocket;
 import org.jetbrains.annotations.NotNull;
 
 @FunctionalInterface
-public interface MessageHandler {
+public interface WsMessageHandler {
     void handleMessage(@NotNull WsMessageContext ctx) throws Exception;
 }
diff --git a/src/main/java/io/javalin/websocket/WsPathMatcher.kt b/src/main/java/io/javalin/websocket/WsPathMatcher.kt
index f2d0998b..66629cad 100644
--- a/src/main/java/io/javalin/websocket/WsPathMatcher.kt
+++ b/src/main/java/io/javalin/websocket/WsPathMatcher.kt
@@ -7,8 +7,7 @@
 package io.javalin.websocket
 
 import io.javalin.core.PathParser
-import io.javalin.core.WsHandlerType
-import io.javalin.security.Role
+import io.javalin.core.security.Role
 import java.util.*
 
 data class WsEntry(val type: WsHandlerType, val path: String, val handler: WsHandler, val permittedRoles: Set<Role>) {


Test patch:
diff --git a/src/test/java/io/javalin/TestAccessManager.kt b/src/test/java/io/javalin/TestAccessManager.kt
index 1cfa5eeb..1d5864fe 100644
--- a/src/test/java/io/javalin/TestAccessManager.kt
+++ b/src/test/java/io/javalin/TestAccessManager.kt
@@ -11,8 +11,8 @@ import com.mashape.unirest.http.Unirest
 import io.javalin.TestAccessManager.MyRoles.ROLE_ONE
 import io.javalin.TestAccessManager.MyRoles.ROLE_TWO
 import io.javalin.apibuilder.ApiBuilder.get
-import io.javalin.security.Role
-import io.javalin.security.SecurityUtil.roles
+import io.javalin.core.security.Role
+import io.javalin.core.security.SecurityUtil.roles
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestApiBuilder.kt b/src/test/java/io/javalin/TestApiBuilder.kt
index 8467b12f..c79ce436 100644
--- a/src/test/java/io/javalin/TestApiBuilder.kt
+++ b/src/test/java/io/javalin/TestApiBuilder.kt
@@ -13,6 +13,8 @@ import io.javalin.TestUtil.okHandler
 import io.javalin.apibuilder.ApiBuilder
 import io.javalin.apibuilder.ApiBuilder.*
 import io.javalin.apibuilder.CrudHandler
+import io.javalin.http.Context
+import io.javalin.http.Handler
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestConfiguration.kt b/src/test/java/io/javalin/TestConfiguration.kt
index 1cb4284f..ea8c8f65 100644
--- a/src/test/java/io/javalin/TestConfiguration.kt
+++ b/src/test/java/io/javalin/TestConfiguration.kt
@@ -6,8 +6,8 @@
 
 package io.javalin
 
-import io.javalin.security.SecurityUtil.roles
-import io.javalin.staticfiles.Location
+import io.javalin.core.security.SecurityUtil.roles
+import io.javalin.http.staticfiles.Location
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.server.Server
 import org.eclipse.jetty.server.session.SessionHandler
diff --git a/src/test/java/io/javalin/TestContextExtensions.java b/src/test/java/io/javalin/TestContextExtensions.java
index 39801cfd..380bb3f7 100644
--- a/src/test/java/io/javalin/TestContextExtensions.java
+++ b/src/test/java/io/javalin/TestContextExtensions.java
@@ -8,6 +8,7 @@ package io.javalin;
 
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import io.javalin.http.Context;
 import io.javalin.misc.SerializeableObject;
 import org.junit.Test;
 import static org.assertj.core.api.Assertions.assertThat;
diff --git a/src/test/java/io/javalin/TestCookieStore.kt b/src/test/java/io/javalin/TestCookieStore.kt
index 67c27373..b194131e 100644
--- a/src/test/java/io/javalin/TestCookieStore.kt
+++ b/src/test/java/io/javalin/TestCookieStore.kt
@@ -6,7 +6,7 @@
 
 package io.javalin
 
-import io.javalin.rendering.template.TemplateUtil
+import io.javalin.plugin.rendering.template.TemplateUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.util.*
diff --git a/src/test/java/io/javalin/TestCustomJetty.kt b/src/test/java/io/javalin/TestCustomJetty.kt
index 1b6bca8f..34cb6a89 100644
--- a/src/test/java/io/javalin/TestCustomJetty.kt
+++ b/src/test/java/io/javalin/TestCustomJetty.kt
@@ -8,7 +8,7 @@
 package io.javalin
 
 import com.mashape.unirest.http.Unirest
-import io.javalin.util.TestServlet
+import io.javalin.misc.TestServlet
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.server.Handler
 import org.eclipse.jetty.server.RequestLog
diff --git a/src/test/java/io/javalin/TestExceptionMapper.kt b/src/test/java/io/javalin/TestExceptionMapper.kt
index a29a0322..e84b3ccf 100644
--- a/src/test/java/io/javalin/TestExceptionMapper.kt
+++ b/src/test/java/io/javalin/TestExceptionMapper.kt
@@ -7,10 +7,12 @@
 
 package io.javalin
 
+import io.javalin.http.BadRequestResponse
+import io.javalin.http.HttpResponseException
+import io.javalin.http.NotFoundResponse
 import io.javalin.misc.TypedException
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
-import java.lang.IllegalArgumentException
 import java.lang.NumberFormatException
 import kotlin.reflect.full.allSuperclasses
 
diff --git a/src/test/java/io/javalin/TestExtension.java b/src/test/java/io/javalin/TestExtension.java
index ea4015e5..d6f7a9bd 100644
--- a/src/test/java/io/javalin/TestExtension.java
+++ b/src/test/java/io/javalin/TestExtension.java
@@ -5,6 +5,9 @@
  */
 package io.javalin;
 
+import io.javalin.core.Extension;
+import io.javalin.http.BadRequestResponse;
+import io.javalin.http.UnauthorizedResponse;
 import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
diff --git a/src/test/java/io/javalin/TestHttpResponseExceptions.kt b/src/test/java/io/javalin/TestHttpResponseExceptions.kt
index 18a51b2f..73a2fad3 100644
--- a/src/test/java/io/javalin/TestHttpResponseExceptions.kt
+++ b/src/test/java/io/javalin/TestHttpResponseExceptions.kt
@@ -7,6 +7,10 @@
 package io.javalin
 
 import io.javalin.core.util.Header
+import io.javalin.http.BadRequestResponse
+import io.javalin.http.ForbiddenResponse
+import io.javalin.http.HttpResponseException
+import io.javalin.http.UnauthorizedResponse
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.http.HttpStatus
 import org.junit.Test
diff --git a/src/test/java/io/javalin/TestJson.kt b/src/test/java/io/javalin/TestJson.kt
index e844b19a..14160a20 100644
--- a/src/test/java/io/javalin/TestJson.kt
+++ b/src/test/java/io/javalin/TestJson.kt
@@ -6,19 +6,27 @@
 
 package io.javalin
 
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.databind.SerializationFeature
 import com.google.gson.GsonBuilder
 import com.mashape.unirest.http.Unirest
-import io.javalin.json.FromJsonMapper
-import io.javalin.json.JavalinJackson
-import io.javalin.json.JavalinJson
-import io.javalin.json.ToJsonMapper
-import io.javalin.misc.CustomMapper
+import io.javalin.plugin.json.FromJsonMapper
+import io.javalin.plugin.json.JavalinJackson
+import io.javalin.plugin.json.JavalinJson
+import io.javalin.plugin.json.ToJsonMapper
 import io.javalin.misc.NonSerializableObject
 import io.javalin.misc.SerializeableObject
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Before
 import org.junit.Test
 
+class CustomMapper : ObjectMapper() {
+    init {
+        this.enable(SerializationFeature.INDENT_OUTPUT)
+        this.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
+    }
+}
+
 class TestJson {
 
     @Before
diff --git a/src/test/java/io/javalin/TestLogging.kt b/src/test/java/io/javalin/TestLogging.kt
index 4f02181f..56a8e44c 100644
--- a/src/test/java/io/javalin/TestLogging.kt
+++ b/src/test/java/io/javalin/TestLogging.kt
@@ -6,7 +6,7 @@
 
 package io.javalin
 
-import io.javalin.util.HttpUtil
+import io.javalin.misc.HttpUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.util.concurrent.CompletableFuture
diff --git a/src/test/java/io/javalin/TestMultipartForms.kt b/src/test/java/io/javalin/TestMultipartForms.kt
index 46e1802c..8023d62d 100644
--- a/src/test/java/io/javalin/TestMultipartForms.kt
+++ b/src/test/java/io/javalin/TestMultipartForms.kt
@@ -6,7 +6,7 @@
 
 package io.javalin
 
-import io.javalin.json.JavalinJackson
+import io.javalin.plugin.json.JavalinJackson
 import io.javalin.misc.UploadInfo
 import okhttp3.*
 import org.apache.commons.io.IOUtils
diff --git a/src/test/java/io/javalin/TestSinglePageMode.kt b/src/test/java/io/javalin/TestSinglePageMode.kt
index 3a404fdf..7851e3b0 100644
--- a/src/test/java/io/javalin/TestSinglePageMode.kt
+++ b/src/test/java/io/javalin/TestSinglePageMode.kt
@@ -9,7 +9,7 @@ package io.javalin
 import com.mashape.unirest.http.Unirest
 import io.javalin.core.util.Header
 import io.javalin.core.util.OptionalDependency
-import io.javalin.staticfiles.Location
+import io.javalin.http.staticfiles.Location
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.io.File
diff --git a/src/test/java/io/javalin/TestSse.java b/src/test/java/io/javalin/TestSse.java
index 00a8de29..b1ece4a5 100644
--- a/src/test/java/io/javalin/TestSse.java
+++ b/src/test/java/io/javalin/TestSse.java
@@ -1,7 +1,7 @@
 package io.javalin;
 
 import com.mashape.unirest.http.Headers;
-import io.javalin.serversentevent.SseClient;
+import io.javalin.http.sse.SseClient;
 import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
diff --git a/src/test/java/io/javalin/TestStaticFiles.kt b/src/test/java/io/javalin/TestStaticFiles.kt
index 6ddd497e..dd60308b 100644
--- a/src/test/java/io/javalin/TestStaticFiles.kt
+++ b/src/test/java/io/javalin/TestStaticFiles.kt
@@ -9,7 +9,8 @@ package io.javalin
 
 import io.javalin.core.util.Header
 import io.javalin.core.util.OptionalDependency
-import io.javalin.staticfiles.Location
+import io.javalin.http.UnauthorizedResponse
+import io.javalin.http.staticfiles.Location
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestStaticFilesEdgeCases.kt b/src/test/java/io/javalin/TestStaticFilesEdgeCases.kt
index bb4e7e18..d34ba99e 100644
--- a/src/test/java/io/javalin/TestStaticFilesEdgeCases.kt
+++ b/src/test/java/io/javalin/TestStaticFilesEdgeCases.kt
@@ -7,7 +7,7 @@
 package io.javalin
 
 import io.javalin.core.util.FileUtil
-import io.javalin.staticfiles.Location
+import io.javalin.http.staticfiles.Location
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.io.File
diff --git a/src/test/java/io/javalin/TestTemplates.kt b/src/test/java/io/javalin/TestTemplates.kt
index 056d85dc..8180ca6e 100644
--- a/src/test/java/io/javalin/TestTemplates.kt
+++ b/src/test/java/io/javalin/TestTemplates.kt
@@ -9,12 +9,12 @@ package io.javalin
 
 import com.mitchellbosecke.pebble.PebbleEngine
 import com.mitchellbosecke.pebble.loader.ClasspathLoader
-import io.javalin.rendering.FileRenderer
-import io.javalin.rendering.JavalinRenderer
-import io.javalin.rendering.template.JavalinJtwig
-import io.javalin.rendering.template.JavalinPebble
-import io.javalin.rendering.template.JavalinVelocity
-import io.javalin.rendering.template.TemplateUtil.model
+import io.javalin.plugin.rendering.FileRenderer
+import io.javalin.plugin.rendering.JavalinRenderer
+import io.javalin.plugin.rendering.template.JavalinJtwig
+import io.javalin.plugin.rendering.template.JavalinPebble
+import io.javalin.plugin.rendering.template.JavalinVelocity
+import io.javalin.plugin.rendering.template.TemplateUtil.model
 import org.apache.velocity.app.VelocityEngine
 import org.assertj.core.api.Assertions.assertThat
 import org.jtwig.environment.EnvironmentConfigurationBuilder
diff --git a/src/test/java/io/javalin/TestUtil.java b/src/test/java/io/javalin/TestUtil.java
index e54add17..33b14b64 100644
--- a/src/test/java/io/javalin/TestUtil.java
+++ b/src/test/java/io/javalin/TestUtil.java
@@ -7,10 +7,11 @@
 package io.javalin;
 
 import com.mashape.unirest.http.HttpMethod;
-import io.javalin.json.JavalinJackson;
-import io.javalin.json.JavalinJson;
-import io.javalin.util.HttpUtil;
-import io.javalin.util.ThrowingBiConsumer;
+import io.javalin.http.Handler;
+import io.javalin.plugin.json.JavalinJackson;
+import io.javalin.plugin.json.JavalinJson;
+import io.javalin.misc.HttpUtil;
+import io.javalin.misc.ThrowingBiConsumer;
 
 public class TestUtil {
 
diff --git a/src/test/java/io/javalin/validation/TestValidation.kt b/src/test/java/io/javalin/TestValidation.kt
similarity index 96%
rename from src/test/java/io/javalin/validation/TestValidation.kt
rename to src/test/java/io/javalin/TestValidation.kt
index 94a452bb..2b14af28 100644
--- a/src/test/java/io/javalin/validation/TestValidation.kt
+++ b/src/test/java/io/javalin/TestValidation.kt
@@ -4,11 +4,12 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.validation
+package io.javalin
 
-import io.javalin.BadRequestResponse
-import io.javalin.TestUtil
-import io.javalin.json.JavalinJson
+import io.javalin.http.BadRequestResponse
+import io.javalin.core.validation.JavalinValidation
+import io.javalin.core.validation.Validator
+import io.javalin.plugin.json.JavalinJson
 import io.javalin.misc.SerializeableObject
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.http.HttpStatus
diff --git a/src/test/java/io/javalin/validation/TestValidation_Java.java b/src/test/java/io/javalin/TestValidation_Java.java
similarity index 92%
rename from src/test/java/io/javalin/validation/TestValidation_Java.java
rename to src/test/java/io/javalin/TestValidation_Java.java
index aafa5167..bb3b5b81 100644
--- a/src/test/java/io/javalin/validation/TestValidation_Java.java
+++ b/src/test/java/io/javalin/TestValidation_Java.java
@@ -4,8 +4,10 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.validation;
+package io.javalin;
 
+import io.javalin.core.validation.JavalinValidation;
+import io.javalin.core.validation.Validator;
 import java.time.Instant;
 import org.junit.Test;
 import static org.assertj.core.api.Assertions.assertThat;
diff --git a/src/test/java/io/javalin/TestWebSocket.kt b/src/test/java/io/javalin/TestWebSocket.kt
index 8485f13a..124c0a87 100644
--- a/src/test/java/io/javalin/TestWebSocket.kt
+++ b/src/test/java/io/javalin/TestWebSocket.kt
@@ -8,7 +8,8 @@ package io.javalin
 
 import com.mashape.unirest.http.Unirest
 import io.javalin.apibuilder.ApiBuilder.ws
-import io.javalin.json.JavalinJson
+import io.javalin.http.UnauthorizedResponse
+import io.javalin.plugin.json.JavalinJson
 import io.javalin.misc.SerializeableObject
 import io.javalin.misc.TypedException
 import io.javalin.websocket.WsContext
diff --git a/src/test/java/io/javalin/examples/HelloWorldAuth.java b/src/test/java/io/javalin/examples/HelloWorldAuth.java
index 070c1b35..86b78821 100644
--- a/src/test/java/io/javalin/examples/HelloWorldAuth.java
+++ b/src/test/java/io/javalin/examples/HelloWorldAuth.java
@@ -8,13 +8,13 @@
 package io.javalin.examples;
 
 import io.javalin.Javalin;
-import io.javalin.security.Role;
+import io.javalin.core.security.Role;
 import static io.javalin.apibuilder.ApiBuilder.get;
 import static io.javalin.apibuilder.ApiBuilder.path;
 import static io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_ONE;
 import static io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_THREE;
 import static io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_TWO;
-import static io.javalin.security.SecurityUtil.roles;
+import static io.javalin.core.security.SecurityUtil.roles;
 
 public class HelloWorldAuth {
 
diff --git a/src/test/java/io/javalin/examples/HelloWorldGson.java b/src/test/java/io/javalin/examples/HelloWorldGson.java
index 05fa2f0e..67868a61 100644
--- a/src/test/java/io/javalin/examples/HelloWorldGson.java
+++ b/src/test/java/io/javalin/examples/HelloWorldGson.java
@@ -9,7 +9,7 @@ package io.javalin.examples;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import io.javalin.Javalin;
-import io.javalin.json.JavalinJson;
+import io.javalin.plugin.json.JavalinJson;
 import java.util.Arrays;
 
 public class HelloWorldGson {
diff --git a/src/test/java/io/javalin/examples/HelloWorldServlet.java b/src/test/java/io/javalin/examples/HelloWorldServlet.java
index fb9f337e..6c4f2079 100644
--- a/src/test/java/io/javalin/examples/HelloWorldServlet.java
+++ b/src/test/java/io/javalin/examples/HelloWorldServlet.java
@@ -7,7 +7,7 @@
 package io.javalin.examples;
 
 import io.javalin.Javalin;
-import io.javalin.util.TestServlet;
+import io.javalin.misc.TestServlet;
 import org.eclipse.jetty.server.Handler;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.handler.ContextHandlerCollection;
diff --git a/src/test/java/io/javalin/examples/HelloWorldSse.java b/src/test/java/io/javalin/examples/HelloWorldSse.java
index 4cb52887..5a0efbdf 100644
--- a/src/test/java/io/javalin/examples/HelloWorldSse.java
+++ b/src/test/java/io/javalin/examples/HelloWorldSse.java
@@ -7,7 +7,7 @@
 package io.javalin.examples;
 
 import io.javalin.Javalin;
-import io.javalin.serversentevent.SseClient;
+import io.javalin.http.sse.SseClient;
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.TimeUnit;
diff --git a/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java b/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java
index 99f924d4..70eb6424 100644
--- a/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java
+++ b/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java
@@ -7,7 +7,7 @@
 package io.javalin.examples;
 
 import io.javalin.Javalin;
-import io.javalin.staticfiles.Location;
+import io.javalin.http.staticfiles.Location;
 
 public class HelloWorldStaticFiles_external {
 
diff --git a/src/test/java/io/javalin/misc/CustomMapper.kt b/src/test/java/io/javalin/misc/CustomMapper.kt
deleted file mode 100644
index 244f68bb..00000000
--- a/src/test/java/io/javalin/misc/CustomMapper.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Javalin - https://javalin.io
- * Copyright 2017 David Åse
- * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
- */
-
-package io.javalin.misc
-
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.databind.SerializationFeature
-
-class CustomMapper : ObjectMapper() {
-    init {
-        this.enable(SerializationFeature.INDENT_OUTPUT)
-        this.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)
-    }
-}
diff --git a/src/test/java/io/javalin/util/HttpUtil.kt b/src/test/java/io/javalin/misc/HttpUtil.kt
similarity index 98%
rename from src/test/java/io/javalin/util/HttpUtil.kt
rename to src/test/java/io/javalin/misc/HttpUtil.kt
index 8a5dbbea..05f9d82a 100644
--- a/src/test/java/io/javalin/util/HttpUtil.kt
+++ b/src/test/java/io/javalin/misc/HttpUtil.kt
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.util
+package io.javalin.misc
 
 import com.mashape.unirest.http.HttpMethod
 import com.mashape.unirest.http.Unirest
diff --git a/src/test/java/io/javalin/util/TestServlet.java b/src/test/java/io/javalin/misc/TestServlet.java
similarity index 95%
rename from src/test/java/io/javalin/util/TestServlet.java
rename to src/test/java/io/javalin/misc/TestServlet.java
index 3361d7a2..61d97bc5 100644
--- a/src/test/java/io/javalin/util/TestServlet.java
+++ b/src/test/java/io/javalin/misc/TestServlet.java
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.util;
+package io.javalin.misc;
 
 import java.io.IOException;
 import java.io.PrintWriter;
diff --git a/src/test/java/io/javalin/util/ThrowingBiConsumer.java b/src/test/java/io/javalin/misc/ThrowingBiConsumer.java
similarity index 95%
rename from src/test/java/io/javalin/util/ThrowingBiConsumer.java
rename to src/test/java/io/javalin/misc/ThrowingBiConsumer.java
index 14f842ec..a6899154 100644
--- a/src/test/java/io/javalin/util/ThrowingBiConsumer.java
+++ b/src/test/java/io/javalin/misc/ThrowingBiConsumer.java
@@ -4,7 +4,7 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.util;
+package io.javalin.misc;
 
 import java.util.function.BiConsumer;
 
diff --git a/src/test/java/io/javalin/performance/RouteMatcherPerformanceTest.kt b/src/test/java/io/javalin/performance/RouteMatcherPerformanceTest.kt
index 46ae29dc..21f86c3b 100644
--- a/src/test/java/io/javalin/performance/RouteMatcherPerformanceTest.kt
+++ b/src/test/java/io/javalin/performance/RouteMatcherPerformanceTest.kt
@@ -3,10 +3,10 @@ package io.javalin.performance
 import com.carrotsearch.junitbenchmarks.BenchmarkOptions
 import com.carrotsearch.junitbenchmarks.BenchmarkRule
 import com.carrotsearch.junitbenchmarks.Clock
-import io.javalin.Handler
-import io.javalin.core.HandlerEntry
-import io.javalin.core.HandlerType
-import io.javalin.core.util.ContextUtil.urlDecode
+import io.javalin.http.Handler
+import io.javalin.http.HandlerEntry
+import io.javalin.http.HandlerType
+import io.javalin.http.util.ContextUtil.urlDecode
 import io.javalin.core.util.Util
 import org.junit.Ignore
 import org.junit.Rule
diff --git a/src/test/java/io/javalin/performance/StupidAsyncTest.java b/src/test/java/io/javalin/performance/StupidAsyncTest.java
index 845ad20a..6ed7e72b 100644
--- a/src/test/java/io/javalin/performance/StupidAsyncTest.java
+++ b/src/test/java/io/javalin/performance/StupidAsyncTest.java
@@ -7,7 +7,7 @@
 package io.javalin.performance;
 
 import io.javalin.Javalin;
-import io.javalin.util.HttpUtil;
+import io.javalin.misc.HttpUtil;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executors;
diff --git a/src/test/java/io/javalin/performance/StupidPerformanceTest.java b/src/test/java/io/javalin/performance/StupidPerformanceTest.java
index 3127f0b3..153c04c7 100644
--- a/src/test/java/io/javalin/performance/StupidPerformanceTest.java
+++ b/src/test/java/io/javalin/performance/StupidPerformanceTest.java
@@ -10,7 +10,7 @@ import com.carrotsearch.junitbenchmarks.BenchmarkOptions;
 import com.carrotsearch.junitbenchmarks.BenchmarkRule;
 import com.carrotsearch.junitbenchmarks.Clock;
 import com.mashape.unirest.http.Unirest;
-import io.javalin.Context;
+import io.javalin.http.Context;
 import io.javalin.Javalin;
 import io.javalin.apibuilder.CrudHandler;
 import org.junit.AfterClass;
diff --git a/src/test/java/io/javalin/misc/HandlerImplementation.java b/src/test/java/io/javalin/routeoverview/HandlerImplementation.java
similarity index 73%
rename from src/test/java/io/javalin/misc/HandlerImplementation.java
rename to src/test/java/io/javalin/routeoverview/HandlerImplementation.java
index 290243cc..2503860a 100644
--- a/src/test/java/io/javalin/misc/HandlerImplementation.java
+++ b/src/test/java/io/javalin/routeoverview/HandlerImplementation.java
@@ -4,10 +4,10 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.misc;
+package io.javalin.routeoverview;
 
-import io.javalin.Context;
-import io.javalin.Handler;
+import io.javalin.http.Context;
+import io.javalin.http.Handler;
 
 public class HandlerImplementation implements Handler {
     @Override
diff --git a/src/test/java/io/javalin/routeoverview/TestRouteOverview_Java.java b/src/test/java/io/javalin/routeoverview/TestRouteOverview_Java.java
index d0626910..ed79eeb2 100644
--- a/src/test/java/io/javalin/routeoverview/TestRouteOverview_Java.java
+++ b/src/test/java/io/javalin/routeoverview/TestRouteOverview_Java.java
@@ -6,10 +6,9 @@
 
 package io.javalin.routeoverview;
 
-import io.javalin.Context;
-import io.javalin.Handler;
+import io.javalin.http.Context;
+import io.javalin.http.Handler;
 import io.javalin.core.util.RouteOverviewUtil;
-import io.javalin.misc.HandlerImplementation;
 import org.junit.Ignore;
 import org.junit.Test;
 import static org.hamcrest.CoreMatchers.is;
@@ -30,8 +29,8 @@ public class TestRouteOverview_Java {
 
     @Test
     public void class_works() {
-        assertThat(RouteOverviewUtil.getMetaInfo(new ImplementingClass()), is("io.javalin.routeoverview.TestRouteOverview_Java$ImplementingClass.class"));
-        assertThat(RouteOverviewUtil.getMetaInfo(new HandlerImplementation()), is("io.javalin.misc.HandlerImplementation.class"));
+        assertThat(RouteOverviewUtil.getMetaInfo(new InnerHandlerImplementation()), is("io.javalin.routeoverview.TestRouteOverview_Java$InnerHandlerImplementation.class"));
+        assertThat(RouteOverviewUtil.getMetaInfo(new HandlerImplementation()), is("io.javalin.routeoverview.HandlerImplementation.class"));
     }
 
     @Test
@@ -45,7 +44,7 @@ public class TestRouteOverview_Java {
         assertThat(RouteOverviewUtil.getMetaInfo((Handler) (ctx -> ctx.result(""))), is("io.javalin.routeoverview.TestRouteOverview_Java::??? (anonymous lambda)"));
     }
 
-    private static class ImplementingClass implements Handler {
+    private static class InnerHandlerImplementation implements Handler {
         @Override
         public void handle(Context context) {
         }
diff --git a/src/test/java/io/javalin/routeoverview/TestRouteOverview_Kotlin.kt b/src/test/java/io/javalin/routeoverview/TestRouteOverview_Kotlin.kt
index f4ce0d07..498d8e25 100644
--- a/src/test/java/io/javalin/routeoverview/TestRouteOverview_Kotlin.kt
+++ b/src/test/java/io/javalin/routeoverview/TestRouteOverview_Kotlin.kt
@@ -6,8 +6,8 @@
 
 package io.javalin.routeoverview
 
-import io.javalin.Context
-import io.javalin.Handler
+import io.javalin.http.Context
+import io.javalin.http.Handler
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
@@ -60,8 +60,3 @@ class ClassHandlers {
     val lambdaField = Handler {}
     fun methodReference(ctx: Context) {}
 }
-
-class HandlerImplementation : Handler {
-    override fun handle(ctx: Context) {
-    }
-}
diff --git a/src/test/java/io/javalin/routeoverview/Util.java b/src/test/java/io/javalin/routeoverview/Util.java
index 405f3e99..4fafec8a 100644
--- a/src/test/java/io/javalin/routeoverview/Util.java
+++ b/src/test/java/io/javalin/routeoverview/Util.java
@@ -6,7 +6,7 @@
 
 package io.javalin.routeoverview;
 
-import io.javalin.Handler;
+import io.javalin.http.Handler;
 import io.javalin.core.util.RouteOverviewUtil;
 
 public class Util {
diff --git a/src/test/java/io/javalin/routeoverview/VisualTest.java b/src/test/java/io/javalin/routeoverview/VisualTest.java
index 5121674d..0891acd3 100644
--- a/src/test/java/io/javalin/routeoverview/VisualTest.java
+++ b/src/test/java/io/javalin/routeoverview/VisualTest.java
@@ -6,11 +6,10 @@
 
 package io.javalin.routeoverview;
 
-import io.javalin.Context;
-import io.javalin.Handler;
+import io.javalin.http.Context;
+import io.javalin.http.Handler;
 import io.javalin.Javalin;
-import io.javalin.core.HandlerType;
-import io.javalin.misc.HandlerImplementation;
+import io.javalin.http.HandlerType;
 import io.javalin.websocket.WsHandler;
 import static io.javalin.TestAccessManager.MyRoles.ROLE_ONE;
 import static io.javalin.TestAccessManager.MyRoles.ROLE_THREE;
@@ -20,7 +19,7 @@ import static io.javalin.apibuilder.ApiBuilder.get;
 import static io.javalin.apibuilder.ApiBuilder.patch;
 import static io.javalin.apibuilder.ApiBuilder.path;
 import static io.javalin.apibuilder.ApiBuilder.post;
-import static io.javalin.security.SecurityUtil.roles;
+import static io.javalin.core.security.SecurityUtil.roles;
 
 public class VisualTest {
 
diff --git a/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt b/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt
index fad2ef46..f905ebd0 100644
--- a/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt
+++ b/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt
@@ -7,7 +7,7 @@
 package io.javalin.examples
 
 import io.javalin.Javalin
-import io.javalin.serversentevent.SseClient
+import io.javalin.http.sse.SseClient
 import org.eclipse.jetty.server.Server
 import org.eclipse.jetty.util.thread.QueuedThreadPool
 import java.util.concurrent.ConcurrentLinkedQueue
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt b/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
index 9d9dbd90..825d7f50 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
@@ -6,14 +6,14 @@
 
 package io.javalin.examples
 
-import io.javalin.Context
-import io.javalin.Handler
+import io.javalin.http.Context
+import io.javalin.http.Handler
 import io.javalin.Javalin
 import io.javalin.apibuilder.ApiBuilder.get
 import io.javalin.apibuilder.ApiBuilder.path
 import io.javalin.examples.HelloWorldAuth.MyRoles.*
-import io.javalin.security.Role
-import io.javalin.security.SecurityUtil.roles
+import io.javalin.core.security.Role
+import io.javalin.core.security.SecurityUtil.roles
 
 enum class MyRoles : Role {
     ROLE_ONE, ROLE_TWO, ROLE_THREE
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldGson.kt b/src/test/kotlin/io/javalin/examples/HelloWorldGson.kt
index bc3e308b..96544eef 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldGson.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldGson.kt
@@ -8,9 +8,9 @@ package io.javalin.examples
 
 import com.google.gson.GsonBuilder
 import io.javalin.Javalin
-import io.javalin.json.FromJsonMapper
-import io.javalin.json.JavalinJson
-import io.javalin.json.ToJsonMapper
+import io.javalin.plugin.json.FromJsonMapper
+import io.javalin.plugin.json.JavalinJson
+import io.javalin.plugin.json.ToJsonMapper
 import java.util.*
 
 fun main(args: Array<String>) {
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldSse.kt b/src/test/kotlin/io/javalin/examples/HelloWorldSse.kt
index 27e4906f..76c6aeed 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldSse.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldSse.kt
@@ -7,7 +7,7 @@
 package io.javalin.examples
 
 import io.javalin.Javalin
-import io.javalin.serversentevent.SseClient
+import io.javalin.http.sse.SseClient
 import java.util.concurrent.ConcurrentLinkedQueue
 import java.util.concurrent.TimeUnit
 
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt b/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt
index b1f35fb7..c9e0a72e 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt
@@ -7,7 +7,7 @@
 package io.javalin.examples
 
 import io.javalin.Javalin
-import io.javalin.staticfiles.Location
+import io.javalin.http.staticfiles.Location
 
 fun main(args: Array<String>) {
     Javalin.create {