diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 0080322..9fb7f0a 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -7,7 +7,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: - java-version: 8 + java-version: 11 - uses: eskatos/gradle-command-action@v1 with: gradle-version: 6.6.1 diff --git a/build.gradle.kts b/build.gradle.kts index 6d26844..5666e77 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ group = "io.vacco.java-express" version = "0.2.1" configure { - addJ8Spec(); addPmd(); addSpotBugs() + addJ8Spec(); addPmd(); addSpotBugs(); addGoogleJavaFormat() setPublishingUrlTransform { repo -> "${repo.url}/${project.name}" } sharedLibrary() } diff --git a/src/main/java/express/DynExpress.java b/src/main/java/express/DynExpress.java index 947df3a..5573892 100644 --- a/src/main/java/express/DynExpress.java +++ b/src/main/java/express/DynExpress.java @@ -1,20 +1,20 @@ package express; import express.http.RequestMethod; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * @author Simon Reinisch * Annotation to use object methods as request handler. + * + * @author Simon Reinisch */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DynExpress { - RequestMethod method() default RequestMethod.GET; + RequestMethod method() default RequestMethod.GET; - String context() default "/"; + String context() default "/"; } diff --git a/src/main/java/express/Express.java b/src/main/java/express/Express.java index f9c4bcb..fef9534 100644 --- a/src/main/java/express/Express.java +++ b/src/main/java/express/Express.java @@ -10,7 +10,6 @@ import express.http.HttpRequestHandler; import express.http.request.Request; import express.http.response.Response; - import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -21,348 +20,362 @@ import java.util.concurrent.Executors; /** + * Core class of java-express. + * * @author Simon Reinisch - * Core class of java-express */ public class Express implements Router { - private final ConcurrentHashMap parameterListener; - private final ConcurrentHashMap locals; - - private final ArrayList worker; - private final FilterLayerHandler handler; - - private Executor executor; - private String hostname; - private HttpServer httpServer; - private HttpsConfigurator httpsConfigurator; - - { - // Initialize - parameterListener = new ConcurrentHashMap<>(); - locals = new ConcurrentHashMap<>(); - - worker = new ArrayList<>(); - handler = new FilterLayerHandler(2); - - executor = Executors.newCachedThreadPool(); - } - - /** - * Create an express instance and bind the server to an hostname. - * Default is "Localhost" - * - * @param hostname The host name - */ - public Express(String hostname) { - this.hostname = hostname; - } - - /** - * Default, will bind the server to "localhost" - * - * @param httpsConfigurator The HttpsConfigurator for https - */ - public Express(HttpsConfigurator httpsConfigurator) { - this.httpsConfigurator = httpsConfigurator; + private final ConcurrentHashMap parameterListener; + private final ConcurrentHashMap locals; + + private final ArrayList worker; + private final FilterLayerHandler handler; + + private Executor executor; + private String hostname; + private HttpServer httpServer; + private HttpsConfigurator httpsConfigurator; + + { + // Initialize + parameterListener = new ConcurrentHashMap<>(); + locals = new ConcurrentHashMap<>(); + + worker = new ArrayList<>(); + handler = new FilterLayerHandler(2); + + executor = Executors.newCachedThreadPool(); + } + + /** + * Create an express instance and bind the server to an hostname. Default is "Localhost" + * + * @param hostname The host name + */ + public Express(String hostname) { + this.hostname = hostname; + } + + /** + * Default, will bind the server to "localhost" + * + * @param httpsConfigurator The HttpsConfigurator for https + */ + public Express(HttpsConfigurator httpsConfigurator) { + this.httpsConfigurator = httpsConfigurator; + } + + /** + * Create an express instance and bind the server to an hostname. Default is "Localhost" + * + * @param hostname The host name + * @param httpsConfigurator The HttpsConfigurator for https + */ + public Express(String hostname, HttpsConfigurator httpsConfigurator) { + this.hostname = hostname; + this.httpsConfigurator = httpsConfigurator; + } + + /** Default, will bind the server to "localhost" */ + public Express() {} + + /** @return True if the server uses https. */ + public boolean isSecure() { + return httpsConfigurator != null; + } + + /** + * Add a listener which will be called when an url with this parameter is called. + * + * @param param The parameter name. + * @param request An request handler. + * @return Express this express instance + */ + public Express onParam(String param, HttpRequestHandler request) { + parameterListener.put(param, request); + return this; + } + + public ConcurrentHashMap getParameterListener() { + return parameterListener; + } + + /** + * Add an key-val pair to the express app, can be used to store data. Uses ConcurrentHashMap so + * it's thread save. + * + * @param key The key + * @param val The value + * @return The last value which was attached by this key, can be null. + */ + public Object set(String key, String val) { + return locals.put(key, val); + } + + /** + * Returns the value which was allocated by this key. + * + * @param key The key. + * @return The value. + */ + public Object get(String key) { + return locals.get(key); + } + + /** + * Set an executor service. Default is CachedThreadPool Can only changed if the server isn't + * already stardet. + * + * @param executor The new executor. + * @throws IOException If the server is currently running + */ + public void setExecutor(Executor executor) throws IOException { + if (httpServer != null) { + throw new IOException("Cannot set executor after the server has stardet!"); + } else { + this.executor = executor; } - - /** - * Create an express instance and bind the server to an hostname. - * Default is "Localhost" - * - * @param hostname The host name - * @param httpsConfigurator The HttpsConfigurator for https - */ - public Express(String hostname, HttpsConfigurator httpsConfigurator) { - this.hostname = hostname; - this.httpsConfigurator = httpsConfigurator; - } - - /** - * Default, will bind the server to "localhost" - */ - public Express() { - } - - /** - * @return True if the server uses https. - */ - public boolean isSecure() { - return httpsConfigurator != null; - } - - /** - * Add a listener which will be called when an url with this parameter is called. - * - * @param param The parameter name. - * @param request An request handler. - * @return Express this express instance - */ - public Express onParam(String param, HttpRequestHandler request) { - parameterListener.put(param, request); - return this; - } - - public ConcurrentHashMap getParameterListener() { - return parameterListener; + } + + /** + * Add an routing object. + * + * @param router The router. + * @return Express this express instance + */ + public Express use(ExpressRouter router) { + this.handler.combine(router.getHandler()); + this.worker.addAll(router.getWorker()); + return this; + } + + /** + * Add an routing object with an specific root root. + * + * @param root The root path for all request to this router. + * @param router The router. + * @return Express this express instance + */ + @SuppressWarnings("unchecked") + public Express use(String root, ExpressRouter router) { + + router + .getHandler() + .forEach( + fl -> + fl.getFilter() + .forEach( + layer -> { + ((FilterImpl) layer).setRoot(root); + })); + + this.handler.combine(router.getHandler()); + this.worker.addAll(router.getWorker()); + + return this; + } + + public Express use(HttpRequestHandler middleware) { + addMiddleware("*", "*", middleware); + return this; + } + + public Express use(String context, HttpRequestHandler middleware) { + addMiddleware("*", context, middleware); + return this; + } + + public Express use(String context, String requestMethod, HttpRequestHandler middleware) { + addMiddleware(requestMethod.toUpperCase(), context, middleware); + return this; + } + + // Internal service to handle middleware + private void addMiddleware(String requestMethod, String context, HttpRequestHandler middleware) { + if (middleware instanceof FilterTask) { + worker.add(new FilterWorker((FilterTask) middleware)); } - /** - * Add an key-val pair to the express app, can be used - * to store data. Uses ConcurrentHashMap so it's thread save. - * - * @param key The key - * @param val The value - * @return The last value which was attached by this key, can be null. - */ - public Object set(String key, String val) { - return locals.put(key, val); - } - - /** - * Returns the value which was allocated by this key. - * - * @param key The key. - * @return The value. - */ - public Object get(String key) { - return locals.get(key); - } - - /** - * Set an executor service. Default is CachedThreadPool - * Can only changed if the server isn't already stardet. - * - * @param executor The new executor. - * @throws IOException If the server is currently running - */ - public void setExecutor(Executor executor) throws IOException { - if (httpServer != null) { - throw new IOException("Cannot set executor after the server has stardet!"); - } else { - this.executor = executor; + handler.add(0, new FilterImpl(requestMethod, context, middleware)); + } + + public Express all(HttpRequestHandler request) { + handler.add(1, new FilterImpl("*", "*", request)); + return this; + } + + public Express all(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("*", context, request)); + return this; + } + + public Express all(String context, String requestMethod, HttpRequestHandler request) { + handler.add(1, new FilterImpl(requestMethod, context, request)); + return this; + } + + public Express get(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("GET", context, request)); + return this; + } + + public Express post(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("POST", context, request)); + return this; + } + + public Express put(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("PUT", context, request)); + return this; + } + + public Express delete(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("DELETE", context, request)); + return this; + } + + public Express patch(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("PATCH", context, request)); + return this; + } + + /** + * Binds a or multiple objects with request-handler methods on this express instance. With the use + * of the DynExpress Annotation handler can be declared with a annotation on a compatible method + * which has as parameter a Request and Response object. + * + * @param objects Object with proper request handler methods. + * @return This express instance. + */ + public Express bind(Object... objects) { + for (Object o : objects) { + + // Skip null objects + if (o == null) { + continue; + } + + // Find methods wich has the DynExpress annotation + Method[] methods = o.getClass().getDeclaredMethods(); + for (Method method : methods) { + + // Skip if annotation is not present + if (!method.isAnnotationPresent(DynExpress.class)) { + continue; } - } - - /** - * Add an routing object. - * - * @param router The router. - * @return Express this express instance - */ - public Express use(ExpressRouter router) { - this.handler.combine(router.getHandler()); - this.worker.addAll(router.getWorker()); - return this; - } - - /** - * Add an routing object with an specific root root. - * - * @param root The root path for all request to this router. - * @param router The router. - * @return Express this express instance - */ - @SuppressWarnings("unchecked") - public Express use(String root, ExpressRouter router) { - - router.getHandler().forEach(fl -> fl.getFilter().forEach(layer -> { - ((FilterImpl) layer).setRoot(root); - })); - - this.handler.combine(router.getHandler()); - this.worker.addAll(router.getWorker()); - - return this; - } - - public Express use(HttpRequestHandler middleware) { - addMiddleware("*", "*", middleware); - return this; - } - - public Express use(String context, HttpRequestHandler middleware) { - addMiddleware("*", context, middleware); - return this; - } - - public Express use(String context, String requestMethod, HttpRequestHandler middleware) { - addMiddleware(requestMethod.toUpperCase(), context, middleware); - return this; - } - // Internal service to handle middleware - private void addMiddleware(String requestMethod, String context, HttpRequestHandler middleware) { - if (middleware instanceof FilterTask) { - worker.add(new FilterWorker((FilterTask) middleware)); + // Make private method accessible + if (!method.isAccessible()) { + method.setAccessible(true); } - handler.add(0, new FilterImpl(requestMethod, context, middleware)); - } - - public Express all(HttpRequestHandler request) { - handler.add(1, new FilterImpl("*", "*", request)); - return this; - } - - public Express all(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("*", context, request)); - return this; - } - - public Express all(String context, String requestMethod, HttpRequestHandler request) { - handler.add(1, new FilterImpl(requestMethod, context, request)); - return this; - } - - public Express get(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("GET", context, request)); - return this; - } - - public Express post(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("POST", context, request)); - return this; - } - - public Express put(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("PUT", context, request)); - return this; - } - - public Express delete(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("DELETE", context, request)); - return this; - } - - public Express patch(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("PATCH", context, request)); - return this; - } - - /** - * Binds a or multiple objects with request-handler methods on this express instance. - * With the use of the DynExpress Annotation handler can be declared with a annotation - * on a compatible method which has as parameter a Request and Response object. - * - * @param objects Object with proper request handler methods. - * @return This express instance. - */ - public Express bind(Object... objects) { - for (Object o : objects) { - - // Skip null objects - if (o == null) { - continue; - } - - // Find methods wich has the DynExpress annotation - Method[] methods = o.getClass().getDeclaredMethods(); - for (Method method : methods) { - - // Skip if annotation is not present - if (!method.isAnnotationPresent(DynExpress.class)) { - continue; - } - - // Make private method accessible - if (!method.isAccessible()) { - method.setAccessible(true); - } - - // Validate parameter types - Class[] params = method.getParameterTypes(); - if (params.length < 1 || params[0] != Request.class || params[1] != Response.class) { - StringBuilder sb = new StringBuilder(); - for (Class c : params) { - sb.append(c.getSimpleName()); - sb.append(", "); - } + // Validate parameter types + Class[] params = method.getParameterTypes(); + if (params.length < 1 || params[0] != Request.class || params[1] != Response.class) { + StringBuilder sb = new StringBuilder(); + for (Class c : params) { + sb.append(c.getSimpleName()); + sb.append(", "); + } + + String paramString = sb.toString(); + if (paramString.length() > 2) { + paramString = paramString.substring(0, paramString.length() - 2); + } + + System.err.println( + "Skipped method with invalid parameter types found in " + + method.getName() + + "(" + + paramString + + ") in " + + o.getClass().getName() + + ". Expected Request and Response."); + continue; + } - String paramString = sb.toString(); - if (paramString.length() > 2) { - paramString = paramString.substring(0, paramString.length() - 2); + DynExpress[] annotations = method.getAnnotationsByType(DynExpress.class); + for (DynExpress dex : annotations) { + String context = dex.context(); + String requestMethod = dex.method().getMethod(); + + // Bind to instance + handler.add( + 1, + new FilterImpl( + requestMethod, + context, + (req, res) -> { + try { + method.invoke(o, req, res); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); } - - System.err.println("Skipped method with invalid parameter types found in " + - method.getName() + "(" + paramString + ") in " + o.getClass().getName() + - ". Expected Request and Response."); - continue; - } - - DynExpress[] annotations = method.getAnnotationsByType(DynExpress.class); - for (DynExpress dex : annotations) { - String context = dex.context(); - String requestMethod = dex.method().getMethod(); - - // Bind to instance - handler.add(1, new FilterImpl(requestMethod, context, (req, res) -> { - try { - method.invoke(o, req, res); - } catch (IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); - } - })); - } - } + })); } - - return this; + } } - /** - * Start the HTTP-Server on port 80. - * This method is asynchronous so be sure to add an listener or keep it in mind! - */ - public void listen() { - listen(null, 80); - } - - /** - * Start the HTTP-Server on a specific port - * This method is asynchronous so be sure to add an listener or keep it in mind! - * - * @param port The port. - */ - public void listen(int port) { - listen(null, port); - } - - /** - * Start the HTTP-Server on port 80. - * This method is asynchronous so be sure to add an listener or keep it in mind! - * - * @param onStart An listener which will be fired after the server is stardet. - */ - public void listen(ExpressListener onStart) { - listen(onStart, 80); - } - - /** - * Start the HTTP-Server on a specific port. - * This method is asynchronous so be sure to add an listener or keep it in mind. - * - * @param onStart An listener which will be fired after the server is stardet. - * @param port The port. - */ - public void listen(ExpressListener onStart, int port) { - new Thread(() -> { - try { + return this; + } + + /** + * Start the HTTP-Server on port 80. This method is asynchronous so be sure to add an listener or + * keep it in mind! + */ + public void listen() { + listen(null, 80); + } + + /** + * Start the HTTP-Server on a specific port This method is asynchronous so be sure to add an + * listener or keep it in mind! + * + * @param port The port. + */ + public void listen(int port) { + listen(null, port); + } + + /** + * Start the HTTP-Server on port 80. This method is asynchronous so be sure to add an listener or + * keep it in mind! + * + * @param onStart An listener which will be fired after the server is stardet. + */ + public void listen(ExpressListener onStart) { + listen(onStart, 80); + } + + /** + * Start the HTTP-Server on a specific port. This method is asynchronous so be sure to add an + * listener or keep it in mind. + * + * @param onStart An listener which will be fired after the server is stardet. + * @param port The port. + */ + public void listen(ExpressListener onStart, int port) { + new Thread( + () -> { + try { // Fire worker threads worker.forEach(FilterWorker::start); - InetSocketAddress socketAddress = this.hostname == null ? new InetSocketAddress(port) : new InetSocketAddress(this.hostname, port); + InetSocketAddress socketAddress = + this.hostname == null + ? new InetSocketAddress(port) + : new InetSocketAddress(this.hostname, port); if (httpsConfigurator != null) { - // Create https server - httpServer = HttpsServer.create(socketAddress, 0); - ((HttpsServer) httpServer).setHttpsConfigurator(httpsConfigurator); + // Create https server + httpServer = HttpsServer.create(socketAddress, 0); + ((HttpsServer) httpServer).setHttpsConfigurator(httpsConfigurator); } else { - // Create http server - httpServer = HttpServer.create(socketAddress, 0); + // Create http server + httpServer = HttpServer.create(socketAddress, 0); } // Set thread executor @@ -376,26 +389,25 @@ public void listen(ExpressListener onStart, int port) { // Fire listener if (onStart != null) { - onStart.action(); + onStart.action(); } - } catch (IOException e) { + } catch (IOException e) { e.printStackTrace(); - } - }).start(); - } + } + }) + .start(); + } - /** - * Stop express - */ - public void stop() { - if (httpServer != null) { + /** Stop express */ + public void stop() { + if (httpServer != null) { - // Stop http-server - httpServer.stop(0); + // Stop http-server + httpServer.stop(0); - // Stop worker threads - worker.forEach(FilterWorker::stop); - } + // Stop worker threads + worker.forEach(FilterWorker::stop); } + } } diff --git a/src/main/java/express/ExpressException.java b/src/main/java/express/ExpressException.java index 3d6323a..41ccda4 100644 --- a/src/main/java/express/ExpressException.java +++ b/src/main/java/express/ExpressException.java @@ -1,23 +1,22 @@ package express; /** + * Exception for java-express errors. + * * @author Simon Reinisch - * Exception for express own errors. */ public class ExpressException extends RuntimeException { - /** - * Constructs a new exception with an empty detail message. - */ - public ExpressException() {} + /** Constructs a new exception with an empty detail message. */ + public ExpressException() {} - /** - * Constructs a new exception with the specified detail message. - * - * @param message the detail message. The detail message is saved for - * later retrieval by the getMessage() method. - */ - public ExpressException(String message) { - super(message); - } -} \ No newline at end of file + /** + * Constructs a new exception with the specified detail message. + * + * @param message the detail message. The detail message is saved for later retrieval by the + * getMessage() method. + */ + public ExpressException(String message) { + super(message); + } +} diff --git a/src/main/java/express/ExpressListener.java b/src/main/java/express/ExpressListener.java index e08dccc..73b4096 100644 --- a/src/main/java/express/ExpressListener.java +++ b/src/main/java/express/ExpressListener.java @@ -1,9 +1,10 @@ package express; /** + * Listener for express actions. + * * @author Simon Reinisch - * Listener for express actions */ public interface ExpressListener { - void action(); + void action(); } diff --git a/src/main/java/express/ExpressRouter.java b/src/main/java/express/ExpressRouter.java index 0ddbac0..3348ffb 100644 --- a/src/main/java/express/ExpressRouter.java +++ b/src/main/java/express/ExpressRouter.java @@ -5,94 +5,94 @@ import express.filter.FilterTask; import express.filter.FilterWorker; import express.http.HttpRequestHandler; - import java.util.ArrayList; /** + * Basic implementation of a router. + * * @author Simon Reinisch - * Basic implementation of an router */ public class ExpressRouter implements Router { - private final ArrayList workers; - private final FilterLayerHandler handler; + private final ArrayList workers; + private final FilterLayerHandler handler; - { - // Initialize - workers = new ArrayList<>(); - handler = new FilterLayerHandler(2); - } + { + // Initialize + workers = new ArrayList<>(); + handler = new FilterLayerHandler(2); + } - public ExpressRouter use(HttpRequestHandler middleware) { - addMiddleware("*", "*", middleware); - return this; - } + public ExpressRouter use(HttpRequestHandler middleware) { + addMiddleware("*", "*", middleware); + return this; + } - public ExpressRouter use(String context, HttpRequestHandler middleware) { - addMiddleware("*", context, middleware); - return this; - } + public ExpressRouter use(String context, HttpRequestHandler middleware) { + addMiddleware("*", context, middleware); + return this; + } - public ExpressRouter use(String context, String requestMethod, HttpRequestHandler middleware) { - addMiddleware(requestMethod.toUpperCase(), context, middleware); - return this; - } - - private void addMiddleware(String requestMethod, String context, HttpRequestHandler middleware) { + public ExpressRouter use(String context, String requestMethod, HttpRequestHandler middleware) { + addMiddleware(requestMethod.toUpperCase(), context, middleware); + return this; + } - // Validate middleware - if (middleware instanceof FilterTask) { - workers.add(new FilterWorker((FilterTask) middleware)); - } + private void addMiddleware(String requestMethod, String context, HttpRequestHandler middleware) { - handler.add(0, new FilterImpl(requestMethod, context, middleware)); + // Validate middleware + if (middleware instanceof FilterTask) { + workers.add(new FilterWorker((FilterTask) middleware)); } - public ExpressRouter all(HttpRequestHandler request) { - handler.add(1, new FilterImpl("*", "*", request)); - return this; - } - - public ExpressRouter all(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("*", context, request)); - return this; - } - - public ExpressRouter all(String context, String requestMethod, HttpRequestHandler request) { - handler.add(1, new FilterImpl(requestMethod, context, request)); - return this; - } - - public ExpressRouter get(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("GET", context, request)); - return this; - } - - public ExpressRouter post(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("POST", context, request)); - return this; - } - - public ExpressRouter put(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("PUT", context, request)); - return this; - } - - public ExpressRouter delete(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("DELETE", context, request)); - return this; - } - - public ExpressRouter patch(String context, HttpRequestHandler request) { - handler.add(1, new FilterImpl("PATCH", context, request)); - return this; - } - - ArrayList getWorker() { - return workers; - } - - FilterLayerHandler getHandler() { - return handler; - } + handler.add(0, new FilterImpl(requestMethod, context, middleware)); + } + + public ExpressRouter all(HttpRequestHandler request) { + handler.add(1, new FilterImpl("*", "*", request)); + return this; + } + + public ExpressRouter all(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("*", context, request)); + return this; + } + + public ExpressRouter all(String context, String requestMethod, HttpRequestHandler request) { + handler.add(1, new FilterImpl(requestMethod, context, request)); + return this; + } + + public ExpressRouter get(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("GET", context, request)); + return this; + } + + public ExpressRouter post(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("POST", context, request)); + return this; + } + + public ExpressRouter put(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("PUT", context, request)); + return this; + } + + public ExpressRouter delete(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("DELETE", context, request)); + return this; + } + + public ExpressRouter patch(String context, HttpRequestHandler request) { + handler.add(1, new FilterImpl("PATCH", context, request)); + return this; + } + + ArrayList getWorker() { + return workers; + } + + FilterLayerHandler getHandler() { + return handler; + } } diff --git a/src/main/java/express/Router.java b/src/main/java/express/Router.java index 1db63fe..3dbd9ec 100644 --- a/src/main/java/express/Router.java +++ b/src/main/java/express/Router.java @@ -3,109 +3,110 @@ import express.http.HttpRequestHandler; /** + * Router interface for express. + * * @author Simon Reinisch - * Router interface for express */ public interface Router { - /** - * Add an middleware which will be called before each request-type listener will be fired. - * - * @param middleware An middleware which will be fired on every request-method and path. - * @return The router itself to allow method call chaining. - */ - Router use(HttpRequestHandler middleware); + /** + * Add an middleware which will be called before each request-type listener will be fired. + * + * @param middleware An middleware which will be fired on every request-method and path. + * @return The router itself to allow method call chaining. + */ + Router use(HttpRequestHandler middleware); - /** - * Add an middleware which will be called before each request-type listener will be fired. - * - * @param context The context where the middleware should listen. - * @param middleware An middleware which will be fired if the context matches the request-path. - * @return The router itself to allow method call chaining. - */ - Router use(String context, HttpRequestHandler middleware); + /** + * Add an middleware which will be called before each request-type listener will be fired. + * + * @param context The context where the middleware should listen. + * @param middleware An middleware which will be fired if the context matches the request-path. + * @return The router itself to allow method call chaining. + */ + Router use(String context, HttpRequestHandler middleware); - /** - * Add an middleware which will be called before each request-type listener will be fired. - * - * @param context The context where the middleware should listen for the request handler.. - * @param requestMethod And type of request-method eg. GET, POST etc. - * @param middleware An middleware which will be fired if the context matches the request-method- and path. - * @return The router itself to allow method call chaining. - */ - Router use(String context, String requestMethod, HttpRequestHandler middleware); + /** + * Add an middleware which will be called before each request-type listener will be fired. + * + * @param context The context where the middleware should listen for the request handler.. + * @param requestMethod And type of request-method eg. GET, POST etc. + * @param middleware An middleware which will be fired if the context matches the request-method- + * and path. + * @return The router itself to allow method call chaining. + */ + Router use(String context, String requestMethod, HttpRequestHandler middleware); - /** - * Add an listener for all request methods and contexts. - * - * @param request Will be fired on all requests. - * @return The router itself to allow method call chaining. - */ - Router all(HttpRequestHandler request); + /** + * Add an listener for all request methods and contexts. + * + * @param request Will be fired on all requests. + * @return The router itself to allow method call chaining. + */ + Router all(HttpRequestHandler request); - /** - * Adds an handler for a specific context. - * - * @param context The context. - * @param request An listener which will be fired if the context matches the request-path. - * @return The router itself to allow method call chaining. - */ - Router all(String context, HttpRequestHandler request); + /** + * Adds an handler for a specific context. + * + * @param context The context. + * @param request An listener which will be fired if the context matches the request-path. + * @return The router itself to allow method call chaining. + */ + Router all(String context, HttpRequestHandler request); - /** - * Adds an handler for a specific context and method. - * You can use a star '*' to match every context / request-method. - * - * @param context The context. - * @param requestMethod The request method. - * @param request An listener which will be fired if the context matches the request-path. - * @return The router itself to allow method call chaining. - */ - Router all(String context, String requestMethod, HttpRequestHandler request); + /** + * Adds an handler for a specific context and method. You can use a star '*' to match every + * context / request-method. + * + * @param context The context. + * @param requestMethod The request method. + * @param request An listener which will be fired if the context matches the request-path. + * @return The router itself to allow method call chaining. + */ + Router all(String context, String requestMethod, HttpRequestHandler request); - /** - * Add an listener for GET request's. - * - * @param context The context. - * @param request An listener which will be fired if the context matches the request-path. - * @return The router itself to allow method call chaining. - */ - Router get(String context, HttpRequestHandler request); + /** + * Add an listener for GET request's. + * + * @param context The context. + * @param request An listener which will be fired if the context matches the request-path. + * @return The router itself to allow method call chaining. + */ + Router get(String context, HttpRequestHandler request); - /** - * Add an listener for POST request's. - * - * @param context The context. - * @param request An listener which will be fired if the context matches the request-path. - * @return The router itself to allow method call chaining. - */ - Router post(String context, HttpRequestHandler request); + /** + * Add an listener for POST request's. + * + * @param context The context. + * @param request An listener which will be fired if the context matches the request-path. + * @return The router itself to allow method call chaining. + */ + Router post(String context, HttpRequestHandler request); - /** - * Add an listener for PUT request's. - * - * @param context The context for the request handler.. - * @param request An listener which will be fired if the context matches the request-path. - * @return The router itself to allow method call chaining. - */ - Router put(String context, HttpRequestHandler request); + /** + * Add an listener for PUT request's. + * + * @param context The context for the request handler.. + * @param request An listener which will be fired if the context matches the request-path. + * @return The router itself to allow method call chaining. + */ + Router put(String context, HttpRequestHandler request); - /** - * Add an listener for DELETE request's. - * - * @param context The context. - * @param request An listener which will be fired if the context matches the request-path. - * @return The router itself to allow method call chaining. - */ - Router delete(String context, HttpRequestHandler request); - - /** - * Add an listener for PATCH request's. - * - * @param context The context. - * @param request An listener which will be fired if the context matches the request-path. - * @return The router itself to allow method call chaining. - */ - Router patch(String context, HttpRequestHandler request); + /** + * Add an listener for DELETE request's. + * + * @param context The context. + * @param request An listener which will be fired if the context matches the request-path. + * @return The router itself to allow method call chaining. + */ + Router delete(String context, HttpRequestHandler request); + /** + * Add an listener for PATCH request's. + * + * @param context The context. + * @param request An listener which will be fired if the context matches the request-path. + * @return The router itself to allow method call chaining. + */ + Router patch(String context, HttpRequestHandler request); } diff --git a/src/main/java/express/filter/Filter.java b/src/main/java/express/filter/Filter.java index eda1983..640c062 100644 --- a/src/main/java/express/filter/Filter.java +++ b/src/main/java/express/filter/Filter.java @@ -1,16 +1,16 @@ package express.filter; /** + * ExpressFilter to save middleware data, the name is the identifier. + * * @author Simon Reinisch - *

- * ExpressFilter to save middleware data, the name is the indentifier. */ public interface Filter { - /** - * Identifier for the middleware - * - * @return The filter name - */ - String getName(); + /** + * Identifier for the middleware + * + * @return The filter name + */ + String getName(); } diff --git a/src/main/java/express/filter/FilterImpl.java b/src/main/java/express/filter/FilterImpl.java index 75848a6..4f1395f 100644 --- a/src/main/java/express/filter/FilterImpl.java +++ b/src/main/java/express/filter/FilterImpl.java @@ -3,169 +3,163 @@ import express.http.HttpRequestHandler; import express.http.request.Request; import express.http.response.Response; - import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; /** + * An http-filter to extract data and check if an context matches the request. + * * @author Simon Reinisch - *

- * An http-filter to extract data and check if an context matches - * the request. */ public class FilterImpl implements HttpRequestHandler { - private final HttpRequestHandler request; - private final String req; - private final String context; - private final boolean reqAll; - private final boolean contextAll; + private final HttpRequestHandler request; + private final String req; + private final String context; + private final boolean reqAll; + private final boolean contextAll; - private String root; - private String fullContext; + private String root; + private String fullContext; - public FilterImpl(String requestMethod, String context, HttpRequestHandler httpRequest) { - this.req = requestMethod; - this.request = httpRequest; - this.context = normalizePath(context); + public FilterImpl(String requestMethod, String context, HttpRequestHandler httpRequest) { + this.req = requestMethod; + this.request = httpRequest; + this.context = normalizePath(context); - // Save some information's which don't need to be processed again - this.reqAll = requestMethod.equals("*"); - this.contextAll = context.equals("*"); + // Save some information's which don't need to be processed again + this.reqAll = requestMethod.equals("*"); + this.contextAll = context.equals("*"); - this.root = "/"; - this.fullContext = this.context; - } + this.root = "/"; + this.fullContext = this.context; + } - public void setRoot(String root) { + public void setRoot(String root) { - // Ignore empty root - if (root == null || root.isEmpty()) { - return; - } + // Ignore empty root + if (root == null || root.isEmpty()) { + return; + } - if (root.charAt(0) != '/') { - root = '/' + root; - } + if (root.charAt(0) != '/') { + root = '/' + root; + } - if (root.charAt(root.length() - 1) != '/') { - root += '/'; - } + if (root.charAt(root.length() - 1) != '/') { + root += '/'; + } + + this.root = normalizePath(root); + this.fullContext = normalizePath(this.root + context); + } + + @Override + public void handle(Request req, Response res) { + String requestMethod = req.getMethod(); + String requestPath = req.getURI().getRawPath(); + ConcurrentHashMap parameterListener = + req.getApp().getParameterListener(); + + if (!(reqAll || this.req.equals(requestMethod))) { + return; + } else if (contextAll) { + req.setContext(context); + request.handle(req, res); + return; + } - this.root = normalizePath(root); - this.fullContext = normalizePath(this.root + context); + // Parse params + HashMap params = matchURL(fullContext, requestPath); + if (params == null) { + return; } - @Override - public void handle(Request req, Response res) { - String requestMethod = req.getMethod(); - String requestPath = req.getURI().getRawPath(); - ConcurrentHashMap parameterListener = req.getApp().getParameterListener(); + // Save parameter to request object + req.setParams(params); - if (!(reqAll || this.req.equals(requestMethod))) { - return; - } else if (contextAll) { - req.setContext(context); + // Check parameter listener + params.forEach( + (s, s2) -> { + HttpRequestHandler request = parameterListener.get(s); + + if (request != null) { request.handle(req, res); - return; - } + } + }); - // Parse params - HashMap params = matchURL(fullContext, requestPath); - if (params == null) { - return; - } + // Check if the response is closed + if (res.isClosed()) { + return; + } - // Save parameter to request object - req.setParams(params); + // Handle request + req.setContext(context); + request.handle(req, res); + } - // Check parameter listener - params.forEach((s, s2) -> { - HttpRequestHandler request = parameterListener.get(s); + /** Extract and match the parameter from the url with an filter. */ + private HashMap matchURL(String filter, String url) { + HashMap params = new HashMap<>(); + StringBuilder key = new StringBuilder(); + StringBuilder val = new StringBuilder(); + char[] uc = url.toCharArray(); + char[] fc = filter.toCharArray(); + int ui = 0, fi = 0; - if (request != null) { - request.handle(req, res); - } - }); + for (; fi < fc.length && ui < uc.length; fi++, ui++) { - // Check if the response is closed - if (res.isClosed()) { - return; - } + if (fc[fi] == ':') { + key.setLength(0); + val.setLength(0); - // Handle request - req.setContext(context); - request.handle(req, res); - } + fi++; + + while (fi < fc.length && fc[fi] != '/') { + key.append(fc[fi++]); + } - /** - * Extract and match the parameter from the url with an filter. - */ - private HashMap matchURL(String filter, String url) { - HashMap params = new HashMap<>(); - StringBuilder key = new StringBuilder(); - StringBuilder val = new StringBuilder(); - char[] uc = url.toCharArray(); - char[] fc = filter.toCharArray(); - int ui = 0, fi = 0; - - for (; fi < fc.length && ui < uc.length; fi++, ui++) { - - if (fc[fi] == ':') { - key.setLength(0); - val.setLength(0); - - fi++; - - while (fi < fc.length && fc[fi] != '/') { - key.append(fc[fi++]); - } - - while (ui < uc.length && uc[ui] != '/') { - val.append(uc[ui++]); - } - - try { - String decVal = URLDecoder.decode(val.toString(), "UTF8"); - params.put(key.toString(), decVal); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - - } else if (fc[fi] != uc[ui]) { - - // Failed - return null; - } + while (ui < uc.length && uc[ui] != '/') { + val.append(uc[ui++]); } - if (ui < url.length() || fi < filter.length()) { - return null; + try { + String decVal = URLDecoder.decode(val.toString(), "UTF8"); + params.put(key.toString(), decVal); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); } - return params; + } else if (fc[fi] != uc[ui]) { + + // Failed + return null; + } } - /** - * Replace all double slashes from an string with an single slash - */ - private String normalizePath(String context) { - if (context == null || context.length() == 1) - return context; - - StringBuilder sb = new StringBuilder(); - char[] chars = context.toCharArray(); - - sb.append(chars[0]); - for (int i = 1; i < chars.length; i++) { - if ((chars[i] == '/' && chars[i - 1] != '/') || chars[i] != '/') { - sb.append(chars[i]); - } - } + if (ui < url.length() || fi < filter.length()) { + return null; + } + + return params; + } + + /** Replace all double slashes from an string with an single slash */ + private String normalizePath(String context) { + if (context == null || context.length() == 1) return context; + + StringBuilder sb = new StringBuilder(); + char[] chars = context.toCharArray(); - return sb.toString(); + sb.append(chars[0]); + for (int i = 1; i < chars.length; i++) { + if ((chars[i] == '/' && chars[i - 1] != '/') || chars[i] != '/') { + sb.append(chars[i]); + } } + return sb.toString(); + } } diff --git a/src/main/java/express/filter/FilterLayer.java b/src/main/java/express/filter/FilterLayer.java index 40c524b..4967e0a 100644 --- a/src/main/java/express/filter/FilterLayer.java +++ b/src/main/java/express/filter/FilterLayer.java @@ -3,42 +3,41 @@ import express.http.HttpRequestHandler; import express.http.request.Request; import express.http.response.Response; - import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; /** - * @author Simon Reinisch - *

* Controller class for FilterLayer. + * + * @author Simon Reinisch */ public class FilterLayer { - private final List filter = Collections.synchronizedList(new ArrayList<>()); + private final List filter = Collections.synchronizedList(new ArrayList<>()); - public void add(T expressFilter) { - this.filter.add(expressFilter); - } + public void add(T expressFilter) { + this.filter.add(expressFilter); + } - public void add(int index, T expressFilter) { - this.filter.add(index, expressFilter); - } + public void add(int index, T expressFilter) { + this.filter.add(index, expressFilter); + } - public void addAll(List expressFilters) { - this.filter.addAll(expressFilters); - } + public void addAll(List expressFilters) { + this.filter.addAll(expressFilters); + } - public List getFilter() { - return filter; - } + public List getFilter() { + return filter; + } - void filter(Request req, Response res) { - ListIterator iter = this.filter.listIterator(); + void filter(Request req, Response res) { + ListIterator iter = this.filter.listIterator(); - while (!res.isClosed() && iter.hasNext()) { - iter.next().handle(req, res); - } + while (!res.isClosed() && iter.hasNext()) { + iter.next().handle(req, res); } + } } diff --git a/src/main/java/express/filter/FilterLayerHandler.java b/src/main/java/express/filter/FilterLayerHandler.java index 7db0e0f..1efb8d6 100644 --- a/src/main/java/express/filter/FilterLayerHandler.java +++ b/src/main/java/express/filter/FilterLayerHandler.java @@ -6,98 +6,100 @@ import express.http.HttpRequestHandler; import express.http.request.Request; import express.http.response.Response; - import java.util.function.Consumer; /** - * @author Simon Reinisch - *

* Handler for multiple FilterLayer. + * + * @author Simon Reinisch */ public class FilterLayerHandler { - private final FilterLayer[] layers; + private final FilterLayer[] layers; - public FilterLayerHandler(int layers) { + public FilterLayerHandler(int layers) { - // Create & initialize layers - this.layers = new FilterLayer[layers]; - for (int i = 0; i < this.layers.length; i++) { - this.layers[i] = new FilterLayer<>(); - } + // Create & initialize layers + this.layers = new FilterLayer[layers]; + for (int i = 0; i < this.layers.length; i++) { + this.layers[i] = new FilterLayer<>(); } + } - public void handle(HttpExchange httpExchange, Express express) { - Request request = new Request(httpExchange, express); - Response response = new Response(httpExchange); + public void handle(HttpExchange httpExchange, Express express) { + Request request = new Request(httpExchange, express); + Response response = new Response(httpExchange); - // First fire all middleware's, then the normal request filter - for (FilterLayer chain : layers) { - chain.filter(request, response); + // First fire all middleware's, then the normal request filter + for (FilterLayer chain : layers) { + chain.filter(request, response); - if (response.isClosed()) { - return; - } - } + if (response.isClosed()) { + return; + } } - - /** - * Add an new handler for an specific handler layers. - * - * @param level The layers. - * @param handler The handler, will be append to the top of the layers. - */ - @SuppressWarnings("unchecked") - public void add(int level, HttpRequestHandler handler) { - - if (level >= layers.length) { - throw new IndexOutOfBoundsException("Out of bounds: " + level + " > " + layers.length); - } - - if (level < 0) { - throw new IndexOutOfBoundsException("Cannot be under zero: " + level + " < 0"); - } - - layers[level].add(handler); + } + + /** + * Add an new handler for an specific handler layers. + * + * @param level The layers. + * @param handler The handler, will be append to the top of the layers. + */ + @SuppressWarnings("unchecked") + public void add(int level, HttpRequestHandler handler) { + + if (level >= layers.length) { + throw new IndexOutOfBoundsException("Out of bounds: " + level + " > " + layers.length); } - /** - * Merge two FilterLayerHandler - * - * @param filterLayerHandler The FilterLayerHandler which you want to merge with this - */ - @SuppressWarnings("unchecked") - public void combine(FilterLayerHandler filterLayerHandler) { - if (filterLayerHandler != null) { - FilterLayer[] chains = filterLayerHandler.getLayers(); - - if (chains.length != layers.length) { - throw new ExpressException("Cannot add an filterLayerHandler with different layers sizes: " + chains.length + " != " + layers.length); - } - - for (int i = 0; i < chains.length; i++) { - layers[i].addAll(chains[i].getFilter()); - } - } + if (level < 0) { + throw new IndexOutOfBoundsException("Cannot be under zero: " + level + " < 0"); } - /** - * Iterate over the different FilterLayer - * - * @param layerConsumer An consumer for the layers - */ - public void forEach(Consumer layerConsumer) { - if (layerConsumer == null) { - return; - } - - for (FilterLayer layer : layers) { - layerConsumer.accept(layer); - } + layers[level].add(handler); + } + + /** + * Merge two FilterLayerHandler + * + * @param filterLayerHandler The FilterLayerHandler which you want to merge with this + */ + @SuppressWarnings("unchecked") + public void combine(FilterLayerHandler filterLayerHandler) { + if (filterLayerHandler != null) { + FilterLayer[] chains = filterLayerHandler.getLayers(); + + if (chains.length != layers.length) { + throw new ExpressException( + "Cannot add an filterLayerHandler with different layers sizes: " + + chains.length + + " != " + + layers.length); + } + + for (int i = 0; i < chains.length; i++) { + layers[i].addAll(chains[i].getFilter()); + } + } + } + + /** + * Iterate over the different FilterLayer + * + * @param layerConsumer An consumer for the layers + */ + public void forEach(Consumer layerConsumer) { + if (layerConsumer == null) { + return; } - private FilterLayer[] getLayers() { - return layers; + for (FilterLayer layer : layers) { + layerConsumer.accept(layer); } + } + private FilterLayer[] getLayers() { + return layers; + } } diff --git a/src/main/java/express/filter/FilterTask.java b/src/main/java/express/filter/FilterTask.java index a3dd136..56387be 100644 --- a/src/main/java/express/filter/FilterTask.java +++ b/src/main/java/express/filter/FilterTask.java @@ -1,31 +1,25 @@ package express.filter; /** - * @author Simon Reinisch - *

* Interface for filter tasks. + * + * @author Simon Reinisch */ public interface FilterTask { - /** - * Returns the delay between the updates - * - * @return Update delay in milliseconds - */ - long getDelay(); + /** + * Returns the delay between the updates + * + * @return Update delay in milliseconds + */ + long getDelay(); - /** - * Will be fired between the delays - */ - void onUpdate(); + /** Will be fired between the delays */ + void onUpdate(); - /** - * Will be fired on express-start - */ - void onStart(); + /** Will be fired on express-start */ + void onStart(); - /** - * Will be fired on express-stop - */ - void onStop(); + /** Will be fired on express-stop */ + void onStop(); } diff --git a/src/main/java/express/filter/FilterWorker.java b/src/main/java/express/filter/FilterWorker.java index c157fe7..4f76c30 100644 --- a/src/main/java/express/filter/FilterWorker.java +++ b/src/main/java/express/filter/FilterWorker.java @@ -4,41 +4,41 @@ import java.util.TimerTask; /** + * Worker module for FilterTasks. + * * @author Simon Reinisch - *

- * Worker modul for FilterTasks. */ public class FilterWorker extends TimerTask { - private final FilterTask middlewareWorker; - private Timer timer; + private final FilterTask middlewareWorker; + private Timer timer; - public FilterWorker(FilterTask middlewareWorker) { - this.middlewareWorker = middlewareWorker; - } + public FilterWorker(FilterTask middlewareWorker) { + this.middlewareWorker = middlewareWorker; + } - public void start() { - if (this.timer == null) { - middlewareWorker.onStart(); - this.timer = new Timer(); - this.timer.scheduleAtFixedRate(this, 0, middlewareWorker.getDelay()); - } + public void start() { + if (this.timer == null) { + middlewareWorker.onStart(); + this.timer = new Timer(); + this.timer.scheduleAtFixedRate(this, 0, middlewareWorker.getDelay()); } + } - public void stop() { - if (timer != null) { - middlewareWorker.onStop(); - this.timer.cancel(); - this.timer = null; - } + public void stop() { + if (timer != null) { + middlewareWorker.onStop(); + this.timer.cancel(); + this.timer = null; } + } - public boolean isActive() { - return timer != null; - } + public boolean isActive() { + return timer != null; + } - @Override - public void run() { - middlewareWorker.onUpdate(); - } + @Override + public void run() { + middlewareWorker.onUpdate(); + } } diff --git a/src/main/java/express/http/Cookie.java b/src/main/java/express/http/Cookie.java index 3d17ace..580fc80 100644 --- a/src/main/java/express/http/Cookie.java +++ b/src/main/java/express/http/Cookie.java @@ -1,238 +1,217 @@ package express.http; - import java.time.Instant; /** + * Simple class which represents an HTTPCookie. + * * @author Simon Reinisch - * Simple class which represents an HTTPCookie */ public class Cookie { - private String name, value; - private String expire; - private String path = "/"; - private String domain; - private String sameSite; - private boolean secure = false; - private boolean httpOnly = false; - private long maxAge = -1; - - /** - * Creates an new http-cookie - * - * @param name Cookie name - * @param value Cookie value - */ - public Cookie(String name, String value) { - name = name.trim(); - - if (name.isEmpty() || name.charAt(0) == '$') { - throw new IllegalArgumentException("Illegal cookie name"); - } - - this.name = name; - this.value = value; - } - - public Cookie() { - } - - /** - * @return The name of the Cookie - */ - public String getName() { - return name; - } - - /** - * Set the cookie name - * - * @param name The name - * @return this cookie instance - */ - public Cookie setName(String name) { - this.name = name; - return this; - } - - /** - * @return The cookie value - */ - public String getValue() { - return value; - } - - /** - * Set the cookie value - * - * @param value The value - * @return this cookie instance - */ - public Cookie setValue(String value) { - this.value = value; - return this; - } - - /** - * @return The expire time in GTM format - */ - public String getExpire() { - return expire; - } - - /** - * Set the cookie expire (GMT wherefore Instant) - * Default is infinite - * - * @param instant The instant - * @return this cookie instance - */ - public Cookie setExpire(Instant instant) { - this.expire = instant.toString(); - return this; - } - - public String getPath() { - return path; - } - - /** - * Set the cookie path, also where the cookie should be send to the server - * Default is root (/) - * - * @param path The path - * @return this cookie instance - */ - public Cookie setPath(String path) { - this.path = path; - return this; - } - - /** - * @return If the cookie is secure (SSL Only) - */ - public boolean isSecure() { - return secure; - } - - /** - * Set the if the cookie should be only send via SSL - * Default is false - * - * @param secure if you want the cookie to be secure - * @return this cookie instance - */ - public Cookie setSecure(boolean secure) { - this.secure = secure; - return this; - } - - /** - * @return If the cookie is HttpOnly - */ - public boolean isHttpOnly() { - return httpOnly; - } - - /** - * Set the if the cookie shouldn't accessible via JavaScript through the Document.cookie property - * Default is false - * - * @param httpOnly if you want the cookie to be httpOnly - * @return this cookie instance - */ - public Cookie setHttpOnly(boolean httpOnly) { - this.httpOnly = httpOnly; - return this; - } - - public String getSameSite() { - return sameSite; - } - - public Cookie setSameSite(SameSite sameSite) { - if (sameSite == null) return this; - this.sameSite = sameSite.name(); - return this; - } - - /** - * @return the maximum age of this cookie (in milliseconds) - */ - public long getMaxAge() { - return maxAge; - } - - /** - * Set the maximum age for this cookie in ms - * Default is infinite - * - * @param maxAge the maxage in milliseconds - * @return this cookie instance - */ - public Cookie setMaxAge(long maxAge) { - this.maxAge = maxAge; - return this; - } - - /** - * @return current cookie domain - */ - public String getDomain() { - return domain; - } - - /** - * Set the cookie domauin - * Default is not defined - * - * @param domain The domain - */ - public void setDomain(String domain) { - this.domain = domain; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof Cookie) { - Cookie other = (Cookie) obj; - - if (!other.getValue().equals(this.getValue())) return false; - if (!other.getName().equals(this.getName())) return false; - if (!other.getDomain().equals(this.getDomain())) return false; - if (!other.getExpire().equals(this.getExpire())) return false; - if (other.getMaxAge() != this.getMaxAge()) return false; - if (!other.getSameSite().equals(this.getSameSite())) return false; - - return other.getPath().equals(this.getPath()); - } - return super.equals(obj); - } - - /** - * Build the string to an cookie-string. - * - * @return The cookie as string, null if the name / value is null. - */ - @Override - public String toString() { - if (name == null || value == null) - return null; - - StringBuilder b = new StringBuilder(); - b.append(name).append("=").append(value); - - if (path != null) b.append("; Path=").append(path); - if (expire != null) b.append("; Expire=").append(expire); - if (maxAge != -1) b.append("; Max-Age=").append(maxAge); - - if (domain != null) b.append("; Domain=").append(domain); - if (sameSite != null) b.append("; SameSite=").append(sameSite); - - if (secure) b.append("; Secure"); - if (httpOnly) b.append("; HttpOnly"); - - return b.toString(); - } + private String name, value; + private String expire; + private String path = "/"; + private String domain; + private String sameSite; + private boolean secure = false; + private boolean httpOnly = false; + private long maxAge = -1; + + /** + * Creates an new http-cookie + * + * @param name Cookie name + * @param value Cookie value + */ + public Cookie(String name, String value) { + name = name.trim(); + + if (name.isEmpty() || name.charAt(0) == '$') { + throw new IllegalArgumentException("Illegal cookie name"); + } + + this.name = name; + this.value = value; + } + + public Cookie() {} + + /** @return The name of the Cookie */ + public String getName() { + return name; + } + + /** + * Set the cookie name + * + * @param name The name + * @return this cookie instance + */ + public Cookie setName(String name) { + this.name = name; + return this; + } + + /** @return The cookie value */ + public String getValue() { + return value; + } + + /** + * Set the cookie value + * + * @param value The value + * @return this cookie instance + */ + public Cookie setValue(String value) { + this.value = value; + return this; + } + + /** @return The expire time in GTM format */ + public String getExpire() { + return expire; + } + + /** + * Set the cookie expire (GMT wherefore Instant) Default is infinite + * + * @param instant The instant + * @return this cookie instance + */ + public Cookie setExpire(Instant instant) { + this.expire = instant.toString(); + return this; + } + + public String getPath() { + return path; + } + + /** + * Set the cookie path, also where the cookie should be send to the server Default is root (/) + * + * @param path The path + * @return this cookie instance + */ + public Cookie setPath(String path) { + this.path = path; + return this; + } + + /** @return If the cookie is secure (SSL Only) */ + public boolean isSecure() { + return secure; + } + + /** + * Set the if the cookie should be only send via SSL Default is false + * + * @param secure if you want the cookie to be secure + * @return this cookie instance + */ + public Cookie setSecure(boolean secure) { + this.secure = secure; + return this; + } + + /** @return If the cookie is HttpOnly */ + public boolean isHttpOnly() { + return httpOnly; + } + + /** + * Set the if the cookie shouldn't accessible via JavaScript through the Document.cookie property + * Default is false + * + * @param httpOnly if you want the cookie to be httpOnly + * @return this cookie instance + */ + public Cookie setHttpOnly(boolean httpOnly) { + this.httpOnly = httpOnly; + return this; + } + + public String getSameSite() { + return sameSite; + } + + public Cookie setSameSite(SameSite sameSite) { + if (sameSite == null) return this; + this.sameSite = sameSite.name(); + return this; + } + + /** @return the maximum age of this cookie (in milliseconds) */ + public long getMaxAge() { + return maxAge; + } + + /** + * Set the maximum age for this cookie in ms Default is infinite + * + * @param maxAge the maxage in milliseconds + * @return this cookie instance + */ + public Cookie setMaxAge(long maxAge) { + this.maxAge = maxAge; + return this; + } + + /** @return current cookie domain */ + public String getDomain() { + return domain; + } + + /** + * Set the cookie domauin Default is not defined + * + * @param domain The domain + */ + public void setDomain(String domain) { + this.domain = domain; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Cookie) { + Cookie other = (Cookie) obj; + + if (!other.getValue().equals(this.getValue())) return false; + if (!other.getName().equals(this.getName())) return false; + if (!other.getDomain().equals(this.getDomain())) return false; + if (!other.getExpire().equals(this.getExpire())) return false; + if (other.getMaxAge() != this.getMaxAge()) return false; + if (!other.getSameSite().equals(this.getSameSite())) return false; + + return other.getPath().equals(this.getPath()); + } + return super.equals(obj); + } + + /** + * Build the string to an cookie-string. + * + * @return The cookie as string, null if the name / value is null. + */ + @Override + public String toString() { + if (name == null || value == null) return null; + + StringBuilder b = new StringBuilder(); + b.append(name).append("=").append(value); + + if (path != null) b.append("; Path=").append(path); + if (expire != null) b.append("; Expire=").append(expire); + if (maxAge != -1) b.append("; Max-Age=").append(maxAge); + + if (domain != null) b.append("; Domain=").append(domain); + if (sameSite != null) b.append("; SameSite=").append(sameSite); + + if (secure) b.append("; Secure"); + if (httpOnly) b.append("; HttpOnly"); + + return b.toString(); + } } diff --git a/src/main/java/express/http/CookieFactory.java b/src/main/java/express/http/CookieFactory.java index 4e5d557..6aabb68 100644 --- a/src/main/java/express/http/CookieFactory.java +++ b/src/main/java/express/http/CookieFactory.java @@ -4,112 +4,112 @@ import java.util.HashMap; /** + * A CookieFactory to parse an string which represents an cookie. + * * @author Simon Reinisch - * An CookieFactory to parse an string which represents an cookie. */ public final class CookieFactory { - private CookieFactory() {} + private CookieFactory() {} - /** - * Parse an cookie string. - * - * @param cookieString The cookie as string. - * @return The parsed cookie as object. - */ - public static Cookie fromString(String cookieString) { - Cookie cookie = null; - char[] chars = cookieString.toCharArray(); + /** + * Parse an cookie string. + * + * @param cookieString The cookie as string. + * @return The parsed cookie as object. + */ + public static Cookie fromString(String cookieString) { + Cookie cookie = null; + char[] chars = cookieString.toCharArray(); - StringBuilder key = new StringBuilder(); - StringBuilder val = new StringBuilder(); - boolean swap = false; + StringBuilder key = new StringBuilder(); + StringBuilder val = new StringBuilder(); + boolean swap = false; - for (char c : chars) { + for (char c : chars) { - if (c == '=') { - swap = true; - } else if (c == ';') { - - cookie = addField(cookie, key.toString(), val.toString()); - - key.setLength(0); - val.setLength(0); - swap = false; - } else if (swap) { - val.append(c); - } else { - key.append(c); - } - } + if (c == '=') { + swap = true; + } else if (c == ';') { cookie = addField(cookie, key.toString(), val.toString()); - return cookie; - } - - /** - * Parse an list of strings which represents an cookie. - * - * @param stringCookies The list with string cookies. - * @return A list with the parsed cookies. - */ - public static HashMap fromStrings(String[] stringCookies) { - HashMap cookies = new HashMap<>(); - if (stringCookies == null || stringCookies.length == 0) { - return cookies; - } + key.setLength(0); + val.setLength(0); + swap = false; + } else if (swap) { + val.append(c); + } else { + key.append(c); + } + } - for (String s : stringCookies) { - Cookie cookie = fromString(s); + cookie = addField(cookie, key.toString(), val.toString()); + return cookie; + } + + /** + * Parse an list of strings which represents an cookie. + * + * @param stringCookies The list with string cookies. + * @return A list with the parsed cookies. + */ + public static HashMap fromStrings(String[] stringCookies) { + HashMap cookies = new HashMap<>(); + + if (stringCookies == null || stringCookies.length == 0) { + return cookies; + } - if (cookie != null) { - cookies.put(cookie.getName(), cookie); - } - } + for (String s : stringCookies) { + Cookie cookie = fromString(s); - return cookies; + if (cookie != null) { + cookies.put(cookie.getName(), cookie); + } } - private static Cookie addField(Cookie cookie, String key, String value) { - key = key.trim(); + return cookies; + } + private static Cookie addField(Cookie cookie, String key, String value) { + key = key.trim(); - if (cookie == null) { - cookie = new Cookie(key, value); - } else if (key.equalsIgnoreCase("Path")) { - cookie.setPath(value); - } else if (key.equalsIgnoreCase("Domain")) { - cookie.setDomain(value); - } else if (key.equalsIgnoreCase("Expire")) { - cookie.setExpire(Instant.parse(value)); - } else if (key.equalsIgnoreCase("Max-Age")) { + if (cookie == null) { + cookie = new Cookie(key, value); + } else if (key.equalsIgnoreCase("Path")) { + cookie.setPath(value); + } else if (key.equalsIgnoreCase("Domain")) { + cookie.setDomain(value); + } else if (key.equalsIgnoreCase("Expire")) { + cookie.setExpire(Instant.parse(value)); + } else if (key.equalsIgnoreCase("Max-Age")) { - if (isInteger(value)) { - cookie.setMaxAge(Long.parseLong(value)); - } + if (isInteger(value)) { + cookie.setMaxAge(Long.parseLong(value)); + } - } else if (key.equalsIgnoreCase("SameSite")) { + } else if (key.equalsIgnoreCase("SameSite")) { - SameSite sameSite = value.equalsIgnoreCase("LAX") ? SameSite.LAX : SameSite.STRICT; - cookie.setSameSite(sameSite); + SameSite sameSite = value.equalsIgnoreCase("LAX") ? SameSite.LAX : SameSite.STRICT; + cookie.setSameSite(sameSite); - } else if (key.equalsIgnoreCase("Secure")) { - cookie.setSecure(true); - } else if (key.equalsIgnoreCase("HttpOnly")) { - cookie.setHttpOnly(true); - } - - return cookie; + } else if (key.equalsIgnoreCase("Secure")) { + cookie.setSecure(true); + } else if (key.equalsIgnoreCase("HttpOnly")) { + cookie.setHttpOnly(true); } - private static boolean isInteger(String str) { - char[] chars = str.toCharArray(); + return cookie; + } - for (char c : chars) { - if (c < 48 || c > 57) return false; - } + private static boolean isInteger(String str) { + char[] chars = str.toCharArray(); - return true; + for (char c : chars) { + if (c < 48 || c > 57) return false; } + + return true; + } } diff --git a/src/main/java/express/http/HttpRequestHandler.java b/src/main/java/express/http/HttpRequestHandler.java index cffc807..2034354 100644 --- a/src/main/java/express/http/HttpRequestHandler.java +++ b/src/main/java/express/http/HttpRequestHandler.java @@ -4,17 +4,18 @@ import express.http.response.Response; /** + * Interface to handle an http-request. + * * @author Simon Reinisch - * Interface to handle an http-request */ @FunctionalInterface public interface HttpRequestHandler { - /** - * Handle an http-request - * - * @param req - The request object - * @param res - The response object - */ - void handle(Request req, Response res); + /** + * Handle an http-request + * + * @param req - The request object + * @param res - The response object + */ + void handle(Request req, Response res); } diff --git a/src/main/java/express/http/RequestMethod.java b/src/main/java/express/http/RequestMethod.java index 6885c87..92cf3cf 100644 --- a/src/main/java/express/http/RequestMethod.java +++ b/src/main/java/express/http/RequestMethod.java @@ -1,32 +1,33 @@ package express.http; /** + * Enum with basic request methods. + * * @author Simon Reinisch - * Enum with basic requestmethods. */ public enum RequestMethod { - // Real request methods - GET("GET"), - POST("POST"), - PUT("PUT"), - PATCH("PATCH"), - DELETE("DELETE"), - CONNECT("CONNECT"), - OPTIONS("OPTIONS"), - TRACE("TRACE"), - HEAD("HEAD"), + // Real request methods + GET("GET"), + POST("POST"), + PUT("PUT"), + PATCH("PATCH"), + DELETE("DELETE"), + CONNECT("CONNECT"), + OPTIONS("OPTIONS"), + TRACE("TRACE"), + HEAD("HEAD"), - // Express specific method which will catch every method - ALL("*"); + // Express specific method which will catch every method + ALL("*"); - private String method; + private String method; - RequestMethod(String method) { - this.method = method; - } + RequestMethod(String method) { + this.method = method; + } - public String getMethod() { - return method; - } + public String getMethod() { + return method; + } } diff --git a/src/main/java/express/http/SameSite.java b/src/main/java/express/http/SameSite.java index ca2cc0f..216c5ad 100644 --- a/src/main/java/express/http/SameSite.java +++ b/src/main/java/express/http/SameSite.java @@ -1,9 +1,7 @@ package express.http; -/** - * @author Simon Reinisch - */ +/** @author Simon Reinisch */ public enum SameSite { - STRICT, - LAX + STRICT, + LAX } diff --git a/src/main/java/express/http/SessionCookie.java b/src/main/java/express/http/SessionCookie.java index 6926f2b..a73e09b 100644 --- a/src/main/java/express/http/SessionCookie.java +++ b/src/main/java/express/http/SessionCookie.java @@ -1,50 +1,44 @@ package express.http; /** + * A simple SessionCookie implementation. + * * @author Simon Reinisch - * An simple SessionCookie implemetation. */ public class SessionCookie { - private final long maxAge; - private final long created; - - private Object data; - - public SessionCookie(long maxAge) { - this.maxAge = maxAge; - this.created = System.currentTimeMillis(); - } - - /** - * @return Max age from this cookie - */ - public long getMaxAge() { - return maxAge; - } - - /** - * @return Create date from the cookie - */ - public long getCreated() { - return created; - } - - /** - * @return Session data - */ - public Object getData() { - return data; - } - - /** - * Set the session data - * - * @param data The data object - * @return The object itself - */ - public Object setData(Object data) { - return this.data = data; - } - + private final long maxAge; + private final long created; + + private Object data; + + public SessionCookie(long maxAge) { + this.maxAge = maxAge; + this.created = System.currentTimeMillis(); + } + + /** @return Max age from this cookie */ + public long getMaxAge() { + return maxAge; + } + + /** @return Create date from the cookie */ + public long getCreated() { + return created; + } + + /** @return Session data */ + public Object getData() { + return data; + } + + /** + * Set the session data + * + * @param data The data object + * @return The object itself + */ + public Object setData(Object data) { + return this.data = data; + } } diff --git a/src/main/java/express/http/request/Authorization.java b/src/main/java/express/http/request/Authorization.java index cf69173..2e6796c 100644 --- a/src/main/java/express/http/request/Authorization.java +++ b/src/main/java/express/http/request/Authorization.java @@ -8,93 +8,85 @@ import java.util.stream.Stream; /** + * Represents an HTTP Authorization header value and encapsulates authorization data. + * * @author Simon Reinisch - *

- * Represents an HTTP Authorization header value - * and encapsulates authorization data */ public class Authorization { - public static final String HEADER_NAME = "Authorization"; + public static final String HEADER_NAME = "Authorization"; - private final String type; - private final String data; + private final String type; + private final String data; - public Authorization(String authHeader) { - String[] parts = Stream.of(authHeader.split(" ")) - .filter(s -> !s.isEmpty()) - .toArray(String[]::new); + public Authorization(String authHeader) { + String[] parts = + Stream.of(authHeader.split(" ")).filter(s -> !s.isEmpty()).toArray(String[]::new); - this.type = parts[0]; - this.data = parts[1]; - } - - /** - * @param req Request instance - * @return A list of authorization options that are contained in the given request. - * Authorization options can be separated by a comma in the Authorization header. - */ - public static List get(Request req) { - List headerVals = req.getHeader(HEADER_NAME); + this.type = parts[0]; + this.data = parts[1]; + } - if (!headerVals.isEmpty()) { - String authHeader = headerVals.get(0); - return Collections.unmodifiableList(Stream.of(authHeader.split(",")) - .map(Authorization::new).collect(Collectors.toList())); - } + /** + * @param req Request instance + * @return A list of authorization options that are contained in the given request. Authorization + * options can be separated by a comma in the Authorization header. + */ + public static List get(Request req) { + List headerVals = req.getHeader(HEADER_NAME); - return Collections.emptyList(); + if (!headerVals.isEmpty()) { + String authHeader = headerVals.get(0); + return Collections.unmodifiableList( + Stream.of(authHeader.split(",")).map(Authorization::new).collect(Collectors.toList())); } - /** - * Validates the given request authentication using each of the given predicates. - * If any of the predicates returns true, the request is counted as - * validly authorized and the method returns true. - * - * @param req Request instance - * @param validators Validators - * @return If authorization was successful - */ - @SafeVarargs - public static boolean validate(Request req, Predicate... validators) { - for (Authorization auth : get(req)) { - for (Predicate validator : validators) { - if (validator.test(auth)) { - return true; - } - } + return Collections.emptyList(); + } + + /** + * Validates the given request authentication using each of the given predicates. If any of the + * predicates returns true, the request is counted as validly authorized and the + * method returns true. + * + * @param req Request instance + * @param validators Validators + * @return If authorization was successful + */ + @SafeVarargs + public static boolean validate(Request req, Predicate... validators) { + for (Authorization auth : get(req)) { + for (Predicate validator : validators) { + if (validator.test(auth)) { + return true; } - return false; + } } + return false; + } - /** - * @param type The expected type of the authorization - * @param data The expected data of the authorization - * @return A predicate that can be used with {@link Authorization#validate(Request, Predicate...)} - * to test for a single type of authorization - */ - public static Predicate validator(String type, String data) { - return (auth -> auth.getType().equals(type) && auth.getData().equals(data)); - } + /** + * @param type The expected type of the authorization + * @param data The expected data of the authorization + * @return A predicate that can be used with {@link Authorization#validate(Request, Predicate...)} + * to test for a single type of authorization + */ + public static Predicate validator(String type, String data) { + return (auth -> auth.getType().equals(type) && auth.getData().equals(data)); + } - /** - * @return The Authorization type - */ - public String getType() { - return type; - } + /** @return The Authorization type */ + public String getType() { + return type; + } - /** - * @return The Authorization data - */ - public String getData() { - return data; - } + /** @return The Authorization data */ + public String getData() { + return data; + } - /** - * @return The Authorization data base64 decoded - */ - public String getDataBase64Decoded() { - return new String(Base64.getDecoder().decode(data)); - } + /** @return The Authorization data base64 decoded */ + public String getDataBase64Decoded() { + return new String(Base64.getDecoder().decode(data)); + } } diff --git a/src/main/java/express/http/request/Request.java b/src/main/java/express/http/request/Request.java index 7496a47..14e7989 100644 --- a/src/main/java/express/http/request/Request.java +++ b/src/main/java/express/http/request/Request.java @@ -7,7 +7,6 @@ import express.filter.Filter; import express.http.Cookie; import express.utils.Utils; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -24,412 +23,401 @@ import java.util.Optional; /** + * Class encapsulating HTTP request data. + * * @author Simon Reinisch - *

- * Class encapsulating HTTP request data */ public class Request { - private final Express express; - - private final String protocol; // Request protocol - private final URI uri; // Request uri - private final InputStream body; // Request body - private final Headers headers; // Request Headers - private final boolean secure; - private final String contentType; // Request content-type - private final long contentLength; // Request content-length - private final String method; // Request method - private final List auth; // Authorization header parsed - private final InetSocketAddress inet; // Client socket address - - private final HashMap middleware; // Middleware Data - private final HashMap cookies; // Request cookies - private final HashMap queries; // URL Query parameters - private final HashMap formQueries; // Form query parameters (application/x-www-form-urlencoded) - - private HashMap params; // URL Params, would be added in ExpressFilterImpl - private String context; // Context which matched - - { - this.middleware = new HashMap<>(); - this.params = new HashMap<>(); - } - - public Request(HttpExchange exchange, Express express) { - this.express = express; - this.method = exchange.getRequestMethod(); - this.uri = exchange.getRequestURI(); - this.headers = exchange.getRequestHeaders(); - this.body = exchange.getRequestBody(); - this.inet = exchange.getRemoteAddress(); - - this.protocol = exchange.getProtocol(); - this.secure = exchange instanceof HttpsExchange; // Can be suckered? - - // Parse content length - String contentLength = headers.get("Content-Length") != null ? headers.get("Content-Length").get(0) : null; - this.contentLength = contentLength != null ? Long.parseLong(contentLength) : -1; - - // Check if the request contains an body-content - this.contentType = headers.get("Content-Type") == null ? "" : headers.get("Content-Type").get(0); - - // Check if the request has an Authorization header - this.auth = Authorization.get(this); - - // Check if the request contains x-www-form-urlencoded form data - this.formQueries = contentType.startsWith("application/x-www-form-urlencoded") - ? RequestUtils.parseRawQuery(Utils.streamToString(body)) - : new HashMap<>(); - - // Parse query and cookies, both returns not null if there is nothing - this.queries = RequestUtils.parseRawQuery(exchange.getRequestURI().getRawQuery()); - this.cookies = RequestUtils.parseCookies(headers); - } - - /** - * @return The request body as InputStream - */ - public InputStream getBody() { - return body; - } - - /** - * Pipe the body from this request to an OutputStream. - * - * @param os The OutputStream. - * @param bufferSize Buffer-size, eg. 4096. - * @throws IOException If an IO-Error occurs. - */ - public void pipe(OutputStream os, int bufferSize) throws IOException { - byte[] buffer = new byte[bufferSize]; - int n; - while ((n = body.read(buffer)) != -1) - os.write(buffer, 0, n); - os.close(); - } - - /** - * Pipe the body from this request to an file. - * If the file not exists, it will be created. - * - * @param f The target file - * @param bufferSize Buffer-size, eg. 4096. - * @throws IOException If an IO-Error occurs. - */ - public void pipe(Path f, int bufferSize) throws IOException { - if (Files.exists(f)) - return; - - Files.createFile(f); - pipe(Files.newOutputStream(f), bufferSize); - } - - /** - * Get a request cookie by name. - * - * @param name The cookie name. - * @return The cookie, null if there is no cookie with this name. - */ - public Cookie getCookie(String name) { - return cookies.get(name); - } - - /** - * Return all cookies from this request. - * - * @return All cookies. - */ - public HashMap getCookies() { - return cookies; - } - - /** - * Add a the content from a middleware - * - * @param middleware The middleware - * @param middlewareData The data from the middleware - */ - public void addMiddlewareContent(Filter middleware, Object middlewareData) { - this.middleware.put(middleware.getName(), middlewareData); - } - - /** - * Get the data from a specific middleware by name (Also the reason - * why the interface ExpressFilter implements a getName()) - * - * @param name The middleware name - * @return The middleware object - */ - public Object getMiddlewareContent(String name) { - return middleware.get(name); - } - - /** - * @return The request user-agent. - */ - public String getUserAgent() { - return headers.get("User-agent").get(0); - } - - /** - * @return The request host. - */ - public String getHost() { - return headers.get("Host").get(0); - } - - /** - * Returns the InetAddress from the client. - * - * @return The InetAddress. - */ - public InetAddress getAddress() { - return inet.getAddress(); - } - - /** - * Returns the IP-Address from the client. - * - * @return The IP-Address. - */ - public String getIp() { - return inet.getAddress().getHostAddress(); - } - - /** - * @return The request content-type. - */ - public String getContentType() { - return contentType; - } - - /** - * Returns the to long parsed content-length. - * - * @return The content-length, -1 if the header was invalid. - */ - public long getContentLength() { - return contentLength; - } - - /** - * @return The request path. - */ - public String getPath() { - return this.uri.getPath(); - } - - /** - * @return The original request uri. - */ - public URI getURI() { - return this.uri; - } - - /** - * @return The request-method. - */ - public String getMethod() { - return this.method; - } - - /** - * Checks if the connection is 'fresh' - * It is true if the cache-control request header doesn't have a no-cache directive, the if-modified-since request header is specified - * and last-modified request header is equal to or earlier than the modified response header or if-none-match request header is *. - * - * @return True if the connection is fresh, false otherwise. - */ - public boolean isFresh() { - - if (headers.containsKey("cache-control") && headers.get("cache-control").get(0) != null && headers.get("cache-control").get(0).equals("no-cache")) + private final Express express; + + private final String protocol; // Request protocol + private final URI uri; // Request uri + private final InputStream body; // Request body + private final Headers headers; // Request Headers + private final boolean secure; + private final String contentType; // Request content-type + private final long contentLength; // Request content-length + private final String method; // Request method + private final List auth; // Authorization header parsed + private final InetSocketAddress inet; // Client socket address + + private final HashMap middleware; // Middleware Data + private final HashMap cookies; // Request cookies + private final HashMap queries; // URL Query parameters + private final HashMap + formQueries; // Form query parameters (application/x-www-form-urlencoded) + + private HashMap params; // URL Params, would be added in ExpressFilterImpl + private String context; // Context which matched + + { + this.middleware = new HashMap<>(); + this.params = new HashMap<>(); + } + + public Request(HttpExchange exchange, Express express) { + this.express = express; + this.method = exchange.getRequestMethod(); + this.uri = exchange.getRequestURI(); + this.headers = exchange.getRequestHeaders(); + this.body = exchange.getRequestBody(); + this.inet = exchange.getRemoteAddress(); + + this.protocol = exchange.getProtocol(); + this.secure = exchange instanceof HttpsExchange; // Can be suckered? + + // Parse content length + String contentLength = + headers.get("Content-Length") != null ? headers.get("Content-Length").get(0) : null; + this.contentLength = contentLength != null ? Long.parseLong(contentLength) : -1; + + // Check if the request contains an body-content + this.contentType = + headers.get("Content-Type") == null ? "" : headers.get("Content-Type").get(0); + + // Check if the request has an Authorization header + this.auth = Authorization.get(this); + + // Check if the request contains x-www-form-urlencoded form data + this.formQueries = + contentType.startsWith("application/x-www-form-urlencoded") + ? RequestUtils.parseRawQuery(Utils.streamToString(body)) + : new HashMap<>(); + + // Parse query and cookies, both returns not null if there is nothing + this.queries = RequestUtils.parseRawQuery(exchange.getRequestURI().getRawQuery()); + this.cookies = RequestUtils.parseCookies(headers); + } + + /** @return The request body as InputStream */ + public InputStream getBody() { + return body; + } + + /** + * Pipe the body from this request to an OutputStream. + * + * @param os The OutputStream. + * @param bufferSize Buffer-size, eg. 4096. + * @throws IOException If an IO-Error occurs. + */ + public void pipe(OutputStream os, int bufferSize) throws IOException { + byte[] buffer = new byte[bufferSize]; + int n; + while ((n = body.read(buffer)) != -1) os.write(buffer, 0, n); + os.close(); + } + + /** + * Pipe the body from this request to an file. If the file not exists, it will be created. + * + * @param f The target file + * @param bufferSize Buffer-size, eg. 4096. + * @throws IOException If an IO-Error occurs. + */ + public void pipe(Path f, int bufferSize) throws IOException { + if (Files.exists(f)) return; + + Files.createFile(f); + pipe(Files.newOutputStream(f), bufferSize); + } + + /** + * Get a request cookie by name. + * + * @param name The cookie name. + * @return The cookie, null if there is no cookie with this name. + */ + public Cookie getCookie(String name) { + return cookies.get(name); + } + + /** + * Return all cookies from this request. + * + * @return All cookies. + */ + public HashMap getCookies() { + return cookies; + } + + /** + * Add a the content from a middleware + * + * @param middleware The middleware + * @param middlewareData The data from the middleware + */ + public void addMiddlewareContent(Filter middleware, Object middlewareData) { + this.middleware.put(middleware.getName(), middlewareData); + } + + /** + * Get the data from a specific middleware by name (Also the reason why the interface + * ExpressFilter implements a getName()) + * + * @param name The middleware name + * @return The middleware object + */ + public Object getMiddlewareContent(String name) { + return middleware.get(name); + } + + /** @return The request user-agent. */ + public String getUserAgent() { + return headers.get("User-agent").get(0); + } + + /** @return The request host. */ + public String getHost() { + return headers.get("Host").get(0); + } + + /** + * Returns the InetAddress from the client. + * + * @return The InetAddress. + */ + public InetAddress getAddress() { + return inet.getAddress(); + } + + /** + * Returns the IP-Address from the client. + * + * @return The IP-Address. + */ + public String getIp() { + return inet.getAddress().getHostAddress(); + } + + /** @return The request content-type. */ + public String getContentType() { + return contentType; + } + + /** + * Returns the to long parsed content-length. + * + * @return The content-length, -1 if the header was invalid. + */ + public long getContentLength() { + return contentLength; + } + + /** @return The request path. */ + public String getPath() { + return this.uri.getPath(); + } + + /** @return The original request uri. */ + public URI getURI() { + return this.uri; + } + + /** @return The request-method. */ + public String getMethod() { + return this.method; + } + + /** + * Checks if the connection is 'fresh' It is true if the cache-control request header doesn't have + * a no-cache directive, the if-modified-since request header is specified and last-modified + * request header is equal to or earlier than the modified response header or if-none-match + * request header is *. + * + * @return True if the connection is fresh, false otherwise. + */ + public boolean isFresh() { + + if (headers.containsKey("cache-control") + && headers.get("cache-control").get(0) != null + && headers.get("cache-control").get(0).equals("no-cache")) return true; + + if (headers.containsKey("if-none-match") + && headers.get("if-none-match").get(0) != null + && headers.get("if-none-match").get(0).equals("*")) return true; + + if (headers.containsKey("if-modified-since") + && headers.containsKey("last-modified") + && headers.containsKey("modified")) { + List lmlist = headers.get("last-modified"); + List mlist = headers.get("modified"); + + // Check lists + if (lmlist.isEmpty() || mlist.isEmpty()) return false; + + String lm = lmlist.get(0); + String m = mlist.get(0); + + // Check header + if (lm != null && m != null) { + try { + + // Try to convert it + Instant lmi = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(lm)); + Instant mi = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(m)); + + if (lmi.isBefore(mi) || lmi.equals(mi)) { return true; + } - if (headers.containsKey("if-none-match") && headers.get("if-none-match").get(0) != null && headers.get("if-none-match").get(0).equals("*")) - return true; - - if (headers.containsKey("if-modified-since") && headers.containsKey("last-modified") && headers.containsKey("modified")) { - List lmlist = headers.get("last-modified"); - List mlist = headers.get("modified"); - - // Check lists - if (lmlist.isEmpty() || mlist.isEmpty()) - return false; - - String lm = lmlist.get(0); - String m = mlist.get(0); - - // Check header - if (lm != null && m != null) { - try { - - // Try to convert it - Instant lmi = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(lm)); - Instant mi = Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(m)); - - if (lmi.isBefore(mi) || lmi.equals(mi)) { - return true; - } - - } catch (Exception ignored) { - } - } + } catch (Exception ignored) { } - - return false; - } - - /** - * Indicates whether the request is "stale" and is the opposite of req.fresh - * - * @return The opposite of req.fresh; - */ - public boolean isStale() { - return !isFresh(); - } - - /** - * Returns whenever the connection is over HTTPS. - * - * @return True when the connection is over HTTPS, false otherwise. - */ - public boolean isSecure() { - return secure; - } - - /** - * Returns true if the 'X-Requested-With' header field is 'XMLHttpRequest'. - * Indicating that the request was made by a client library such as jQuery. - * - * @return True if the 'X-Requested-With' header field is 'XMLHttpRequest'. - */ - public boolean isXHR() { - return headers.containsKey("X-Requested-With") && !headers.get("X-Requested-With").isEmpty() && headers.get("X-Requested-With").get(0).equals("XMLHttpRequest"); - } - - /** - * The connection protocol HTTP/1.0, HTTP/1.1 etc. - * - * @return The connection protocol. - */ - public String getProtocol() { - return protocol; - } - - /** - * @return A list of authorization options in this request - */ - public List getAuthorization() { - return Collections.unmodifiableList(auth); - } - - /** - * @return True if there was an Authorization header and the Authorization object was successfully created. - */ - public boolean hasAuthorization() { - return !auth.isEmpty(); - } - - /** - * Returns a query from a form which uses the 'application/x-www-form-urlencoded' request header. - * - * @param name The name. - * @return The value, null if there is none. - */ - public String getFormQuery(String name) { - return formQueries.get(name); - } - - /** - * Returns an param from a dynamic url. - * - * @param param The param. - * @return The value, null if there is none. - */ - public String getParam(String param) { - return params.get(param); - } - - /** - * Returns the value from the url-query. - * - * @param name The name. - * @return The value, null if there is none. - */ - public String getQuery(String name) { - return queries.get(name); - } - - /** - * Returns all query's from an x-www-form-urlencoded body. - * - * @return An entire list of key-values - */ - public HashMap getFormQuerys() { - return formQueries; - } - - /** - * Returns all params from the url. - * - * @return An entire list of key-values - */ - public HashMap getParams() { - return params; - } - - /** - * Set the params. - * - * @param params Request parameter - */ - public void setParams(HashMap params) { - this.params = params; - } - - /** - * Returns the corresponding context. - * - * @return The corresponding context. - */ - public String getContext() { - return context; - } - - /** - * Set the corresponding context. - * - * @param context The corresponding context. - */ - public void setContext(String context) { - this.context = context; - } - - /** - * Return all url-query's. - * - * @return An entire list of key-values - */ - public HashMap getQuerys() { - return queries; - } - - /** - * Returns an header value. - * - * @param header The header name - * @return A list with values. - */ - public List getHeader(String header) { - return Optional.ofNullable(headers.get(header)).orElse(Collections.emptyList()); - } - - /** - * @return The corresponding express object. - */ - public Express getApp() { - return express; - } - + } + } + + return false; + } + + /** + * Indicates whether the request is "stale" and is the opposite of req.fresh + * + * @return The opposite of req.fresh; + */ + public boolean isStale() { + return !isFresh(); + } + + /** + * Returns whenever the connection is over HTTPS. + * + * @return True when the connection is over HTTPS, false otherwise. + */ + public boolean isSecure() { + return secure; + } + + /** + * Returns true if the 'X-Requested-With' header field is 'XMLHttpRequest'. Indicating that the + * request was made by a client library such as jQuery. + * + * @return True if the 'X-Requested-With' header field is 'XMLHttpRequest'. + */ + public boolean isXHR() { + return headers.containsKey("X-Requested-With") + && !headers.get("X-Requested-With").isEmpty() + && headers.get("X-Requested-With").get(0).equals("XMLHttpRequest"); + } + + /** + * The connection protocol HTTP/1.0, HTTP/1.1 etc. + * + * @return The connection protocol. + */ + public String getProtocol() { + return protocol; + } + + /** @return A list of authorization options in this request */ + public List getAuthorization() { + return Collections.unmodifiableList(auth); + } + + /** + * @return True if there was an Authorization header and the Authorization object was successfully + * created. + */ + public boolean hasAuthorization() { + return !auth.isEmpty(); + } + + /** + * Returns a query from a form which uses the 'application/x-www-form-urlencoded' request header. + * + * @param name The name. + * @return The value, null if there is none. + */ + public String getFormQuery(String name) { + return formQueries.get(name); + } + + /** + * Returns an param from a dynamic url. + * + * @param param The param. + * @return The value, null if there is none. + */ + public String getParam(String param) { + return params.get(param); + } + + /** + * Returns the value from the url-query. + * + * @param name The name. + * @return The value, null if there is none. + */ + public String getQuery(String name) { + return queries.get(name); + } + + /** + * Returns all query's from an x-www-form-urlencoded body. + * + * @return An entire list of key-values + */ + public HashMap getFormQuerys() { + return formQueries; + } + + /** + * Returns all params from the url. + * + * @return An entire list of key-values + */ + public HashMap getParams() { + return params; + } + + /** + * Set the params. + * + * @param params Request parameter + */ + public void setParams(HashMap params) { + this.params = params; + } + + /** + * Returns the corresponding context. + * + * @return The corresponding context. + */ + public String getContext() { + return context; + } + + /** + * Set the corresponding context. + * + * @param context The corresponding context. + */ + public void setContext(String context) { + this.context = context; + } + + /** + * Return all url-query's. + * + * @return An entire list of key-values + */ + public HashMap getQuerys() { + return queries; + } + + /** + * Returns an header value. + * + * @param header The header name + * @return A list with values. + */ + public List getHeader(String header) { + return Optional.ofNullable(headers.get(header)).orElse(Collections.emptyList()); + } + + /** @return The corresponding express object. */ + public Express getApp() { + return express; + } } diff --git a/src/main/java/express/http/request/RequestUtils.java b/src/main/java/express/http/request/RequestUtils.java index a17d78f..0527103 100644 --- a/src/main/java/express/http/request/RequestUtils.java +++ b/src/main/java/express/http/request/RequestUtils.java @@ -2,7 +2,6 @@ import com.sun.net.httpserver.Headers; import express.http.Cookie; - import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.HashMap; @@ -10,99 +9,100 @@ final class RequestUtils { - private RequestUtils() {} + private RequestUtils() {} - /** - * Extract the cookies from the 'Cookie' header. - * - * @param headers The Headers - * @return An HashMap with the cookie name as key and the complete cookie as value. - */ - static HashMap parseCookies(Headers headers) { - HashMap cookieList = new HashMap<>(); - List headerCookies = headers.get("Cookie"); + /** + * Extract the cookies from the 'Cookie' header. + * + * @param headers The Headers + * @return An HashMap with the cookie name as key and the complete cookie as value. + */ + static HashMap parseCookies(Headers headers) { + HashMap cookieList = new HashMap<>(); + List headerCookies = headers.get("Cookie"); - if (headerCookies == null || headerCookies.isEmpty()) { - return cookieList; - } + if (headerCookies == null || headerCookies.isEmpty()) { + return cookieList; + } - char[] chars = headerCookies.get(0).toCharArray(); - StringBuilder key = new StringBuilder(); - StringBuilder val = new StringBuilder(); - boolean swap = false; - - for (char c : chars) { - if (c == '=') { - swap = true; - } else if (c == ';') { - String rkey = key.toString().trim(); - cookieList.put(rkey, new Cookie(rkey, val.toString())); - - key.setLength(0); - val.setLength(0); - swap = false; - } else if (swap) { - val.append(c); - } else { - key.append(c); - } - } + char[] chars = headerCookies.get(0).toCharArray(); + StringBuilder key = new StringBuilder(); + StringBuilder val = new StringBuilder(); + boolean swap = false; + + for (char c : chars) { + if (c == '=') { + swap = true; + } else if (c == ';') { + String rkey = key.toString().trim(); + cookieList.put(rkey, new Cookie(rkey, val.toString())); + + key.setLength(0); + val.setLength(0); + swap = false; + } else if (swap) { + val.append(c); + } else { + key.append(c); + } + } - if (key.length() > 0 && val.length() > 0) { - String rkey = key.toString().trim(); - cookieList.put(rkey, new Cookie(rkey, val.toString())); - } + if (key.length() > 0 && val.length() > 0) { + String rkey = key.toString().trim(); + cookieList.put(rkey, new Cookie(rkey, val.toString())); + } - return cookieList; + return cookieList; + } + + /** + * Method to extract the query's from an url. + * + * @param rawQuery The raw query + * @return An list with key-values which are encoded in UTF8. + */ + static HashMap parseRawQuery(String rawQuery) { + HashMap querys = new HashMap<>(); + + // Return empty map on null + if (rawQuery == null) { + return querys; } - /** - * Method to extract the query's from an url. - * - * @param rawQuery The raw query - * @return An list with key-values which are encoded in UTF8. - */ - static HashMap parseRawQuery(String rawQuery) { - HashMap querys = new HashMap<>(); - - // Return empty map on null - if (rawQuery == null) { - return querys; - } + StringBuilder key = new StringBuilder(); + StringBuilder val = new StringBuilder(); + char[] chars = rawQuery.toCharArray(); + boolean keyac = false; + char c = '='; - StringBuilder key = new StringBuilder(); - StringBuilder val = new StringBuilder(); - char[] chars = rawQuery.toCharArray(); - boolean keyac = false; - char c = '='; - - for (char cc : chars) { - c = cc; - - if (c == '=') { - keyac = true; - } else if (c == '&') { - - try { - querys.put(URLDecoder.decode(key.toString(), "UTF-8"), URLDecoder.decode(val.toString(), "UTF8")); - } catch (UnsupportedEncodingException ignored) { - } - - key.setLength(0); - val.setLength(0); - keyac = false; - } else if (keyac) { - val.append(c); - } else { - key.append(c); - } - } + for (char cc : chars) { + c = cc; - if (c != '=' && c != '&') { - querys.put(key.toString(), val.toString()); + if (c == '=') { + keyac = true; + } else if (c == '&') { + + try { + querys.put( + URLDecoder.decode(key.toString(), "UTF-8"), + URLDecoder.decode(val.toString(), "UTF8")); + } catch (UnsupportedEncodingException ignored) { } - return querys; + key.setLength(0); + val.setLength(0); + keyac = false; + } else if (keyac) { + val.append(c); + } else { + key.append(c); + } + } + + if (c != '=' && c != '&') { + querys.put(key.toString(), val.toString()); } + return querys; + } } diff --git a/src/main/java/express/http/response/Response.java b/src/main/java/express/http/response/Response.java index 937b5d9..6a2137f 100644 --- a/src/main/java/express/http/response/Response.java +++ b/src/main/java/express/http/response/Response.java @@ -6,9 +6,6 @@ import express.utils.MediaType; import express.utils.Status; import express.utils.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -16,331 +13,323 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * @author Simon Reinisch * Class for an http-response. + * + * @author Simon Reinisch */ public class Response { - private static final Logger log = LoggerFactory.getLogger(Response.class); + private static final Logger log = LoggerFactory.getLogger(Response.class); + + private final HttpExchange httpExchange; + private final OutputStream body; + private final Headers headers; + + private String contentType = MediaType._txt.getMIME(); + private boolean isClose = false; + private long contentLength = 0; + private int status = 200; + + public Response(HttpExchange exchange) { + this.httpExchange = exchange; + this.headers = exchange.getResponseHeaders(); + this.body = exchange.getResponseBody(); + } + + /** + * Add an specific value to the reponse header. + * + * @param key The header name. + * @param value The header value. + * @return This Response instance. + */ + public Response setHeader(String key, String value) { + headers.add(key, value); + return this; + } + + /** + * @param key The header key. + * @return The values which are associated with this key. + */ + public List getHeader(String key) { + return headers.get(key); + } + + /** + * Sets the response Location HTTP header to the specified path parameter. + * + * @param location The location. + */ + public void redirect(String location) { + headers.add("Location", location); + setStatus(Status._302); + send(); + } + + /** + * Set an cookie. + * + * @param cookie The cookie. + * @return This Response instance. + */ + public Response setCookie(Cookie cookie) { + if (isClosed()) return this; + this.headers.add("Set-Cookie", cookie.toString()); + return this; + } + + /** @return Current response status. */ + public int getStatus() { + return this.status; + } + + /** + * Set the response-status. Default is 200 (ok). + * + * @param status The response status. + * @return This Response instance. + */ + public Response setStatus(Status status) { + if (isClosed()) return this; + this.status = status.getCode(); + return this; + } + + /** + * Set the response-status and send the response. + * + * @param status The response status. + */ + public void sendStatus(Status status) { + if (isClosed()) return; + this.status = status.getCode(); + send(); + } + + /** @return The current contentType */ + public String getContentType() { + return contentType; + } + + /** + * Set the contentType for this response. + * + * @param contentType - The contentType + */ + public void setContentType(MediaType contentType) { + this.contentType = contentType.getMIME(); + } + + /** + * Set the contentType for this response. + * + * @param contentType - The contentType + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** Send an empty response (Content-Length = 0) */ + public void send() { + if (isClosed()) return; + this.contentLength = 0; + sendHeaders(); + close(); + } + + /** + * Send an string as response. + * + * @param s The string. + */ + public void send(String s) { + if (s == null) { + send(); + return; + } - private final HttpExchange httpExchange; - private final OutputStream body; - private final Headers headers; + if (isClosed()) return; + byte[] data = s.getBytes(); - private String contentType = MediaType._txt.getMIME(); - private boolean isClose = false; - private long contentLength = 0; - private int status = 200; + this.contentLength = data.length; + sendHeaders(); - public Response(HttpExchange exchange) { - this.httpExchange = exchange; - this.headers = exchange.getResponseHeaders(); - this.body = exchange.getResponseBody(); + try { + this.body.write(s.getBytes()); + } catch (IOException e) { + log.error("Failed to write char sequence to client.", e); } - /** - * Add an specific value to the reponse header. - * - * @param key The header name. - * @param value The header value. - * @return This Response instance. - */ - public Response setHeader(String key, String value) { - headers.add(key, value); - return this; + close(); + } + + /** + * Sets the 'Content-Disposition' header to 'attachment' and his Content-Disposition "filename=" + * parameter to the file name. Normally this triggers an download event client-side. + * + * @param file The file which will be send as attachment. + * @return True if the file was successfully send, false if the file doesn't exists or the respose + * is already closed. + */ + public boolean sendAttachment(Path file) { + if (isClosed() || !Files.isRegularFile(file)) { + return false; } - /** - * @param key The header key. - * @return The values which are associated with this key. - */ - public List getHeader(String key) { - return headers.get(key); - } + String dispo = "attachment; filename=\"" + file.getFileName() + "\""; + setHeader("Content-Disposition", dispo); - /** - * Sets the response Location HTTP header to the specified path parameter. - * - * @param location The location. - */ - public void redirect(String location) { - headers.add("Location", location); - setStatus(Status._302); - send(); - } + return send(file); + } - /** - * Set an cookie. - * - * @param cookie The cookie. - * @return This Response instance. - */ - public Response setCookie(Cookie cookie) { - if (isClosed()) return this; - this.headers.add("Set-Cookie", cookie.toString()); - return this; - } + /** + * Send an entire file as response The mime type will be automatically detected. + * + * @param file The file. + * @return True if the file was successfully send, false if the file doesn't exists or the respose + * is already closed. + */ + public boolean send(Path file) { - /** - * @return Current response status. - */ - public int getStatus() { - return this.status; + if (isClosed() || !Files.isRegularFile(file)) { + return false; } - /** - * Set the response-status. - * Default is 200 (ok). - * - * @param status The response status. - * @return This Response instance. - */ - public Response setStatus(Status status) { - if (isClosed()) return this; - this.status = status.getCode(); - return this; - } + try { + this.contentLength = Files.size(file); - /** - * Set the response-status and send the response. - * - * @param status The response status. - */ - public void sendStatus(Status status) { - if (isClosed()) return; - this.status = status.getCode(); - send(); - } + // Detect content type + MediaType mediaType = Utils.getContentType(file); + this.contentType = mediaType == null ? null : mediaType.getMIME(); - /** - * @return The current contentType - */ - public String getContentType() { - return contentType; - } + // Send header + sendHeaders(); - /** - * Set the contentType for this response. - * - * @param contentType - The contentType - */ - public void setContentType(MediaType contentType) { - this.contentType = contentType.getMIME(); - } + // Send file + InputStream fis = Files.newInputStream(file, StandardOpenOption.READ); + byte[] buffer = new byte[1024]; + int n; + while ((n = fis.read(buffer)) != -1) { + this.body.write(buffer, 0, n); + } - /** - * Set the contentType for this response. - * - * @param contentType - The contentType - */ - public void setContentType(String contentType) { - this.contentType = contentType; - } + fis.close(); - /** - * Send an empty response (Content-Length = 0) - */ - public void send() { - if (isClosed()) return; - this.contentLength = 0; - sendHeaders(); - close(); + } catch (IOException e) { + log.error("Failed to pipe file to output stream.", e); + return false; + } finally { + close(); } - /** - * Send an string as response. - * - * @param s The string. - */ - public void send(String s) { - if (s == null) { - send(); - return; - } - - if (isClosed()) return; - byte[] data = s.getBytes(); - - this.contentLength = data.length; - sendHeaders(); - - try { - this.body.write(s.getBytes()); - } catch (IOException e) { - log.error("Failed to write char sequence to client.",e ); - } - - close(); - } + return true; + } - /** - * Sets the 'Content-Disposition' header to 'attachment' and his - * Content-Disposition "filename=" parameter to the file name. - * Normally this triggers an download event client-side. - * - * @param file The file which will be send as attachment. - * @return True if the file was successfully send, false if the file doesn't exists or the respose is already closed. - */ - public boolean sendAttachment(Path file) { - if (isClosed() || !Files.isRegularFile(file)) { - return false; - } - - String dispo = "attachment; filename=\"" + file.getFileName() + "\""; - setHeader("Content-Disposition", dispo); - - return send(file); - } + /** + * Send a byte array as response. Content type will be set to application/octet-streamFrom + * + * @param bytes Byte array + * @return If operation was successful + */ + public boolean sendBytes(byte[] bytes) { - /** - * Send an entire file as response - * The mime type will be automatically detected. - * - * @param file The file. - * @return True if the file was successfully send, false if the file doesn't exists or the respose is already closed. - */ - public boolean send(Path file) { - - if (isClosed() || !Files.isRegularFile(file)) { - return false; - } - - try { - this.contentLength = Files.size(file); - - // Detect content type - MediaType mediaType = Utils.getContentType(file); - this.contentType = mediaType == null ? null : mediaType.getMIME(); - - // Send header - sendHeaders(); - - // Send file - InputStream fis = Files.newInputStream(file, StandardOpenOption.READ); - byte[] buffer = new byte[1024]; - int n; - while ((n = fis.read(buffer)) != -1) { - this.body.write(buffer, 0, n); - } - - fis.close(); - - } catch (IOException e) { - log.error("Failed to pipe file to output stream.", e); - return false; - } finally { - close(); - } - - return true; + if (isClosed() || bytes == null) { + return false; } - /** - * Send a byte array as response. Content type will be - * set to application/octet-streamFrom - * - * @param bytes Byte array - * @return If operation was successful - */ - public boolean sendBytes(byte[] bytes) { - - if (isClosed() || bytes == null) { - return false; - } - - try { - this.contentLength = bytes.length; - - // Set content type to octet streamFrom - this.contentType = MediaType._bin.getMIME(); - - // Send header - sendHeaders(); - - // Write bytes to body - this.body.write(bytes); - } catch (IOException e) { - log.error("Failed to pipe file to output stream.", e); - return false; - } finally { - close(); - } - - return true; - } + try { + this.contentLength = bytes.length; - /** - * Streams an input stream to the client. - * Requires a contentLength as well as a MediaType - * - * @param contentLength Total size - * @param is Inputstream - * @param mediaType Stream type - * @return If operation was successful - */ - public boolean streamFrom(long contentLength, InputStream is, MediaType mediaType) { - - if (isClosed() || is == null) { - return false; - } - - try { - this.contentLength = contentLength; - - // Set content type to octet-stream - this.contentType = mediaType.getMIME(); - - // Send header - sendHeaders(); - - // Write bytes to body - byte[] buffer = new byte[4096]; - int n; - while ((n = is.read(buffer)) != -1) { - this.body.write(buffer, 0, n); - } - - is.close(); - } catch (IOException e) { - log.error("Failed to pipe file to output stream.", e); - return false; - } finally { - close(); - } - - return true; + // Set content type to octet streamFrom + this.contentType = MediaType._bin.getMIME(); + + // Send header + sendHeaders(); + + // Write bytes to body + this.body.write(bytes); + } catch (IOException e) { + log.error("Failed to pipe file to output stream.", e); + return false; + } finally { + close(); } - /** - * @return If the response is already closed (headers have been sent). - */ - public boolean isClosed() { - return this.isClose; + return true; + } + + /** + * Streams an input stream to the client. Requires a contentLength as well as a MediaType + * + * @param contentLength Total size + * @param is Inputstream + * @param mediaType Stream type + * @return If operation was successful + */ + public boolean streamFrom(long contentLength, InputStream is, MediaType mediaType) { + + if (isClosed() || is == null) { + return false; } - private void sendHeaders() { - try { + try { + this.contentLength = contentLength; - // Fallback - String contentType = getContentType() == null ? MediaType._bin.getExtension() : getContentType(); + // Set content type to octet-stream + this.contentType = mediaType.getMIME(); - // Set header and send response - this.headers.set("Content-Type", contentType); - this.httpExchange.sendResponseHeaders(status, contentLength); - } catch (IOException e) { - log.error("Failed to send headers.", e); - } - } + // Send header + sendHeaders(); - private void close() { - try { - this.body.close(); - this.isClose = true; - } catch (IOException e) { - log.error("Failed to close output stream.", e); - } + // Write bytes to body + byte[] buffer = new byte[4096]; + int n; + while ((n = is.read(buffer)) != -1) { + this.body.write(buffer, 0, n); + } + + is.close(); + } catch (IOException e) { + log.error("Failed to pipe file to output stream.", e); + return false; + } finally { + close(); } + return true; + } + + /** @return If the response is already closed (headers have been sent). */ + public boolean isClosed() { + return this.isClose; + } + + private void sendHeaders() { + try { + + // Fallback + String contentType = + getContentType() == null ? MediaType._bin.getExtension() : getContentType(); + + // Set header and send response + this.headers.set("Content-Type", contentType); + this.httpExchange.sendResponseHeaders(status, contentLength); + } catch (IOException e) { + log.error("Failed to send headers.", e); + } + } + + private void close() { + try { + this.body.close(); + this.isClose = true; + } catch (IOException e) { + log.error("Failed to close output stream.", e); + } + } } diff --git a/src/main/java/express/middleware/CookieSession.java b/src/main/java/express/middleware/CookieSession.java index 16202a0..bf8376a 100644 --- a/src/main/java/express/middleware/CookieSession.java +++ b/src/main/java/express/middleware/CookieSession.java @@ -8,78 +8,77 @@ import express.http.request.Request; import express.http.response.Response; import express.utils.Utils; - import java.util.concurrent.ConcurrentHashMap; /** + * A middleware to create cookie-sessions. + * * @author Simon Reinisch - * An middleware to create cookie-sessions. */ final class CookieSession implements HttpRequestHandler, Filter, FilterTask { - private final static String MIDDLEWARE_NAME = "sessioncookie"; - - private final ConcurrentHashMap cookies = new ConcurrentHashMap<>(); - private final String cookieName; - private final long maxAge; - - CookieSession(String cookieName, long maxAge) { - this.cookieName = cookieName; - this.maxAge = maxAge; - } + private static final String MIDDLEWARE_NAME = "sessioncookie"; - @SuppressWarnings("SuspiciousMethodCalls") - @Override - public void handle(Request req, Response res) { - Cookie cookie = req.getCookie(cookieName); + private final ConcurrentHashMap cookies = new ConcurrentHashMap<>(); + private final String cookieName; + private final long maxAge; - if (cookie != null && cookies.containsKey(cookie.getValue())) { - req.addMiddlewareContent(this, cookies.get(cookie.getValue())); - } else { - String token; + CookieSession(String cookieName, long maxAge) { + this.cookieName = cookieName; + this.maxAge = maxAge; + } - do { - token = Utils.randomToken(32, 16); - } while (cookies.contains(token)); + @SuppressWarnings("SuspiciousMethodCalls") + @Override + public void handle(Request req, Response res) { + Cookie cookie = req.getCookie(cookieName); - cookie = new Cookie(cookieName, token).setMaxAge(maxAge); - res.setCookie(cookie); - - SessionCookie sessionCookie = new SessionCookie(maxAge); - cookies.put(token, sessionCookie); - - req.addMiddlewareContent(this, sessionCookie); - } - } + if (cookie != null && cookies.containsKey(cookie.getValue())) { + req.addMiddlewareContent(this, cookies.get(cookie.getValue())); + } else { + String token; - @Override - public String getName() { - return MIDDLEWARE_NAME; - } + do { + token = Utils.randomToken(32, 16); + } while (cookies.contains(token)); - @Override - public void onStart() { - } + cookie = new Cookie(cookieName, token).setMaxAge(maxAge); + res.setCookie(cookie); - @Override - public void onStop() { - cookies.clear(); - } + SessionCookie sessionCookie = new SessionCookie(maxAge); + cookies.put(token, sessionCookie); - @Override - public long getDelay() { - return 60000; + req.addMiddlewareContent(this, sessionCookie); } - - @Override - public void onUpdate() { - long current = System.currentTimeMillis(); - - cookies.forEach((cookieHash, cookie) -> { - if (current > cookie.getCreated() + cookie.getMaxAge()) { - cookies.remove(cookieHash); - } + } + + @Override + public String getName() { + return MIDDLEWARE_NAME; + } + + @Override + public void onStart() {} + + @Override + public void onStop() { + cookies.clear(); + } + + @Override + public long getDelay() { + return 60000; + } + + @Override + public void onUpdate() { + long current = System.currentTimeMillis(); + + cookies.forEach( + (cookieHash, cookie) -> { + if (current > cookie.getCreated() + cookie.getMaxAge()) { + cookies.remove(cookieHash); + } }); - } - + } } diff --git a/src/main/java/express/middleware/Cors.java b/src/main/java/express/middleware/Cors.java index e8a0ba5..26f50ae 100644 --- a/src/main/java/express/middleware/Cors.java +++ b/src/main/java/express/middleware/Cors.java @@ -7,42 +7,42 @@ public class Cors implements HttpRequestHandler { - private final CorsOptions options; + private final CorsOptions options; - public Cors(CorsOptions options) { - this.options = options; - } + public Cors(CorsOptions options) { + this.options = options; + } + + @Override + public void handle(Request req, Response res) { + CorsOptions.Filter filter = this.options.getFilter(); - @Override - public void handle(Request req, Response res) { - CorsOptions.Filter filter = this.options.getFilter(); - - // Check if filter is present - if (filter != null && !filter.shouldBypass(req)) { - return; - } - - // Acquire options - boolean ac = this.options.isAllowCredentials(); - String origins = this.options.getOrigin(); - String[] headers = this.options.getHeaders(); - RequestMethod[] methods = this.options.getMethods(); - - // Apply headers - res.setHeader("Access-Control-Allow-Credentials", Boolean.toString(ac)); - res.setHeader("Access-Control-Allow-Origin", origins != null ? origins : "*"); - res.setHeader("Access-Control-Allow-Methods", methods != null ? join(methods) : "*"); - res.setHeader("Access-Control-Request-Headers", headers != null ? join(headers) : "*"); + // Check if filter is present + if (filter != null && !filter.shouldBypass(req)) { + return; } - private String join(Object[] objects) { - StringBuilder sb = new StringBuilder(); + // Acquire options + boolean ac = this.options.isAllowCredentials(); + String origins = this.options.getOrigin(); + String[] headers = this.options.getHeaders(); + RequestMethod[] methods = this.options.getMethods(); - for (Object o : objects) { - sb.append(o.toString()).append(", "); - } + // Apply headers + res.setHeader("Access-Control-Allow-Credentials", Boolean.toString(ac)); + res.setHeader("Access-Control-Allow-Origin", origins != null ? origins : "*"); + res.setHeader("Access-Control-Allow-Methods", methods != null ? join(methods) : "*"); + res.setHeader("Access-Control-Request-Headers", headers != null ? join(headers) : "*"); + } - String string = sb.toString(); - return string.substring(0, string.length() - 2); + private String join(Object[] objects) { + StringBuilder sb = new StringBuilder(); + + for (Object o : objects) { + sb.append(o.toString()).append(", "); } + + String string = sb.toString(); + return string.substring(0, string.length() - 2); + } } diff --git a/src/main/java/express/middleware/CorsOptions.java b/src/main/java/express/middleware/CorsOptions.java index e998efd..8034001 100644 --- a/src/main/java/express/middleware/CorsOptions.java +++ b/src/main/java/express/middleware/CorsOptions.java @@ -5,65 +5,70 @@ public class CorsOptions { - private boolean allowCredentials; - private RequestMethod[] methods; - private String[] headers; - private String origin; - private Filter filter; - - public CorsOptions(boolean allowCredentials, String origin, String[] headers, RequestMethod[] methods, Filter filter) { - this.allowCredentials = allowCredentials; - this.origin = origin; - this.filter = filter; - this.methods = methods; - this.headers = headers; - } - - public CorsOptions() { - this(false, null, null, null, null); - } - - public String[] getHeaders() { - return headers; - } - - public void setHeaders(String[] headers) { - this.headers = headers; - } - - public String getOrigin() { - return origin; - } - - public boolean isAllowCredentials() { - return allowCredentials; - } - - public void setAllowCredentials(boolean allowCredentials) { - this.allowCredentials = allowCredentials; - } - - public void setOrigin(String origin) { - this.origin = origin; - } - - public RequestMethod[] getMethods() { - return methods; - } - - public void setMethods(RequestMethod[] methods) { - this.methods = methods; - } - - public Filter getFilter() { - return filter; - } - - public void setFilter(Filter filter) { - this.filter = filter; - } - - interface Filter { - boolean shouldBypass(Request req); - } -} \ No newline at end of file + private boolean allowCredentials; + private RequestMethod[] methods; + private String[] headers; + private String origin; + private Filter filter; + + public CorsOptions( + boolean allowCredentials, + String origin, + String[] headers, + RequestMethod[] methods, + Filter filter) { + this.allowCredentials = allowCredentials; + this.origin = origin; + this.filter = filter; + this.methods = methods; + this.headers = headers; + } + + public CorsOptions() { + this(false, null, null, null, null); + } + + public String[] getHeaders() { + return headers; + } + + public void setHeaders(String[] headers) { + this.headers = headers; + } + + public String getOrigin() { + return origin; + } + + public boolean isAllowCredentials() { + return allowCredentials; + } + + public void setAllowCredentials(boolean allowCredentials) { + this.allowCredentials = allowCredentials; + } + + public void setOrigin(String origin) { + this.origin = origin; + } + + public RequestMethod[] getMethods() { + return methods; + } + + public void setMethods(RequestMethod[] methods) { + this.methods = methods; + } + + public Filter getFilter() { + return filter; + } + + public void setFilter(Filter filter) { + this.filter = filter; + } + + interface Filter { + boolean shouldBypass(Request req); + } +} diff --git a/src/main/java/express/middleware/DotFiles.java b/src/main/java/express/middleware/DotFiles.java index cd22873..761430c 100644 --- a/src/main/java/express/middleware/DotFiles.java +++ b/src/main/java/express/middleware/DotFiles.java @@ -1,10 +1,8 @@ package express.middleware; -/** - * @author Simon Reinisch - */ +/** @author Simon Reinisch */ public enum DotFiles { - IGNORE, - DENY, - ALLOW + IGNORE, + DENY, + ALLOW } diff --git a/src/main/java/express/middleware/FileProvider.java b/src/main/java/express/middleware/FileProvider.java index e761591..31fbc51 100644 --- a/src/main/java/express/middleware/FileProvider.java +++ b/src/main/java/express/middleware/FileProvider.java @@ -5,9 +5,6 @@ import express.http.response.Response; import express.utils.Status; import express.utils.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -17,141 +14,141 @@ import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** + * A middleware to provide access to static server-files. + * * @author Simon Reinisch - * An middleware to provide access to static server-files. */ public final class FileProvider implements HttpRequestHandler { - private static final Logger log = LoggerFactory.getLogger(FileProvider.class); - - private FileProviderOptions options; - private String root; + private static final Logger log = LoggerFactory.getLogger(FileProvider.class); - FileProvider(String root, FileProviderOptions options) throws IOException { - Path rootDir = Paths.get(root); + private FileProviderOptions options; + private String root; - if (!Files.exists(rootDir) || !Files.isDirectory(rootDir)) { - throw new IOException(rootDir + " does not exists or isn't a directory."); - } + FileProvider(String root, FileProviderOptions options) throws IOException { + Path rootDir = Paths.get(root); - this.root = rootDir.toAbsolutePath().toString(); - this.options = options; + if (!Files.exists(rootDir) || !Files.isDirectory(rootDir)) { + throw new IOException(rootDir + " does not exists or isn't a directory."); } - @Override - public void handle(Request req, Response res) { - try { - String path = req.getURI().getPath(); + this.root = rootDir.toAbsolutePath().toString(); + this.options = options; + } - // Check context - String context = req.getContext(); - if (path.indexOf(context) == 0) { - path = path.substring(context.length()); - } + @Override + public void handle(Request req, Response res) { + try { + String path = req.getURI().getPath(); - // If the path is empty try index.html - if (path.length() <= 1) { - path = "index.html"; - } + // Check context + String context = req.getContext(); + if (path.indexOf(context) == 0) { + path = path.substring(context.length()); + } - Path reqFile = Paths.get(root + File.separator + path); + // If the path is empty try index.html + if (path.length() <= 1) { + path = "index.html"; + } - /* - * If the file wasn't found, it will search in the target-directory for - * the file by the raw-name without extension. - */ - if (options.isFallBackSearching() && !Files.exists(reqFile) && !Files.isDirectory(reqFile)) { - String name = reqFile.getFileName().toString(); + Path reqFile = Paths.get(root + File.separator + path); - try { - Path parent = reqFile.getParent(); + /* + * If the file wasn't found, it will search in the target-directory for + * the file by the raw-name without extension. + */ + if (options.isFallBackSearching() && !Files.exists(reqFile) && !Files.isDirectory(reqFile)) { + String name = reqFile.getFileName().toString(); - // Check if reading is allowed - if (Files.isReadable(parent)) { + try { + Path parent = reqFile.getParent(); - Optional founded = Files.walk(parent) - .filter(sub -> getBaseName(sub).equals(name)) - .findFirst(); + // Check if reading is allowed + if (Files.isReadable(parent)) { - if (founded.isPresent()) { - reqFile = founded.get(); - } - } - } catch (IOException e) { - log.error("Cannot walk file tree.", e); - } - } + Optional founded = + Files.walk(parent).filter(sub -> getBaseName(sub).equals(name)).findFirst(); - if (Files.exists(reqFile) && Files.isRegularFile(reqFile)) { - - if (reqFile.getFileName().toString().charAt(0) == '.') { - switch (options.getDotFiles()) { - case IGNORE: - res.setStatus(Status._404); - return; - case DENY: - res.setStatus(Status._403); - return; - } - } - - // Check if extension is present - if (options.getExtensions() != null) { - String reqEx = Utils.getExtension(reqFile); - - if (reqEx == null) { - return; - } - - for (String ex : options.getExtensions()) { - if (reqEx.equals(ex)) { - finish(reqFile, req, res); - break; - } - } - - res.setStatus(Status._403); - } else { - finish(reqFile, req, res); - } + if (founded.isPresent()) { + reqFile = founded.get(); } - } catch (Exception e) { - e.printStackTrace(); + } + } catch (IOException e) { + log.error("Cannot walk file tree.", e); } - } - - private void finish(Path file, Request req, Response res) { - if (options.getHandler() != null) { - options.getHandler().handle(req, res); + } + + if (Files.exists(reqFile) && Files.isRegularFile(reqFile)) { + + if (reqFile.getFileName().toString().charAt(0) == '.') { + switch (options.getDotFiles()) { + case IGNORE: + res.setStatus(Status._404); + return; + case DENY: + res.setStatus(Status._403); + return; + } } - try { + // Check if extension is present + if (options.getExtensions() != null) { + String reqEx = Utils.getExtension(reqFile); - // Apply header - if (options.isLastModified()) { - Instant instant = Instant.ofEpochMilli(Files.getLastModifiedTime(file).toMillis()); - DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME - .withZone(ZoneOffset.UTC); + if (reqEx == null) { + return; + } - res.setHeader("Last-Modified", formatter.format(instant)); + for (String ex : options.getExtensions()) { + if (reqEx.equals(ex)) { + finish(reqFile, req, res); + break; } + } - } catch (IOException e) { - res.sendStatus(Status._500); - log.error("Cannot read LastModifiedTime from file [{}]", file.toString(), e); - return; + res.setStatus(Status._403); + } else { + finish(reqFile, req, res); } + } + } catch (Exception e) { + e.printStackTrace(); + } + } - res.setHeader("Cache-Control", String.valueOf(options.getMaxAge())); - res.send(file); + private void finish(Path file, Request req, Response res) { + if (options.getHandler() != null) { + options.getHandler().handle(req, res); } - private String getBaseName(Path path) { - String name = path.getFileName().toString(); - int index = name.lastIndexOf('.'); - return index == -1 ? name : name.substring(0, index); + try { + + // Apply header + if (options.isLastModified()) { + Instant instant = Instant.ofEpochMilli(Files.getLastModifiedTime(file).toMillis()); + DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC); + + res.setHeader("Last-Modified", formatter.format(instant)); + } + + } catch (IOException e) { + res.sendStatus(Status._500); + log.error("Cannot read LastModifiedTime from file [{}]", file.toString(), e); + return; } + res.setHeader("Cache-Control", String.valueOf(options.getMaxAge())); + res.send(file); + } + + private String getBaseName(Path path) { + String name = path.getFileName().toString(); + int index = name.lastIndexOf('.'); + return index == -1 ? name : name.substring(0, index); + } } diff --git a/src/main/java/express/middleware/FileProviderOptions.java b/src/main/java/express/middleware/FileProviderOptions.java index 67a1661..4d8ef8c 100644 --- a/src/main/java/express/middleware/FileProviderOptions.java +++ b/src/main/java/express/middleware/FileProviderOptions.java @@ -3,147 +3,131 @@ import express.http.HttpRequestHandler; /** + * Options-Class for FileProvider. + * * @author Simon R. - *

- * Options-Class for FileProvider */ public class FileProviderOptions { - private String[] extensions; - private HttpRequestHandler handler; - private boolean fallBackSearching; - private boolean lastModified; - private long maxAge; - private DotFiles dotFiles; - - { - // Initalize some values. - this.extensions = null; - this.handler = null; - this.fallBackSearching = false; - this.lastModified = true; - this.maxAge = 0; - this.dotFiles = DotFiles.IGNORE; - } - - public FileProviderOptions() { - } - - /** - * @return The current allowed extensions. - */ - public String[] getExtensions() { - return extensions; - } - - /** - * Set the extension from the file which can be accessed outside. - * By default all extensions are allowed. - * - * @param extensions The extensions. - * @return This instance. - */ - public FileProviderOptions setExtensions(String... extensions) { - this.extensions = extensions; - return this; - } - - /** - * @return If the fallback-search is activated. - */ - public boolean isFallBackSearching() { - return fallBackSearching; - } - - /** - * Activate the fallback-search. - * E.g. if an request to /js/code.js was made but the - * requested resource cannot be found. It will be looked for an file called code - * and return it. - *

- * Default is false. - * - * @param fallBackSearching If you want to activate the file-fallback-search. - * @return This instance. - */ - public FileProviderOptions setFallBackSearching(boolean fallBackSearching) { - this.fallBackSearching = fallBackSearching; - return this; - } - - /** - * @return If the last modified date of the file on the OS will be send. - */ - public boolean isLastModified() { - return lastModified; - } - - /** - * Set the Last-Modified header to the last modified date of the file on the OS. - * Default is true. - * - * @param lastModified If you want to send the last modified date of the file on the OS. - * @return This instance. - */ - public FileProviderOptions setLastModified(boolean lastModified) { - this.lastModified = lastModified; - return this; - } - - /** - * @return The current maxAge of of the Cache-Control header. - */ - public long getMaxAge() { - return maxAge; - } - - /** - * Set the max-age property of the Cache-Control header in milliseconds or a string in ms format. - *

- * Default is 0. - * - * @param maxAge The new maxAge value. - * @return This instance. - */ - public FileProviderOptions setMaxAge(long maxAge) { - this.maxAge = maxAge; - return this; - } - - /** - * @return The current corresponding handler. - */ - HttpRequestHandler getHandler() { - return handler; - } - - /** - * Add an alternate request-handler which will be fired before the file will be send. - * - * @param handler The HttpRequest handler. - * @return This instance. - */ - public FileProviderOptions setHandler(HttpRequestHandler handler) { - this.handler = handler; - return this; - } - - /** - * @return How Dot-files will be currently handled. - */ - public DotFiles getDotFiles() { - return dotFiles; - } - - /** - * Set how ".foo" file will be handled. - * Ignore: Act if these files don't exists, response with 404 - * Deny: Deny the file, response with 303 and do nothing. - * Allow: No special treatment for this files. - * - * @param dotFiles The handling type. - */ - public void setDotFiles(DotFiles dotFiles) { - this.dotFiles = dotFiles; - } + private String[] extensions; + private HttpRequestHandler handler; + private boolean fallBackSearching; + private boolean lastModified; + private long maxAge; + private DotFiles dotFiles; + + { + // Initalize some values. + this.extensions = null; + this.handler = null; + this.fallBackSearching = false; + this.lastModified = true; + this.maxAge = 0; + this.dotFiles = DotFiles.IGNORE; + } + + public FileProviderOptions() {} + + /** @return The current allowed extensions. */ + public String[] getExtensions() { + return extensions; + } + + /** + * Set the extension from the file which can be accessed outside. By default all extensions are + * allowed. + * + * @param extensions The extensions. + * @return This instance. + */ + public FileProviderOptions setExtensions(String... extensions) { + this.extensions = extensions; + return this; + } + + /** @return If the fallback-search is activated. */ + public boolean isFallBackSearching() { + return fallBackSearching; + } + + /** + * Activate the fallback-search. E.g. if an request to /js/code.js was made but the + * requested resource cannot be found. It will be looked for an file called code and + * return it. + * + *

Default is false. + * + * @param fallBackSearching If you want to activate the file-fallback-search. + * @return This instance. + */ + public FileProviderOptions setFallBackSearching(boolean fallBackSearching) { + this.fallBackSearching = fallBackSearching; + return this; + } + + /** @return If the last modified date of the file on the OS will be send. */ + public boolean isLastModified() { + return lastModified; + } + + /** + * Set the Last-Modified header to the last modified date of the file on the OS. Default is true. + * + * @param lastModified If you want to send the last modified date of the file on the OS. + * @return This instance. + */ + public FileProviderOptions setLastModified(boolean lastModified) { + this.lastModified = lastModified; + return this; + } + + /** @return The current maxAge of of the Cache-Control header. */ + public long getMaxAge() { + return maxAge; + } + + /** + * Set the max-age property of the Cache-Control header in milliseconds or a string in ms format. + * + *

Default is 0. + * + * @param maxAge The new maxAge value. + * @return This instance. + */ + public FileProviderOptions setMaxAge(long maxAge) { + this.maxAge = maxAge; + return this; + } + + /** @return The current corresponding handler. */ + HttpRequestHandler getHandler() { + return handler; + } + + /** + * Add an alternate request-handler which will be fired before the file will be send. + * + * @param handler The HttpRequest handler. + * @return This instance. + */ + public FileProviderOptions setHandler(HttpRequestHandler handler) { + this.handler = handler; + return this; + } + + /** @return How Dot-files will be currently handled. */ + public DotFiles getDotFiles() { + return dotFiles; + } + + /** + * Set how ".foo" file will be handled. Ignore: Act if these files don't exists, response with 404 + * Deny: Deny the file, response with 303 and do nothing. Allow: No special treatment for this + * files. + * + * @param dotFiles The handling type. + */ + public void setDotFiles(DotFiles dotFiles) { + this.dotFiles = dotFiles; + } } diff --git a/src/main/java/express/middleware/Middleware.java b/src/main/java/express/middleware/Middleware.java index 60633fa..776c251 100644 --- a/src/main/java/express/middleware/Middleware.java +++ b/src/main/java/express/middleware/Middleware.java @@ -3,67 +3,70 @@ import java.io.IOException; /** + * Class which provides middleware. + * * @author Simon Reinisch - * Class which provides middleware */ public final class Middleware { - // Don't allow instantiating this class - private Middleware() {} + // Don't allow instantiating this class + private Middleware() {} - /** - * Create an new cookie-session middleware. - * You can access and edit to session-cookie data via request.getMiddlewareContent('SessionCookie'). - * - * @param cookieName An name for the session-cookie, it's recommend to use NOT SID for security reasons - * @param maxAge An max-age for the cookie - * @return New CookieSession - */ - public static CookieSession cookieSession(String cookieName, long maxAge) { - return new CookieSession(cookieName, maxAge); - } + /** + * Create an new cookie-session middleware. You can access and edit to session-cookie data via + * request.getMiddlewareContent('SessionCookie'). + * + * @param cookieName An name for the session-cookie, it's recommend to use NOT SID for security + * reasons + * @param maxAge An max-age for the cookie + * @return New CookieSession + */ + public static CookieSession cookieSession(String cookieName, long maxAge) { + return new CookieSession(cookieName, maxAge); + } - /** - * This class serves an entire folder which can contains static file for your - * web application, You can use StaticOptions to add som configurations. - * - * @param directoryPath The root directory - * @return A fresh FileProvider - * @throws IOException If path cannot be found or something like that - */ - public static FileProvider statics(String directoryPath) throws IOException { - return new FileProvider(directoryPath, new FileProviderOptions()); - } + /** + * This class serves an entire folder which can contains static file for your web application, You + * can use StaticOptions to add som configurations. + * + * @param directoryPath The root directory + * @return A fresh FileProvider + * @throws IOException If path cannot be found or something like that + */ + public static FileProvider statics(String directoryPath) throws IOException { + return new FileProvider(directoryPath, new FileProviderOptions()); + } - /** - * This class serves an entire folder which can contains static file for your - * web application, You can use StaticOptions to add som configurations. - * - * @param directoryPath The root directory - * @param staticOptions Optional options for the file serving. - * @return A fresh FileProvider - * @throws IOException If path cannot be found or something like that - */ - public static FileProvider statics(String directoryPath, FileProviderOptions staticOptions) throws IOException { - return new FileProvider(directoryPath, staticOptions); - } + /** + * This class serves an entire folder which can contains static file for your web application, You + * can use StaticOptions to add som configurations. + * + * @param directoryPath The root directory + * @param staticOptions Optional options for the file serving. + * @return A fresh FileProvider + * @throws IOException If path cannot be found or something like that + */ + public static FileProvider statics(String directoryPath, FileProviderOptions staticOptions) + throws IOException { + return new FileProvider(directoryPath, staticOptions); + } - /** - * CORS Middleware - * - * @param options Cors options - * @return - */ - public static Cors cors(CorsOptions options) { - return new Cors(options); - } + /** + * CORS Middleware + * + * @param options Cors options + * @return + */ + public static Cors cors(CorsOptions options) { + return new Cors(options); + } - /** - * CORS Middleware with default settings - * - * @return - */ - public static Cors cors() { - return new Cors(new CorsOptions()); - } + /** + * CORS Middleware with default settings + * + * @return + */ + public static Cors cors() { + return new Cors(new CorsOptions()); + } } diff --git a/src/main/java/express/utils/MediaType.java b/src/main/java/express/utils/MediaType.java index 8227146..bc8ef7a 100644 --- a/src/main/java/express/utils/MediaType.java +++ b/src/main/java/express/utils/MediaType.java @@ -1,720 +1,720 @@ package express.utils; /** + * Enum with common MediaTypes. + * * @author Simon Reinisch - * Enum with all MediaTypes */ public enum MediaType { + _aw("aw", "application/applixware"), + _atom("atom", "application/atom+xml "), + _atomcat("atomcat", "application/atomcat+xml"), + _atomsvc("atomsvc", "application/atomsvc+xml"), + _ccxml("ccxml", "application/ccxml+xml"), + _cdmia("cdmia", "application/cdmi-capability"), + _cdmic("cdmic", "application/cdmi-container"), + _cdmid("cdmid", "application/cdmi-domain"), + _cdmio("cdmio", "application/cdmi-object"), + _cdmiq("cdmiq", "application/cdmi-queue"), + _cu("cu", "application/cu-seeme"), + _davmount("davmount", "application/davmount+xml"), + _dssc("dssc", "application/dssc+der"), + _xdssc("xdssc", "application/dssc+xml"), + _es("es", "application/ecmascript"), + _emma("emma", "application/emma+xml"), + _epub("epub", "application/epub+zip"), + _exi("exi", "application/exi"), + _pfr("pfr", "application/font-tdpfr"), + _stk("stk", "application/hyperstudio"), + _ipfix("ipfix", "application/ipfix"), + _jar("jar", "application/java-archive"), + _ser("ser", "application/java-serialized-object"), + _class("class", "application/java-vm"), + _js("js", "application/javascript"), + _json("json", "application/json"), + _hqx("hqx", "application/mac-binhex40"), + _cpt("cpt", "application/mac-compactpro"), + _mads("mads", "application/mads+xml"), + _mrc("mrc", "application/marc"), + _mrcx("mrcx", "application/marcxml+xml"), + _ma("ma", "application/mathematica"), + _mathml("mathml", "application/mathml+xml"), + _mbox("mbox", "application/mbox"), + _mscml("mscml", "application/mediaservercontrol+xml"), + _meta4("meta4", "application/metalink4+xml"), + _mets("mets", "application/mets+xml"), + _mods("mods", "application/mods+xml"), + _m21("m21", "application/mp21"), + _doc("doc", "application/msword"), + _mxf("mxf", "application/mxf"), + _bin("bin", "application/octet-stream"), + _oda("oda", "application/oda"), + _opf("opf", "application/oebps-package+xml"), + _ogx("ogx", "application/ogg"), + _onetoc("onetoc", "application/onenote"), + _xer("xer", "application/patch-ops-error+xml"), + _pdf("pdf", "application/pdf"), + _prf("prf", "application/pics-rules"), + _p10("p10", "application/pkcs10"), + _p7m("p7m", "application/pkcs7-mime"), + _p7s("p7s", "application/pkcs7-signature"), + _p8("p8", "application/pkcs8"), + _ac("ac", "application/pkix-attr-cert"), + _cer("cer", "application/pkix-cert"), + _crl("crl", "application/pkix-crl"), + _pkipath("pkipath", "application/pkix-pkipath"), + _pki("pki", "application/pkixcmp"), + _pls("pls", "application/pls+xml"), + _ai("ai", "application/postscript"), + _cww("cww", "application/prs.cww"), + _pskcxml("pskcxml", "application/pskc+xml"), + _rdf("rdf", "application/rdf+xml"), + _rif("rif", "application/reginfo+xml"), + _rnc("rnc", "application/relax-ng-compact-syntax"), + _rl("rl", "application/resource-lists+xml"), + _rld("rld", "application/resource-lists-diff+xml"), + _rs("rs", "application/rls-services+xml"), + _rsd("rsd", "application/rsd+xml"), + _rss("rss", "application/rss+xml"), + _rtf("rtf", "application/rtf"), + _sbml("sbml", "application/sbml+xml"), + _scq("scq", "application/scvp-cv-request"), + _scs("scs", "application/scvp-cv-response"), + _spq("spq", "application/scvp-vp-request"), + _spp("spp", "application/scvp-vp-response"), + _sdp("sdp", "application/sdp"), + _setpay("setpay", "application/set-payment-initiation"), + _setreg("setreg", "application/set-registration-initiation"), + _shf("shf", "application/shf+xml"), + _smi("smi", "application/smil+xml"), + _rq("rq", "application/sparql-query"), + _srx("srx", "application/sparql-results+xml"), + _gram("gram", "application/srgs"), + _grxml("grxml", "application/srgs+xml"), + _sru("sru", "application/sru+xml"), + _ssml("ssml", "application/ssml+xml"), + _tei("tei", "application/tei+xml"), + _tfi("tfi", "application/thraud+xml"), + _tsd("tsd", "application/timestamped-data"), + _plb("plb", "application/vnd.3gpp.pic-bw-large"), + _psb("psb", "application/vnd.3gpp.pic-bw-small"), + _pvb("pvb", "application/vnd.3gpp.pic-bw-var"), + _tcap("tcap", "application/vnd.3gpp2.tcap"), + _pwn("pwn", "application/vnd.3m.post-it-notes"), + _aso("aso", "application/vnd.accpac.simply.aso"), + _imp("imp", "application/vnd.accpac.simply.imp"), + _acu("acu", "application/vnd.acucobol"), + _atc("atc", "application/vnd.acucorp"), + _air("air", "application/vnd.adobe.air-application-installer-package+zip"), + _fxp("fxp", "application/vnd.adobe.fxp"), + _xdp("xdp", "application/vnd.adobe.xdp+xml"), + _xfdf("xfdf", "application/vnd.adobe.xfdf"), + _ahead("ahead", "application/vnd.ahead.space"), + _azf("azf", "application/vnd.airzip.filesecure.azf"), + _azs("azs", "application/vnd.airzip.filesecure.azs"), + _azw("azw", "application/vnd.amazon.ebook"), + _acc("acc", "application/vnd.americandynamics.acc"), + _ami("ami", "application/vnd.amiga.ami"), + _apk("apk", "application/vnd.android.package-archive"), + _cii("cii", "application/vnd.anser-web-certificate-issue-initiation"), + _fti("fti", "application/vnd.anser-web-funds-transfer-initiation"), + _atx("atx", "application/vnd.antix.game-component"), + _mpkg("mpkg", "application/vnd.apple.installer+xml"), + _m3u8("m3u8", "application/vnd.apple.mpegurl"), + _swi("swi", "application/vnd.aristanetworks.swi"), + _aep("aep", "application/vnd.audiograph"), + _mpm("mpm", "application/vnd.blueice.multipass"), + _bmi("bmi", "application/vnd.bmi"), + _rep("rep", "application/vnd.businessobjects"), + _cdxml("cdxml", "application/vnd.chemdraw+xml"), + _mmd("mmd", "application/vnd.chipnuts.karaoke-mmd"), + _cdy("cdy", "application/vnd.cinderella"), + _cla("cla", "application/vnd.claymore"), + _rp9("rp9", "application/vnd.cloanto.rp9"), + _c4g("c4g", "application/vnd.clonk.c4group"), + _c11amc("c11amc", "application/vnd.cluetrust.cartomobile-config"), + _c11amz("c11amz", "application/vnd.cluetrust.cartomobile-config-pkg"), + _csp("csp", "application/vnd.commonspace"), + _cdbcmsg("cdbcmsg", "application/vnd.contact.cmsg"), + _cmc("cmc", "application/vnd.cosmocaller"), + _clkx("clkx", "application/vnd.crick.clicker"), + _clkk("clkk", "application/vnd.crick.clicker.keyboard"), + _clkp("clkp", "application/vnd.crick.clicker.palette"), + _clkt("clkt", "application/vnd.crick.clicker.template"), + _clkw("clkw", "application/vnd.crick.clicker.wordbank"), + _wbs("wbs", "application/vnd.criticaltools.wbs+xml"), + _pml("pml", "application/vnd.ctc-posml"), + _ppd("ppd", "application/vnd.cups-ppd"), + _car("car", "application/vnd.curl.car"), + _pcurl("pcurl", "application/vnd.curl.pcurl"), + _rdz("rdz", "application/vnd.data-vision.rdz"), + _fe_launch("fe_launch", "application/vnd.denovo.fcselayout-link"), + _dna("dna", "application/vnd.dna"), + _mlp("mlp", "application/vnd.dolby.mlp"), + _dpg("dpg", "application/vnd.dpgraph"), + _dfac("dfac", "application/vnd.dreamfactory"), + _ait("ait", "application/vnd.dvb.ait"), + _svc("svc", "application/vnd.dvb.service"), + _geo("geo", "application/vnd.dynageo"), + _mag("mag", "application/vnd.ecowin.chart"), + _nml("nml", "application/vnd.enliven"), + _esf("esf", "application/vnd.epson.esf"), + _msf("msf", "application/vnd.epson.msf"), + _qam("qam", "application/vnd.epson.quickanime"), + _slt("slt", "application/vnd.epson.salt"), + _ssf("ssf", "application/vnd.epson.ssf"), + _es3("es3", "application/vnd.eszigno3+xml"), + _ez2("ez2", "application/vnd.ezpix-album"), + _ez3("ez3", "application/vnd.ezpix-package"), + _fdf("fdf", "application/vnd.fdf"), + _seed("seed", "application/vnd.fdsn.seed"), + _gph("gph", "application/vnd.flographit"), + _ftc("ftc", "application/vnd.fluxtime.clip"), + _fm("fm", "application/vnd.framemaker"), + _fnc("fnc", "application/vnd.frogans.fnc"), + _ltf("ltf", "application/vnd.frogans.ltf"), + _fsc("fsc", "application/vnd.fsc.weblaunch"), + _oas("oas", "application/vnd.fujitsu.oasys"), + _oa2("oa2", "application/vnd.fujitsu.oasys2"), + _oa3("oa3", "application/vnd.fujitsu.oasys3"), + _fg5("fg5", "application/vnd.fujitsu.oasysgp"), + _bh2("bh2", "application/vnd.fujitsu.oasysprs"), + _ddd("ddd", "application/vnd.fujixerox.ddd"), + _xdw("xdw", "application/vnd.fujixerox.docuworks"), + _xbd("xbd", "application/vnd.fujixerox.docuworks.binder"), + _fzs("fzs", "application/vnd.fuzzysheet"), + _txd("txd", "application/vnd.genomatix.tuxedo"), + _ggb("ggb", "application/vnd.geogebra.file"), + _ggt("ggt", "application/vnd.geogebra.tool"), + _gex("gex", "application/vnd.geometry-explorer"), + _gxt("gxt", "application/vnd.geonext"), + _g2w("g2w", "application/vnd.geoplan"), + _g3w("g3w", "application/vnd.geospace"), + _gmx("gmx", "application/vnd.gmx"), + _kml("kml", "application/vnd.google-earth.kml+xml"), + _kmz("kmz", "application/vnd.google-earth.kmz"), + _gqf("gqf", "application/vnd.grafeq"), + _gac("gac", "application/vnd.groove-account"), + _ghf("ghf", "application/vnd.groove-help"), + _gim("gim", "application/vnd.groove-identity-message"), + _grv("grv", "application/vnd.groove-injector"), + _gtm("gtm", "application/vnd.groove-tool-message"), + _tpl("tpl", "application/vnd.groove-tool-template"), + _vcg("vcg", "application/vnd.groove-vcard"), + _hal("hal", "application/vnd.hal+xml"), + _zmm("zmm", "application/vnd.handheld-entertainment+xml"), + _hbci("hbci", "application/vnd.hbci"), + _les("les", "application/vnd.hhe.lesson-player"), + _hpgl("hpgl", "application/vnd.hp-hpgl"), + _hpid("hpid", "application/vnd.hp-hpid"), + _hps("hps", "application/vnd.hp-hps"), + _jlt("jlt", "application/vnd.hp-jlyt"), + _pcl("pcl", "application/vnd.hp-pcl"), + _pclxl("pclxl", "application/vnd.hp-pclxl"), + _sfd_hdstx("sfd-hdstx", "application/vnd.hydrostatix.sof-data"), + _x3d("x3d", "application/vnd.hzn-3d-crossword"), + _mpy("mpy", "application/vnd.ibm.minipay"), + _afp("afp", "application/vnd.ibm.modcap"), + _irm("irm", "application/vnd.ibm.rights-management"), + _sc("sc", "application/vnd.ibm.secure-container"), + _icc("icc", "application/vnd.iccprofile"), + _igl("igl", "application/vnd.igloader"), + _ivp("ivp", "application/vnd.immervision-ivp"), + _ivu("ivu", "application/vnd.immervision-ivu"), + _igm("igm", "application/vnd.insors.igm"), + _xpw("xpw", "application/vnd.intercon.formnet"), + _i2g("i2g", "application/vnd.intergeo"), + _qbo("qbo", "application/vnd.intu.qbo"), + _qfx("qfx", "application/vnd.intu.qfx"), + _rcprofile("rcprofile", "application/vnd.ipunplugged.rcprofile"), + _irp("irp", "application/vnd.irepository.package+xml"), + _xpr("xpr", "application/vnd.is-xpr"), + _fcs("fcs", "application/vnd.isac.fcs"), + _jam("jam", "application/vnd.jam"), + _rms("rms", "application/vnd.jcp.javame.midlet-rms"), + _jisp("jisp", "application/vnd.jisp"), + _joda("joda", "application/vnd.joost.joda-archive"), + _ktz("ktz", "application/vnd.kahootz"), + _karbon("karbon", "application/vnd.kde.karbon"), + _chrt("chrt", "application/vnd.kde.kchart"), + _kfo("kfo", "application/vnd.kde.kformula"), + _flw("flw", "application/vnd.kde.kivio"), + _kon("kon", "application/vnd.kde.kontour"), + _kpr("kpr", "application/vnd.kde.kpresenter"), + _ksp("ksp", "application/vnd.kde.kspread"), + _kwd("kwd", "application/vnd.kde.kword"), + _htke("htke", "application/vnd.kenameaapp"), + _kia("kia", "application/vnd.kidspiration"), + _kne("kne", "application/vnd.kinar"), + _skp("skp", "application/vnd.koan"), + _sse("sse", "application/vnd.kodak-descriptor"), + _lasxml("lasxml", "application/vnd.las.las+xml"), + _lbd("lbd", "application/vnd.llamagraphics.life-balance.desktop"), + _lbe("lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"), + _123("123", "application/vnd.lotus-1-2-3"), + _apr("apr", "application/vnd.lotus-approach"), + _pre("pre", "application/vnd.lotus-freelance"), + _nsf("nsf", "application/vnd.lotus-notes"), + _org("org", "application/vnd.lotus-organizer"), + _scm("scm", "application/vnd.lotus-screencam"), + _lwp("lwp", "application/vnd.lotus-wordpro"), + _portpkg("portpkg", "application/vnd.macports.portpkg"), + _mcd("mcd", "application/vnd.mcd"), + _mc1("mc1", "application/vnd.medcalcdata"), + _cdkey("cdkey", "application/vnd.mediastation.cdkey"), + _mwf("mwf", "application/vnd.mfer"), + _mfm("mfm", "application/vnd.mfmp"), + _flo("flo", "application/vnd.micrografx.flo"), + _igx("igx", "application/vnd.micrografx.igx"), + _mif("mif", "application/vnd.mif"), + _daf("daf", "application/vnd.mobius.daf"), + _dis("dis", "application/vnd.mobius.dis"), + _mbk("mbk", "application/vnd.mobius.mbk"), + _mqy("mqy", "application/vnd.mobius.mqy"), + _msl("msl", "application/vnd.mobius.msl"), + _plc("plc", "application/vnd.mobius.plc"), + _txf("txf", "application/vnd.mobius.txf"), + _mpn("mpn", "application/vnd.mophun.application"), + _mpc("mpc", "application/vnd.mophun.certificate"), + _xul("xul", "application/vnd.mozilla.xul+xml"), + _cil("cil", "application/vnd.ms-artgalry"), + _cab("cab", "application/vnd.ms-cab-compressed"), + _xls("xls", "application/vnd.ms-excel"), + _xlam("xlam", "application/vnd.ms-excel.addin.macroenabled.12"), + _xlsb("xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"), + _xlsm("xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"), + _xltm("xltm", "application/vnd.ms-excel.template.macroenabled.12"), + _eot("eot", "application/vnd.ms-fontobject"), + _chm("chm", "application/vnd.ms-htmlhelp"), + _ims("ims", "application/vnd.ms-ims"), + _lrm("lrm", "application/vnd.ms-lrm"), + _thmx("thmx", "application/vnd.ms-officetheme"), + _cat("cat", "application/vnd.ms-pki.seccat"), + _stl("stl", "application/vnd.ms-pki.stl"), + _ppt("ppt", "application/vnd.ms-powerpoint"), + _ppam("ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"), + _pptm("pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"), + _sldm("sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"), + _ppsm("ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"), + _potm("potm", "application/vnd.ms-powerpoint.template.macroenabled.12"), + _mpp("mpp", "application/vnd.ms-project"), + _docm("docm", "application/vnd.ms-word.document.macroenabled.12"), + _dotm("dotm", "application/vnd.ms-word.template.macroenabled.12"), + _wps("wps", "application/vnd.ms-works"), + _wpl("wpl", "application/vnd.ms-wpl"), + _xps("xps", "application/vnd.ms-xpsdocument"), + _mseq("mseq", "application/vnd.mseq"), + _mus("mus", "application/vnd.musician"), + _msty("msty", "application/vnd.muvee.style"), + _nlu("nlu", "application/vnd.neurolanguage.nlu"), + _nnd("nnd", "application/vnd.noblenet-directory"), + _nns("nns", "application/vnd.noblenet-sealer"), + _nnw("nnw", "application/vnd.noblenet-web"), + _ngdat("ngdat", "application/vnd.nokia.n-gage.data"), + _n_gage("n-gage", "application/vnd.nokia.n-gage.symbian.install"), + _rpst("rpst", "application/vnd.nokia.radio-preset"), + _rpss("rpss", "application/vnd.nokia.radio-presets"), + _edm("edm", "application/vnd.novadigm.edm"), + _edx("edx", "application/vnd.novadigm.edx"), + _ext("ext", "application/vnd.novadigm.ext"), + _odc("odc", "application/vnd.oasis.opendocument.chart"), + _otc("otc", "application/vnd.oasis.opendocument.chart-template"), + _odb("odb", "application/vnd.oasis.opendocument.database"), + _odf("odf", "application/vnd.oasis.opendocument.formula"), + _odft("odft", "application/vnd.oasis.opendocument.formula-template"), + _odg("odg", "application/vnd.oasis.opendocument.graphics"), + _otg("otg", "application/vnd.oasis.opendocument.graphics-template"), + _odi("odi", "application/vnd.oasis.opendocument.image"), + _oti("oti", "application/vnd.oasis.opendocument.image-template"), + _odp("odp", "application/vnd.oasis.opendocument.presentation"), + _otp("otp", "application/vnd.oasis.opendocument.presentation-template"), + _ods("ods", "application/vnd.oasis.opendocument.spreadsheet"), + _ots("ots", "application/vnd.oasis.opendocument.spreadsheet-template"), + _odt("odt", "application/vnd.oasis.opendocument.text"), + _odm("odm", "application/vnd.oasis.opendocument.text-master"), + _ott("ott", "application/vnd.oasis.opendocument.text-template"), + _oth("oth", "application/vnd.oasis.opendocument.text-web"), + _xo("xo", "application/vnd.olpc-sugar"), + _dd2("dd2", "application/vnd.oma.dd2+xml"), + _oxt("oxt", "application/vnd.openofficeorg.extension"), + _pptx("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"), + _sldx("sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"), + _ppsx("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"), + _potx("potx", "application/vnd.openxmlformats-officedocument.presentationml.template"), + _xlsx("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), + _xltx("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"), + _docx("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"), + _dotx("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"), + _mgp("mgp", "application/vnd.osgeo.mapguide.package"), + _dp("dp", "application/vnd.osgi.dp"), + _pdb("pdb", "application/vnd.palm"), + _paw("paw", "application/vnd.pawaafile"), + _str("str", "application/vnd.pg.format"), + _ei6("ei6", "application/vnd.pg.osasli"), + _efif("efif", "application/vnd.picsel"), + _wg("wg", "application/vnd.pmi.widget"), + _plf("plf", "application/vnd.pocketlearn"), + _pbd("pbd", "application/vnd.powerbuilder6"), + _box("box", "application/vnd.previewsystems.box"), + _mgz("mgz", "application/vnd.proteus.magazine"), + _qps("qps", "application/vnd.publishare-delta-tree"), + _ptid("ptid", "application/vnd.pvi.ptid1"), + _qxd("qxd", "application/vnd.quark.quarkxpress"), + _bed("bed", "application/vnd.realvnc.bed"), + _mxl("mxl", "application/vnd.recordare.musicxml"), + _musicxml("musicxml", "application/vnd.recordare.musicxml+xml"), + _cryptonote("cryptonote", "application/vnd.rig.cryptonote"), + _cod("cod", "application/vnd.rim.cod"), + _rm("rm", "application/vnd.rn-realmedia"), + _link66("link66", "application/vnd.route66.link66+xml"), + _st("st", "application/vnd.sailingtracker.track"), + _see("see", "application/vnd.seemail"), + _sema("sema", "application/vnd.sema"), + _semd("semd", "application/vnd.semd"), + _semf("semf", "application/vnd.semf"), + _ifm("ifm", "application/vnd.shana.informed.formdata"), + _itp("itp", "application/vnd.shana.informed.formtemplate"), + _iif("iif", "application/vnd.shana.informed.interchange"), + _ipk("ipk", "application/vnd.shana.informed.package"), + _twd("twd", "application/vnd.simtech-mindmapper"), + _mmf("mmf", "application/vnd.smaf"), + _teacher("teacher", "application/vnd.smart.teacher"), + _sdkm("sdkm", "application/vnd.solent.sdkm+xml"), + _dxp("dxp", "application/vnd.spotfire.dxp"), + _sfs("sfs", "application/vnd.spotfire.sfs"), + _sdc("sdc", "application/vnd.stardivision.calc"), + _sda("sda", "application/vnd.stardivision.draw"), + _sdd("sdd", "application/vnd.stardivision.impress"), + _smf("smf", "application/vnd.stardivision.math"), + _sdw("sdw", "application/vnd.stardivision.writer"), + _sgl("sgl", "application/vnd.stardivision.writer-global"), + _sm("sm", "application/vnd.stepmania.stepchart"), + _sxc("sxc", "application/vnd.sun.xml.calc"), + _stc("stc", "application/vnd.sun.xml.calc.template"), + _sxd("sxd", "application/vnd.sun.xml.draw"), + _std("std", "application/vnd.sun.xml.draw.template"), + _sxi("sxi", "application/vnd.sun.xml.impress"), + _sti("sti", "application/vnd.sun.xml.impress.template"), + _sxm("sxm", "application/vnd.sun.xml.math"), + _sxw("sxw", "application/vnd.sun.xml.writer"), + _sxg("sxg", "application/vnd.sun.xml.writer.global"), + _stw("stw", "application/vnd.sun.xml.writer.template"), + _sus("sus", "application/vnd.sus-calendar"), + _svd("svd", "application/vnd.svd"), + _sis("sis", "application/vnd.symbian.install"), + _xsm("xsm", "application/vnd.syncml+xml"), + _bdm("bdm", "application/vnd.syncml.dm+wbxml"), + _xdm("xdm", "application/vnd.syncml.dm+xml"), + _tao("tao", "application/vnd.tao.intent-module-archive"), + _tmo("tmo", "application/vnd.tmobile-livetv"), + _tpt("tpt", "application/vnd.trid.tpt"), + _mxs("mxs", "application/vnd.triscape.mxs"), + _tra("tra", "application/vnd.trueapp"), + _ufd("ufd", "application/vnd.ufdl"), + _utz("utz", "application/vnd.uiq.theme"), + _umj("umj", "application/vnd.umajin"), + _unityweb("unityweb", "application/vnd.unity"), + _uoml("uoml", "application/vnd.uoml+xml"), + _vcx("vcx", "application/vnd.vcx"), + _vsd("vsd", "application/vnd.visio"), + _vsdx("vsdx", "application/vnd.visio2013"), + _vis("vis", "application/vnd.visionary"), + _vsf("vsf", "application/vnd.vsf"), + _wbxml("wbxml", "application/vnd.wap.wbxml"), + _wmlc("wmlc", "application/vnd.wap.wmlc"), + _wmlsc("wmlsc", "application/vnd.wap.wmlscriptc"), + _wtb("wtb", "application/vnd.webturbo"), + _nbp("nbp", "application/vnd.wolfram.player"), + _wpd("wpd", "application/vnd.wordperfect"), + _wqd("wqd", "application/vnd.wqd"), + _stf("stf", "application/vnd.wt.stf"), + _xar("xar", "application/vnd.xara"), + _xfdl("xfdl", "application/vnd.xfdl"), + _hvd("hvd", "application/vnd.yamaha.hv-dic"), + _hvs("hvs", "application/vnd.yamaha.hv-script"), + _hvp("hvp", "application/vnd.yamaha.hv-voice"), + _osf("osf", "application/vnd.yamaha.openscoreformat"), + _osfpvg("osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"), + _saf("saf", "application/vnd.yamaha.smaf-audio"), + _spf("spf", "application/vnd.yamaha.smaf-phrase"), + _cmp("cmp", "application/vnd.yellowriver-custom-menu"), + _zir("zir", "application/vnd.zul"), + _zaz("zaz", "application/vnd.zzazz.deck+xml"), + _vxml("vxml", "application/voicexml+xml"), + _wgt("wgt", "application/widget"), + _hlp("hlp", "application/winhlp"), + _wsdl("wsdl", "application/wsdl+xml"), + _wspolicy("wspolicy", "application/wspolicy+xml"), + _7z("7z", "application/x-7z-compressed"), + _abw("abw", "application/x-abiword"), + _ace("ace", "application/x-ace-compressed"), + _aab("aab", "application/x-authorware-bin"), + _aam("aam", "application/x-authorware-map"), + _aas("aas", "application/x-authorware-seg"), + _bcpio("bcpio", "application/x-bcpio"), + _torrent("torrent", "application/x-bittorrent"), + _bz("bz", "application/x-bzip"), + _bz2("bz2", "application/x-bzip2"), + _vcd("vcd", "application/x-cdlink"), + _chat("chat", "application/x-chat"), + _pgn("pgn", "application/x-chess-pgn"), + _cpio("cpio", "application/x-cpio"), + _csh("csh", "application/x-csh"), + _deb("deb", "application/x-debian-package"), + _dir("dir", "application/x-director"), + _wad("wad", "application/x-doom"), + _ncx("ncx", "application/x-dtbncx+xml"), + _dtb("dtb", "application/x-dtbook+xml"), + _res("res", "application/x-dtbresource+xml"), + _dvi("dvi", "application/x-dvi"), + _bdf("bdf", "application/x-font-bdf"), + _gsf("gsf", "application/x-font-ghostscript"), + _psf("psf", "application/x-font-linux-psf"), + _otf("otf", "application/x-font-otf"), + _pcf("pcf", "application/x-font-pcf"), + _snf("snf", "application/x-font-snf"), + _ttf("ttf", "application/x-font-ttf"), + _pfa("pfa", "application/x-font-type1"), + _woff("woff", "application/x-font-woff"), + _spl("spl", "application/x-futuresplash"), + _gnumeric("gnumeric", "application/x-gnumeric"), + _gtar("gtar", "application/x-gtar"), + _hdf("hdf", "application/x-hdf"), + _jnlp("jnlp", "application/x-java-jnlp-file"), + _latex("latex", "application/x-latex"), + _prc("prc", "application/x-mobipocket-ebook"), + _application("application", "application/x-ms-application"), + _wmd("wmd", "application/x-ms-wmd"), + _wmz("wmz", "application/x-ms-wmz"), + _xbap("xbap", "application/x-ms-xbap"), + _mdb("mdb", "application/x-msaccess"), + _obd("obd", "application/x-msbinder"), + _crd("crd", "application/x-mscardfile"), + _clp("clp", "application/x-msclip"), + _exe("exe", "application/x-msdownload"), + _mvb("mvb", "application/x-msmediaview"), + _wmf("wmf", "application/x-msmetafile"), + _mny("mny", "application/x-msmoney"), + _pub("pub", "application/x-mspublisher"), + _scd("scd", "application/x-msschedule"), + _trm("trm", "application/x-msterminal"), + _wri("wri", "application/x-mswrite"), + _nc("nc", "application/x-netcdf"), + _p12("p12", "application/x-pkcs12"), + _p7b("p7b", "application/x-pkcs7-certificates"), + _p7r("p7r", "application/x-pkcs7-certreqresp"), + _rar("rar", "application/x-rar-compressed"), + _sh("sh", "application/x-sh"), + _shar("shar", "application/x-shar"), + _swf("swf", "application/x-shockwave-flash"), + _xap("xap", "application/x-silverlight-app"), + _sit("sit", "application/x-stuffit"), + _sitx("sitx", "application/x-stuffitx"), + _sv4cpio("sv4cpio", "application/x-sv4cpio"), + _sv4crc("sv4crc", "application/x-sv4crc"), + _tar("tar", "application/x-tar"), + _tcl("tcl", "application/x-tcl"), + _tex("tex", "application/x-tex"), + _tfm("tfm", "application/x-tex-tfm"), + _texinfo("texinfo", "application/x-texinfo"), + _ustar("ustar", "application/x-ustar"), + _src("src", "application/x-wais-source"), + _der("der", "application/x-x509-ca-cert"), + _fig("fig", "application/x-xfig"), + _xpi("xpi", "application/x-xpinstall"), + _xdf("xdf", "application/xcap-diff+xml"), + _xenc("xenc", "application/xenc+xml"), + _xhtml("xhtml", "application/xhtml+xml"), + _xml("xml", "application/xml"), + _dtd("dtd", "application/xml-dtd"), + _xop("xop", "application/xop+xml"), + _xslt("xslt", "application/xslt+xml"), + _xspf("xspf", "application/xspf+xml"), + _mxml("mxml", "application/xv+xml"), + _yang("yang", "application/yang"), + _yin("yin", "application/yin+xml"), + _zip("zip", "application/zip"), + _adp("adp", "audio/adpcm"), + _au("au", "audio/basic"), + _mid("mid", "audio/midi"), + _mp4a("mp4a", "audio/mp4"), + _mpga("mpga", "audio/mpeg"), + _oga("oga", "audio/ogg"), + _uva("uva", "audio/vnd.dece.audio"), + _eol("eol", "audio/vnd.digital-winds"), + _dra("dra", "audio/vnd.dra"), + _dts("dts", "audio/vnd.dts"), + _dtshd("dtshd", "audio/vnd.dts.hd"), + _lvp("lvp", "audio/vnd.lucent.voice"), + _pya("pya", "audio/vnd.ms-playready.media.pya"), + _ecelp4800("ecelp4800", "audio/vnd.nuera.ecelp4800"), + _ecelp7470("ecelp7470", "audio/vnd.nuera.ecelp7470"), + _ecelp9600("ecelp9600", "audio/vnd.nuera.ecelp9600"), + _rip("rip", "audio/vnd.rip"), + _weba("weba", "audio/webm"), + _aac("aac", "audio/x-aac"), + _aif("aif", "audio/x-aiff"), + _m3u("m3u", "audio/x-mpegurl"), + _wax("wax", "audio/x-ms-wax"), + _wma("wma", "audio/x-ms-wma"), + _ram("ram", "audio/x-pn-realaudio"), + _rmp("rmp", "audio/x-pn-realaudio-plugin"), + _wav("wav", "audio/x-wav"), + _cdx("cdx", "chemical/x-cdx"), + _cif("cif", "chemical/x-cif"), + _cmdf("cmdf", "chemical/x-cmdf"), + _cml("cml", "chemical/x-cml"), + _csml("csml", "chemical/x-csml"), + _xyz("xyz", "chemical/x-xyz"), + _bmp("bmp", "image/bmp"), + _cgm("cgm", "image/cgm"), + _g3("g3", "image/g3fax"), + _gif("gif", "image/gif"), + _ief("ief", "image/ief"), + _jpeg("jpeg", "image/jpeg"), + _jpg("jpg", "image/jpeg"), + _pjpeg("pjpeg", "image/pjpeg"), + _ktx("ktx", "image/ktx"), + _png("png", "image/x-citrix-png"), + _btif("btif", "image/prs.btif"), + _svg("svg", "image/svg+xml"), + _tiff("tiff", "image/tiff"), + _psd("psd", "image/vnd.adobe.photoshop"), + _uvi("uvi", "image/vnd.dece.graphic"), + _sub("sub", "image/vnd.dvb.subtitle"), + _djvu("djvu", "image/vnd.djvu"), + _dwg("dwg", "image/vnd.dwg"), + _dxf("dxf", "image/vnd.dxf"), + _fbs("fbs", "image/vnd.fastbidsheet"), + _fpx("fpx", "image/vnd.fpx"), + _fst("fst", "image/vnd.fst"), + _mmr("mmr", "image/vnd.fujixerox.edmics-mmr"), + _rlc("rlc", "image/vnd.fujixerox.edmics-rlc"), + _mdi("mdi", "image/vnd.ms-modi"), + _npx("npx", "image/vnd.net-fpx"), + _wbmp("wbmp", "image/vnd.wap.wbmp"), + _xif("xif", "image/vnd.xiff"), + _webp("webp", "image/webp"), + _ras("ras", "image/x-cmu-raster"), + _cmx("cmx", "image/x-cmx"), + _fh("fh", "image/x-freehand"), + _ico("ico", "image/x-icon"), + _pcx("pcx", "image/x-pcx"), + _pic("pic", "image/x-pict"), + _pnm("pnm", "image/x-portable-anymap"), + _pbm("pbm", "image/x-portable-bitmap"), + _pgm("pgm", "image/x-portable-graymap"), + _ppm("ppm", "image/x-portable-pixmap"), + _rgb("rgb", "image/x-rgb"), + _xbm("xbm", "image/x-xbitmap"), + _xpm("xpm", "image/x-xpixmap"), + _xwd("xwd", "image/x-xwindowdump"), + _eml("eml", "message/rfc822"), + _igs("igs", "model/iges"), + _msh("msh", "model/mesh"), + _dae("dae", "model/vnd.collada+xml"), + _dwf("dwf", "model/vnd.dwf"), + _gdl("gdl", "model/vnd.gdl"), + _gtw("gtw", "model/vnd.gtw"), + _mts("mts", "model/vnd.mts"), + _vtu("vtu", "model/vnd.vtu"), + _wrl("wrl", "model/vrml"), + _ics("ics", "text/calendar"), + _css("css", "text/css"), + _csv("csv", "text/csv"), + _html("html", "text/html"), + _n3("n3", "text/n3"), + _txt("txt", "text/plain"), + _dsc("dsc", "text/prs.lines.tag"), + _rtx("rtx", "text/richtext"), + _sgml("sgml", "text/sgml"), + _tsv("tsv", "text/tab-separated-values"), + _t("t", "text/troff"), + _ttl("ttl", "text/turtle"), + _uri("uri", "text/uri-list"), + _curl("curl", "text/vnd.curl"), + _dcurl("dcurl", "text/vnd.curl.dcurl"), + _scurl("scurl", "text/vnd.curl.scurl"), + _mcurl("mcurl", "text/vnd.curl.mcurl"), + _fly("fly", "text/vnd.fly"), + _flx("flx", "text/vnd.fmi.flexstor"), + _gv("gv", "text/vnd.graphviz"), + _3dml("3dml", "text/vnd.in3d.3dml"), + _spot("spot", "text/vnd.in3d.spot"), + _jad("jad", "text/vnd.sun.j2me.app-descriptor"), + _wml("wml", "text/vnd.wap.wml"), + _wmls("wmls", "text/vnd.wap.wmlscript"), + _s("s", "text/x-asm"), + _c("c", "text/x-c"), + _f("f", "text/x-fortran"), + _p("p", "text/x-pascal"), + _java("java", "text/x-java-source"), + _etx("etx", "text/x-setext"), + _uu("uu", "text/x-uuencode"), + _vcs("vcs", "text/x-vcalendar"), + _vcf("vcf", "text/x-vcard"), + _3gp("3gp", "video/3gpp"), + _3g2("3g2", "video/3gpp2"), + _h261("h261", "video/h261"), + _h263("h263", "video/h263"), + _h264("h264", "video/h264"), + _jpgv("jpgv", "video/jpeg"), + _jpm("jpm", "video/jpm"), + _mj2("mj2", "video/mj2"), + _mp4("mp4", "video/mp4"), + _mpeg("mpeg", "video/mpeg"), + _ogv("ogv", "video/ogg"), + _qt("qt", "video/quicktime"), + _uvh("uvh", "video/vnd.dece.hd"), + _uvm("uvm", "video/vnd.dece.mobile"), + _uvp("uvp", "video/vnd.dece.pd"), + _uvs("uvs", "video/vnd.dece.sd"), + _uvv("uvv", "video/vnd.dece.video"), + _fvt("fvt", "video/vnd.fvt"), + _mxu("mxu", "video/vnd.mpegurl"), + _pyv("pyv", "video/vnd.ms-playready.media.pyv"), + _uvu("uvu", "video/vnd.uvvu.mp4"), + _viv("viv", "video/vnd.vivo"), + _webm("webm", "video/webm"), + _f4v("f4v", "video/x-f4v"), + _fli("fli", "video/x-fli"), + _flv("flv", "video/x-flv"), + _m4v("m4v", "video/x-m4v"), + _asf("asf", "video/x-ms-asf"), + _wm("wm", "video/x-ms-wm"), + _wmv("wmv", "video/x-ms-wmv"), + _wmx("wmx", "video/x-ms-wmx"), + _wvx("wvx", "video/x-ms-wvx"), + _avi("avi", "video/x-msvideo"), + _movie("movie", "video/x-sgi-movie"), + _ice("ice", "x-conference/x-cooltalk"), + _par("par", "text/plain-bas"), + _yaml("yaml", "text/yaml"), + _dmg("dmg", "application/x-apple-diskimage"), + _xww("form", "application/x-www-form-urlencoded"); - _aw("aw", "application/applixware"), - _atom("atom", "application/atom+xml "), - _atomcat("atomcat", "application/atomcat+xml"), - _atomsvc("atomsvc", "application/atomsvc+xml"), - _ccxml("ccxml", "application/ccxml+xml"), - _cdmia("cdmia", "application/cdmi-capability"), - _cdmic("cdmic", "application/cdmi-container"), - _cdmid("cdmid", "application/cdmi-domain"), - _cdmio("cdmio", "application/cdmi-object"), - _cdmiq("cdmiq", "application/cdmi-queue"), - _cu("cu", "application/cu-seeme"), - _davmount("davmount", "application/davmount+xml"), - _dssc("dssc", "application/dssc+der"), - _xdssc("xdssc", "application/dssc+xml"), - _es("es", "application/ecmascript"), - _emma("emma", "application/emma+xml"), - _epub("epub", "application/epub+zip"), - _exi("exi", "application/exi"), - _pfr("pfr", "application/font-tdpfr"), - _stk("stk", "application/hyperstudio"), - _ipfix("ipfix", "application/ipfix"), - _jar("jar", "application/java-archive"), - _ser("ser", "application/java-serialized-object"), - _class("class", "application/java-vm"), - _js("js", "application/javascript"), - _json("json", "application/json"), - _hqx("hqx", "application/mac-binhex40"), - _cpt("cpt", "application/mac-compactpro"), - _mads("mads", "application/mads+xml"), - _mrc("mrc", "application/marc"), - _mrcx("mrcx", "application/marcxml+xml"), - _ma("ma", "application/mathematica"), - _mathml("mathml", "application/mathml+xml"), - _mbox("mbox", "application/mbox"), - _mscml("mscml", "application/mediaservercontrol+xml"), - _meta4("meta4", "application/metalink4+xml"), - _mets("mets", "application/mets+xml"), - _mods("mods", "application/mods+xml"), - _m21("m21", "application/mp21"), - _doc("doc", "application/msword"), - _mxf("mxf", "application/mxf"), - _bin("bin", "application/octet-stream"), - _oda("oda", "application/oda"), - _opf("opf", "application/oebps-package+xml"), - _ogx("ogx", "application/ogg"), - _onetoc("onetoc", "application/onenote"), - _xer("xer", "application/patch-ops-error+xml"), - _pdf("pdf", "application/pdf"), - _prf("prf", "application/pics-rules"), - _p10("p10", "application/pkcs10"), - _p7m("p7m", "application/pkcs7-mime"), - _p7s("p7s", "application/pkcs7-signature"), - _p8("p8", "application/pkcs8"), - _ac("ac", "application/pkix-attr-cert"), - _cer("cer", "application/pkix-cert"), - _crl("crl", "application/pkix-crl"), - _pkipath("pkipath", "application/pkix-pkipath"), - _pki("pki", "application/pkixcmp"), - _pls("pls", "application/pls+xml"), - _ai("ai", "application/postscript"), - _cww("cww", "application/prs.cww"), - _pskcxml("pskcxml", "application/pskc+xml"), - _rdf("rdf", "application/rdf+xml"), - _rif("rif", "application/reginfo+xml"), - _rnc("rnc", "application/relax-ng-compact-syntax"), - _rl("rl", "application/resource-lists+xml"), - _rld("rld", "application/resource-lists-diff+xml"), - _rs("rs", "application/rls-services+xml"), - _rsd("rsd", "application/rsd+xml"), - _rss("rss", "application/rss+xml"), - _rtf("rtf", "application/rtf"), - _sbml("sbml", "application/sbml+xml"), - _scq("scq", "application/scvp-cv-request"), - _scs("scs", "application/scvp-cv-response"), - _spq("spq", "application/scvp-vp-request"), - _spp("spp", "application/scvp-vp-response"), - _sdp("sdp", "application/sdp"), - _setpay("setpay", "application/set-payment-initiation"), - _setreg("setreg", "application/set-registration-initiation"), - _shf("shf", "application/shf+xml"), - _smi("smi", "application/smil+xml"), - _rq("rq", "application/sparql-query"), - _srx("srx", "application/sparql-results+xml"), - _gram("gram", "application/srgs"), - _grxml("grxml", "application/srgs+xml"), - _sru("sru", "application/sru+xml"), - _ssml("ssml", "application/ssml+xml"), - _tei("tei", "application/tei+xml"), - _tfi("tfi", "application/thraud+xml"), - _tsd("tsd", "application/timestamped-data"), - _plb("plb", "application/vnd.3gpp.pic-bw-large"), - _psb("psb", "application/vnd.3gpp.pic-bw-small"), - _pvb("pvb", "application/vnd.3gpp.pic-bw-var"), - _tcap("tcap", "application/vnd.3gpp2.tcap"), - _pwn("pwn", "application/vnd.3m.post-it-notes"), - _aso("aso", "application/vnd.accpac.simply.aso"), - _imp("imp", "application/vnd.accpac.simply.imp"), - _acu("acu", "application/vnd.acucobol"), - _atc("atc", "application/vnd.acucorp"), - _air("air", "application/vnd.adobe.air-application-installer-package+zip"), - _fxp("fxp", "application/vnd.adobe.fxp"), - _xdp("xdp", "application/vnd.adobe.xdp+xml"), - _xfdf("xfdf", "application/vnd.adobe.xfdf"), - _ahead("ahead", "application/vnd.ahead.space"), - _azf("azf", "application/vnd.airzip.filesecure.azf"), - _azs("azs", "application/vnd.airzip.filesecure.azs"), - _azw("azw", "application/vnd.amazon.ebook"), - _acc("acc", "application/vnd.americandynamics.acc"), - _ami("ami", "application/vnd.amiga.ami"), - _apk("apk", "application/vnd.android.package-archive"), - _cii("cii", "application/vnd.anser-web-certificate-issue-initiation"), - _fti("fti", "application/vnd.anser-web-funds-transfer-initiation"), - _atx("atx", "application/vnd.antix.game-component"), - _mpkg("mpkg", "application/vnd.apple.installer+xml"), - _m3u8("m3u8", "application/vnd.apple.mpegurl"), - _swi("swi", "application/vnd.aristanetworks.swi"), - _aep("aep", "application/vnd.audiograph"), - _mpm("mpm", "application/vnd.blueice.multipass"), - _bmi("bmi", "application/vnd.bmi"), - _rep("rep", "application/vnd.businessobjects"), - _cdxml("cdxml", "application/vnd.chemdraw+xml"), - _mmd("mmd", "application/vnd.chipnuts.karaoke-mmd"), - _cdy("cdy", "application/vnd.cinderella"), - _cla("cla", "application/vnd.claymore"), - _rp9("rp9", "application/vnd.cloanto.rp9"), - _c4g("c4g", "application/vnd.clonk.c4group"), - _c11amc("c11amc", "application/vnd.cluetrust.cartomobile-config"), - _c11amz("c11amz", "application/vnd.cluetrust.cartomobile-config-pkg"), - _csp("csp", "application/vnd.commonspace"), - _cdbcmsg("cdbcmsg", "application/vnd.contact.cmsg"), - _cmc("cmc", "application/vnd.cosmocaller"), - _clkx("clkx", "application/vnd.crick.clicker"), - _clkk("clkk", "application/vnd.crick.clicker.keyboard"), - _clkp("clkp", "application/vnd.crick.clicker.palette"), - _clkt("clkt", "application/vnd.crick.clicker.template"), - _clkw("clkw", "application/vnd.crick.clicker.wordbank"), - _wbs("wbs", "application/vnd.criticaltools.wbs+xml"), - _pml("pml", "application/vnd.ctc-posml"), - _ppd("ppd", "application/vnd.cups-ppd"), - _car("car", "application/vnd.curl.car"), - _pcurl("pcurl", "application/vnd.curl.pcurl"), - _rdz("rdz", "application/vnd.data-vision.rdz"), - _fe_launch("fe_launch", "application/vnd.denovo.fcselayout-link"), - _dna("dna", "application/vnd.dna"), - _mlp("mlp", "application/vnd.dolby.mlp"), - _dpg("dpg", "application/vnd.dpgraph"), - _dfac("dfac", "application/vnd.dreamfactory"), - _ait("ait", "application/vnd.dvb.ait"), - _svc("svc", "application/vnd.dvb.service"), - _geo("geo", "application/vnd.dynageo"), - _mag("mag", "application/vnd.ecowin.chart"), - _nml("nml", "application/vnd.enliven"), - _esf("esf", "application/vnd.epson.esf"), - _msf("msf", "application/vnd.epson.msf"), - _qam("qam", "application/vnd.epson.quickanime"), - _slt("slt", "application/vnd.epson.salt"), - _ssf("ssf", "application/vnd.epson.ssf"), - _es3("es3", "application/vnd.eszigno3+xml"), - _ez2("ez2", "application/vnd.ezpix-album"), - _ez3("ez3", "application/vnd.ezpix-package"), - _fdf("fdf", "application/vnd.fdf"), - _seed("seed", "application/vnd.fdsn.seed"), - _gph("gph", "application/vnd.flographit"), - _ftc("ftc", "application/vnd.fluxtime.clip"), - _fm("fm", "application/vnd.framemaker"), - _fnc("fnc", "application/vnd.frogans.fnc"), - _ltf("ltf", "application/vnd.frogans.ltf"), - _fsc("fsc", "application/vnd.fsc.weblaunch"), - _oas("oas", "application/vnd.fujitsu.oasys"), - _oa2("oa2", "application/vnd.fujitsu.oasys2"), - _oa3("oa3", "application/vnd.fujitsu.oasys3"), - _fg5("fg5", "application/vnd.fujitsu.oasysgp"), - _bh2("bh2", "application/vnd.fujitsu.oasysprs"), - _ddd("ddd", "application/vnd.fujixerox.ddd"), - _xdw("xdw", "application/vnd.fujixerox.docuworks"), - _xbd("xbd", "application/vnd.fujixerox.docuworks.binder"), - _fzs("fzs", "application/vnd.fuzzysheet"), - _txd("txd", "application/vnd.genomatix.tuxedo"), - _ggb("ggb", "application/vnd.geogebra.file"), - _ggt("ggt", "application/vnd.geogebra.tool"), - _gex("gex", "application/vnd.geometry-explorer"), - _gxt("gxt", "application/vnd.geonext"), - _g2w("g2w", "application/vnd.geoplan"), - _g3w("g3w", "application/vnd.geospace"), - _gmx("gmx", "application/vnd.gmx"), - _kml("kml", "application/vnd.google-earth.kml+xml"), - _kmz("kmz", "application/vnd.google-earth.kmz"), - _gqf("gqf", "application/vnd.grafeq"), - _gac("gac", "application/vnd.groove-account"), - _ghf("ghf", "application/vnd.groove-help"), - _gim("gim", "application/vnd.groove-identity-message"), - _grv("grv", "application/vnd.groove-injector"), - _gtm("gtm", "application/vnd.groove-tool-message"), - _tpl("tpl", "application/vnd.groove-tool-template"), - _vcg("vcg", "application/vnd.groove-vcard"), - _hal("hal", "application/vnd.hal+xml"), - _zmm("zmm", "application/vnd.handheld-entertainment+xml"), - _hbci("hbci", "application/vnd.hbci"), - _les("les", "application/vnd.hhe.lesson-player"), - _hpgl("hpgl", "application/vnd.hp-hpgl"), - _hpid("hpid", "application/vnd.hp-hpid"), - _hps("hps", "application/vnd.hp-hps"), - _jlt("jlt", "application/vnd.hp-jlyt"), - _pcl("pcl", "application/vnd.hp-pcl"), - _pclxl("pclxl", "application/vnd.hp-pclxl"), - _sfd_hdstx("sfd-hdstx", "application/vnd.hydrostatix.sof-data"), - _x3d("x3d", "application/vnd.hzn-3d-crossword"), - _mpy("mpy", "application/vnd.ibm.minipay"), - _afp("afp", "application/vnd.ibm.modcap"), - _irm("irm", "application/vnd.ibm.rights-management"), - _sc("sc", "application/vnd.ibm.secure-container"), - _icc("icc", "application/vnd.iccprofile"), - _igl("igl", "application/vnd.igloader"), - _ivp("ivp", "application/vnd.immervision-ivp"), - _ivu("ivu", "application/vnd.immervision-ivu"), - _igm("igm", "application/vnd.insors.igm"), - _xpw("xpw", "application/vnd.intercon.formnet"), - _i2g("i2g", "application/vnd.intergeo"), - _qbo("qbo", "application/vnd.intu.qbo"), - _qfx("qfx", "application/vnd.intu.qfx"), - _rcprofile("rcprofile", "application/vnd.ipunplugged.rcprofile"), - _irp("irp", "application/vnd.irepository.package+xml"), - _xpr("xpr", "application/vnd.is-xpr"), - _fcs("fcs", "application/vnd.isac.fcs"), - _jam("jam", "application/vnd.jam"), - _rms("rms", "application/vnd.jcp.javame.midlet-rms"), - _jisp("jisp", "application/vnd.jisp"), - _joda("joda", "application/vnd.joost.joda-archive"), - _ktz("ktz", "application/vnd.kahootz"), - _karbon("karbon", "application/vnd.kde.karbon"), - _chrt("chrt", "application/vnd.kde.kchart"), - _kfo("kfo", "application/vnd.kde.kformula"), - _flw("flw", "application/vnd.kde.kivio"), - _kon("kon", "application/vnd.kde.kontour"), - _kpr("kpr", "application/vnd.kde.kpresenter"), - _ksp("ksp", "application/vnd.kde.kspread"), - _kwd("kwd", "application/vnd.kde.kword"), - _htke("htke", "application/vnd.kenameaapp"), - _kia("kia", "application/vnd.kidspiration"), - _kne("kne", "application/vnd.kinar"), - _skp("skp", "application/vnd.koan"), - _sse("sse", "application/vnd.kodak-descriptor"), - _lasxml("lasxml", "application/vnd.las.las+xml"), - _lbd("lbd", "application/vnd.llamagraphics.life-balance.desktop"), - _lbe("lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"), - _123("123", "application/vnd.lotus-1-2-3"), - _apr("apr", "application/vnd.lotus-approach"), - _pre("pre", "application/vnd.lotus-freelance"), - _nsf("nsf", "application/vnd.lotus-notes"), - _org("org", "application/vnd.lotus-organizer"), - _scm("scm", "application/vnd.lotus-screencam"), - _lwp("lwp", "application/vnd.lotus-wordpro"), - _portpkg("portpkg", "application/vnd.macports.portpkg"), - _mcd("mcd", "application/vnd.mcd"), - _mc1("mc1", "application/vnd.medcalcdata"), - _cdkey("cdkey", "application/vnd.mediastation.cdkey"), - _mwf("mwf", "application/vnd.mfer"), - _mfm("mfm", "application/vnd.mfmp"), - _flo("flo", "application/vnd.micrografx.flo"), - _igx("igx", "application/vnd.micrografx.igx"), - _mif("mif", "application/vnd.mif"), - _daf("daf", "application/vnd.mobius.daf"), - _dis("dis", "application/vnd.mobius.dis"), - _mbk("mbk", "application/vnd.mobius.mbk"), - _mqy("mqy", "application/vnd.mobius.mqy"), - _msl("msl", "application/vnd.mobius.msl"), - _plc("plc", "application/vnd.mobius.plc"), - _txf("txf", "application/vnd.mobius.txf"), - _mpn("mpn", "application/vnd.mophun.application"), - _mpc("mpc", "application/vnd.mophun.certificate"), - _xul("xul", "application/vnd.mozilla.xul+xml"), - _cil("cil", "application/vnd.ms-artgalry"), - _cab("cab", "application/vnd.ms-cab-compressed"), - _xls("xls", "application/vnd.ms-excel"), - _xlam("xlam", "application/vnd.ms-excel.addin.macroenabled.12"), - _xlsb("xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"), - _xlsm("xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"), - _xltm("xltm", "application/vnd.ms-excel.template.macroenabled.12"), - _eot("eot", "application/vnd.ms-fontobject"), - _chm("chm", "application/vnd.ms-htmlhelp"), - _ims("ims", "application/vnd.ms-ims"), - _lrm("lrm", "application/vnd.ms-lrm"), - _thmx("thmx", "application/vnd.ms-officetheme"), - _cat("cat", "application/vnd.ms-pki.seccat"), - _stl("stl", "application/vnd.ms-pki.stl"), - _ppt("ppt", "application/vnd.ms-powerpoint"), - _ppam("ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"), - _pptm("pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"), - _sldm("sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"), - _ppsm("ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"), - _potm("potm", "application/vnd.ms-powerpoint.template.macroenabled.12"), - _mpp("mpp", "application/vnd.ms-project"), - _docm("docm", "application/vnd.ms-word.document.macroenabled.12"), - _dotm("dotm", "application/vnd.ms-word.template.macroenabled.12"), - _wps("wps", "application/vnd.ms-works"), - _wpl("wpl", "application/vnd.ms-wpl"), - _xps("xps", "application/vnd.ms-xpsdocument"), - _mseq("mseq", "application/vnd.mseq"), - _mus("mus", "application/vnd.musician"), - _msty("msty", "application/vnd.muvee.style"), - _nlu("nlu", "application/vnd.neurolanguage.nlu"), - _nnd("nnd", "application/vnd.noblenet-directory"), - _nns("nns", "application/vnd.noblenet-sealer"), - _nnw("nnw", "application/vnd.noblenet-web"), - _ngdat("ngdat", "application/vnd.nokia.n-gage.data"), - _n_gage("n-gage", "application/vnd.nokia.n-gage.symbian.install"), - _rpst("rpst", "application/vnd.nokia.radio-preset"), - _rpss("rpss", "application/vnd.nokia.radio-presets"), - _edm("edm", "application/vnd.novadigm.edm"), - _edx("edx", "application/vnd.novadigm.edx"), - _ext("ext", "application/vnd.novadigm.ext"), - _odc("odc", "application/vnd.oasis.opendocument.chart"), - _otc("otc", "application/vnd.oasis.opendocument.chart-template"), - _odb("odb", "application/vnd.oasis.opendocument.database"), - _odf("odf", "application/vnd.oasis.opendocument.formula"), - _odft("odft", "application/vnd.oasis.opendocument.formula-template"), - _odg("odg", "application/vnd.oasis.opendocument.graphics"), - _otg("otg", "application/vnd.oasis.opendocument.graphics-template"), - _odi("odi", "application/vnd.oasis.opendocument.image"), - _oti("oti", "application/vnd.oasis.opendocument.image-template"), - _odp("odp", "application/vnd.oasis.opendocument.presentation"), - _otp("otp", "application/vnd.oasis.opendocument.presentation-template"), - _ods("ods", "application/vnd.oasis.opendocument.spreadsheet"), - _ots("ots", "application/vnd.oasis.opendocument.spreadsheet-template"), - _odt("odt", "application/vnd.oasis.opendocument.text"), - _odm("odm", "application/vnd.oasis.opendocument.text-master"), - _ott("ott", "application/vnd.oasis.opendocument.text-template"), - _oth("oth", "application/vnd.oasis.opendocument.text-web"), - _xo("xo", "application/vnd.olpc-sugar"), - _dd2("dd2", "application/vnd.oma.dd2+xml"), - _oxt("oxt", "application/vnd.openofficeorg.extension"), - _pptx("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"), - _sldx("sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"), - _ppsx("ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"), - _potx("potx", "application/vnd.openxmlformats-officedocument.presentationml.template"), - _xlsx("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), - _xltx("xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"), - _docx("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"), - _dotx("dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"), - _mgp("mgp", "application/vnd.osgeo.mapguide.package"), - _dp("dp", "application/vnd.osgi.dp"), - _pdb("pdb", "application/vnd.palm"), - _paw("paw", "application/vnd.pawaafile"), - _str("str", "application/vnd.pg.format"), - _ei6("ei6", "application/vnd.pg.osasli"), - _efif("efif", "application/vnd.picsel"), - _wg("wg", "application/vnd.pmi.widget"), - _plf("plf", "application/vnd.pocketlearn"), - _pbd("pbd", "application/vnd.powerbuilder6"), - _box("box", "application/vnd.previewsystems.box"), - _mgz("mgz", "application/vnd.proteus.magazine"), - _qps("qps", "application/vnd.publishare-delta-tree"), - _ptid("ptid", "application/vnd.pvi.ptid1"), - _qxd("qxd", "application/vnd.quark.quarkxpress"), - _bed("bed", "application/vnd.realvnc.bed"), - _mxl("mxl", "application/vnd.recordare.musicxml"), - _musicxml("musicxml", "application/vnd.recordare.musicxml+xml"), - _cryptonote("cryptonote", "application/vnd.rig.cryptonote"), - _cod("cod", "application/vnd.rim.cod"), - _rm("rm", "application/vnd.rn-realmedia"), - _link66("link66", "application/vnd.route66.link66+xml"), - _st("st", "application/vnd.sailingtracker.track"), - _see("see", "application/vnd.seemail"), - _sema("sema", "application/vnd.sema"), - _semd("semd", "application/vnd.semd"), - _semf("semf", "application/vnd.semf"), - _ifm("ifm", "application/vnd.shana.informed.formdata"), - _itp("itp", "application/vnd.shana.informed.formtemplate"), - _iif("iif", "application/vnd.shana.informed.interchange"), - _ipk("ipk", "application/vnd.shana.informed.package"), - _twd("twd", "application/vnd.simtech-mindmapper"), - _mmf("mmf", "application/vnd.smaf"), - _teacher("teacher", "application/vnd.smart.teacher"), - _sdkm("sdkm", "application/vnd.solent.sdkm+xml"), - _dxp("dxp", "application/vnd.spotfire.dxp"), - _sfs("sfs", "application/vnd.spotfire.sfs"), - _sdc("sdc", "application/vnd.stardivision.calc"), - _sda("sda", "application/vnd.stardivision.draw"), - _sdd("sdd", "application/vnd.stardivision.impress"), - _smf("smf", "application/vnd.stardivision.math"), - _sdw("sdw", "application/vnd.stardivision.writer"), - _sgl("sgl", "application/vnd.stardivision.writer-global"), - _sm("sm", "application/vnd.stepmania.stepchart"), - _sxc("sxc", "application/vnd.sun.xml.calc"), - _stc("stc", "application/vnd.sun.xml.calc.template"), - _sxd("sxd", "application/vnd.sun.xml.draw"), - _std("std", "application/vnd.sun.xml.draw.template"), - _sxi("sxi", "application/vnd.sun.xml.impress"), - _sti("sti", "application/vnd.sun.xml.impress.template"), - _sxm("sxm", "application/vnd.sun.xml.math"), - _sxw("sxw", "application/vnd.sun.xml.writer"), - _sxg("sxg", "application/vnd.sun.xml.writer.global"), - _stw("stw", "application/vnd.sun.xml.writer.template"), - _sus("sus", "application/vnd.sus-calendar"), - _svd("svd", "application/vnd.svd"), - _sis("sis", "application/vnd.symbian.install"), - _xsm("xsm", "application/vnd.syncml+xml"), - _bdm("bdm", "application/vnd.syncml.dm+wbxml"), - _xdm("xdm", "application/vnd.syncml.dm+xml"), - _tao("tao", "application/vnd.tao.intent-module-archive"), - _tmo("tmo", "application/vnd.tmobile-livetv"), - _tpt("tpt", "application/vnd.trid.tpt"), - _mxs("mxs", "application/vnd.triscape.mxs"), - _tra("tra", "application/vnd.trueapp"), - _ufd("ufd", "application/vnd.ufdl"), - _utz("utz", "application/vnd.uiq.theme"), - _umj("umj", "application/vnd.umajin"), - _unityweb("unityweb", "application/vnd.unity"), - _uoml("uoml", "application/vnd.uoml+xml"), - _vcx("vcx", "application/vnd.vcx"), - _vsd("vsd", "application/vnd.visio"), - _vsdx("vsdx", "application/vnd.visio2013"), - _vis("vis", "application/vnd.visionary"), - _vsf("vsf", "application/vnd.vsf"), - _wbxml("wbxml", "application/vnd.wap.wbxml"), - _wmlc("wmlc", "application/vnd.wap.wmlc"), - _wmlsc("wmlsc", "application/vnd.wap.wmlscriptc"), - _wtb("wtb", "application/vnd.webturbo"), - _nbp("nbp", "application/vnd.wolfram.player"), - _wpd("wpd", "application/vnd.wordperfect"), - _wqd("wqd", "application/vnd.wqd"), - _stf("stf", "application/vnd.wt.stf"), - _xar("xar", "application/vnd.xara"), - _xfdl("xfdl", "application/vnd.xfdl"), - _hvd("hvd", "application/vnd.yamaha.hv-dic"), - _hvs("hvs", "application/vnd.yamaha.hv-script"), - _hvp("hvp", "application/vnd.yamaha.hv-voice"), - _osf("osf", "application/vnd.yamaha.openscoreformat"), - _osfpvg("osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"), - _saf("saf", "application/vnd.yamaha.smaf-audio"), - _spf("spf", "application/vnd.yamaha.smaf-phrase"), - _cmp("cmp", "application/vnd.yellowriver-custom-menu"), - _zir("zir", "application/vnd.zul"), - _zaz("zaz", "application/vnd.zzazz.deck+xml"), - _vxml("vxml", "application/voicexml+xml"), - _wgt("wgt", "application/widget"), - _hlp("hlp", "application/winhlp"), - _wsdl("wsdl", "application/wsdl+xml"), - _wspolicy("wspolicy", "application/wspolicy+xml"), - _7z("7z", "application/x-7z-compressed"), - _abw("abw", "application/x-abiword"), - _ace("ace", "application/x-ace-compressed"), - _aab("aab", "application/x-authorware-bin"), - _aam("aam", "application/x-authorware-map"), - _aas("aas", "application/x-authorware-seg"), - _bcpio("bcpio", "application/x-bcpio"), - _torrent("torrent", "application/x-bittorrent"), - _bz("bz", "application/x-bzip"), - _bz2("bz2", "application/x-bzip2"), - _vcd("vcd", "application/x-cdlink"), - _chat("chat", "application/x-chat"), - _pgn("pgn", "application/x-chess-pgn"), - _cpio("cpio", "application/x-cpio"), - _csh("csh", "application/x-csh"), - _deb("deb", "application/x-debian-package"), - _dir("dir", "application/x-director"), - _wad("wad", "application/x-doom"), - _ncx("ncx", "application/x-dtbncx+xml"), - _dtb("dtb", "application/x-dtbook+xml"), - _res("res", "application/x-dtbresource+xml"), - _dvi("dvi", "application/x-dvi"), - _bdf("bdf", "application/x-font-bdf"), - _gsf("gsf", "application/x-font-ghostscript"), - _psf("psf", "application/x-font-linux-psf"), - _otf("otf", "application/x-font-otf"), - _pcf("pcf", "application/x-font-pcf"), - _snf("snf", "application/x-font-snf"), - _ttf("ttf", "application/x-font-ttf"), - _pfa("pfa", "application/x-font-type1"), - _woff("woff", "application/x-font-woff"), - _spl("spl", "application/x-futuresplash"), - _gnumeric("gnumeric", "application/x-gnumeric"), - _gtar("gtar", "application/x-gtar"), - _hdf("hdf", "application/x-hdf"), - _jnlp("jnlp", "application/x-java-jnlp-file"), - _latex("latex", "application/x-latex"), - _prc("prc", "application/x-mobipocket-ebook"), - _application("application", "application/x-ms-application"), - _wmd("wmd", "application/x-ms-wmd"), - _wmz("wmz", "application/x-ms-wmz"), - _xbap("xbap", "application/x-ms-xbap"), - _mdb("mdb", "application/x-msaccess"), - _obd("obd", "application/x-msbinder"), - _crd("crd", "application/x-mscardfile"), - _clp("clp", "application/x-msclip"), - _exe("exe", "application/x-msdownload"), - _mvb("mvb", "application/x-msmediaview"), - _wmf("wmf", "application/x-msmetafile"), - _mny("mny", "application/x-msmoney"), - _pub("pub", "application/x-mspublisher"), - _scd("scd", "application/x-msschedule"), - _trm("trm", "application/x-msterminal"), - _wri("wri", "application/x-mswrite"), - _nc("nc", "application/x-netcdf"), - _p12("p12", "application/x-pkcs12"), - _p7b("p7b", "application/x-pkcs7-certificates"), - _p7r("p7r", "application/x-pkcs7-certreqresp"), - _rar("rar", "application/x-rar-compressed"), - _sh("sh", "application/x-sh"), - _shar("shar", "application/x-shar"), - _swf("swf", "application/x-shockwave-flash"), - _xap("xap", "application/x-silverlight-app"), - _sit("sit", "application/x-stuffit"), - _sitx("sitx", "application/x-stuffitx"), - _sv4cpio("sv4cpio", "application/x-sv4cpio"), - _sv4crc("sv4crc", "application/x-sv4crc"), - _tar("tar", "application/x-tar"), - _tcl("tcl", "application/x-tcl"), - _tex("tex", "application/x-tex"), - _tfm("tfm", "application/x-tex-tfm"), - _texinfo("texinfo", "application/x-texinfo"), - _ustar("ustar", "application/x-ustar"), - _src("src", "application/x-wais-source"), - _der("der", "application/x-x509-ca-cert"), - _fig("fig", "application/x-xfig"), - _xpi("xpi", "application/x-xpinstall"), - _xdf("xdf", "application/xcap-diff+xml"), - _xenc("xenc", "application/xenc+xml"), - _xhtml("xhtml", "application/xhtml+xml"), - _xml("xml", "application/xml"), - _dtd("dtd", "application/xml-dtd"), - _xop("xop", "application/xop+xml"), - _xslt("xslt", "application/xslt+xml"), - _xspf("xspf", "application/xspf+xml"), - _mxml("mxml", "application/xv+xml"), - _yang("yang", "application/yang"), - _yin("yin", "application/yin+xml"), - _zip("zip", "application/zip"), - _adp("adp", "audio/adpcm"), - _au("au", "audio/basic"), - _mid("mid", "audio/midi"), - _mp4a("mp4a", "audio/mp4"), - _mpga("mpga", "audio/mpeg"), - _oga("oga", "audio/ogg"), - _uva("uva", "audio/vnd.dece.audio"), - _eol("eol", "audio/vnd.digital-winds"), - _dra("dra", "audio/vnd.dra"), - _dts("dts", "audio/vnd.dts"), - _dtshd("dtshd", "audio/vnd.dts.hd"), - _lvp("lvp", "audio/vnd.lucent.voice"), - _pya("pya", "audio/vnd.ms-playready.media.pya"), - _ecelp4800("ecelp4800", "audio/vnd.nuera.ecelp4800"), - _ecelp7470("ecelp7470", "audio/vnd.nuera.ecelp7470"), - _ecelp9600("ecelp9600", "audio/vnd.nuera.ecelp9600"), - _rip("rip", "audio/vnd.rip"), - _weba("weba", "audio/webm"), - _aac("aac", "audio/x-aac"), - _aif("aif", "audio/x-aiff"), - _m3u("m3u", "audio/x-mpegurl"), - _wax("wax", "audio/x-ms-wax"), - _wma("wma", "audio/x-ms-wma"), - _ram("ram", "audio/x-pn-realaudio"), - _rmp("rmp", "audio/x-pn-realaudio-plugin"), - _wav("wav", "audio/x-wav"), - _cdx("cdx", "chemical/x-cdx"), - _cif("cif", "chemical/x-cif"), - _cmdf("cmdf", "chemical/x-cmdf"), - _cml("cml", "chemical/x-cml"), - _csml("csml", "chemical/x-csml"), - _xyz("xyz", "chemical/x-xyz"), - _bmp("bmp", "image/bmp"), - _cgm("cgm", "image/cgm"), - _g3("g3", "image/g3fax"), - _gif("gif", "image/gif"), - _ief("ief", "image/ief"), - _jpeg("jpeg", "image/jpeg"), - _jpg("jpg", "image/jpeg"), - _pjpeg("pjpeg", "image/pjpeg"), - _ktx("ktx", "image/ktx"), - _png("png", "image/x-citrix-png"), - _btif("btif", "image/prs.btif"), - _svg("svg", "image/svg+xml"), - _tiff("tiff", "image/tiff"), - _psd("psd", "image/vnd.adobe.photoshop"), - _uvi("uvi", "image/vnd.dece.graphic"), - _sub("sub", "image/vnd.dvb.subtitle"), - _djvu("djvu", "image/vnd.djvu"), - _dwg("dwg", "image/vnd.dwg"), - _dxf("dxf", "image/vnd.dxf"), - _fbs("fbs", "image/vnd.fastbidsheet"), - _fpx("fpx", "image/vnd.fpx"), - _fst("fst", "image/vnd.fst"), - _mmr("mmr", "image/vnd.fujixerox.edmics-mmr"), - _rlc("rlc", "image/vnd.fujixerox.edmics-rlc"), - _mdi("mdi", "image/vnd.ms-modi"), - _npx("npx", "image/vnd.net-fpx"), - _wbmp("wbmp", "image/vnd.wap.wbmp"), - _xif("xif", "image/vnd.xiff"), - _webp("webp", "image/webp"), - _ras("ras", "image/x-cmu-raster"), - _cmx("cmx", "image/x-cmx"), - _fh("fh", "image/x-freehand"), - _ico("ico", "image/x-icon"), - _pcx("pcx", "image/x-pcx"), - _pic("pic", "image/x-pict"), - _pnm("pnm", "image/x-portable-anymap"), - _pbm("pbm", "image/x-portable-bitmap"), - _pgm("pgm", "image/x-portable-graymap"), - _ppm("ppm", "image/x-portable-pixmap"), - _rgb("rgb", "image/x-rgb"), - _xbm("xbm", "image/x-xbitmap"), - _xpm("xpm", "image/x-xpixmap"), - _xwd("xwd", "image/x-xwindowdump"), - _eml("eml", "message/rfc822"), - _igs("igs", "model/iges"), - _msh("msh", "model/mesh"), - _dae("dae", "model/vnd.collada+xml"), - _dwf("dwf", "model/vnd.dwf"), - _gdl("gdl", "model/vnd.gdl"), - _gtw("gtw", "model/vnd.gtw"), - _mts("mts", "model/vnd.mts"), - _vtu("vtu", "model/vnd.vtu"), - _wrl("wrl", "model/vrml"), - _ics("ics", "text/calendar"), - _css("css", "text/css"), - _csv("csv", "text/csv"), - _html("html", "text/html"), - _n3("n3", "text/n3"), - _txt("txt", "text/plain"), - _dsc("dsc", "text/prs.lines.tag"), - _rtx("rtx", "text/richtext"), - _sgml("sgml", "text/sgml"), - _tsv("tsv", "text/tab-separated-values"), - _t("t", "text/troff"), - _ttl("ttl", "text/turtle"), - _uri("uri", "text/uri-list"), - _curl("curl", "text/vnd.curl"), - _dcurl("dcurl", "text/vnd.curl.dcurl"), - _scurl("scurl", "text/vnd.curl.scurl"), - _mcurl("mcurl", "text/vnd.curl.mcurl"), - _fly("fly", "text/vnd.fly"), - _flx("flx", "text/vnd.fmi.flexstor"), - _gv("gv", "text/vnd.graphviz"), - _3dml("3dml", "text/vnd.in3d.3dml"), - _spot("spot", "text/vnd.in3d.spot"), - _jad("jad", "text/vnd.sun.j2me.app-descriptor"), - _wml("wml", "text/vnd.wap.wml"), - _wmls("wmls", "text/vnd.wap.wmlscript"), - _s("s", "text/x-asm"), - _c("c", "text/x-c"), - _f("f", "text/x-fortran"), - _p("p", "text/x-pascal"), - _java("java", "text/x-java-source"), - _etx("etx", "text/x-setext"), - _uu("uu", "text/x-uuencode"), - _vcs("vcs", "text/x-vcalendar"), - _vcf("vcf", "text/x-vcard"), - _3gp("3gp", "video/3gpp"), - _3g2("3g2", "video/3gpp2"), - _h261("h261", "video/h261"), - _h263("h263", "video/h263"), - _h264("h264", "video/h264"), - _jpgv("jpgv", "video/jpeg"), - _jpm("jpm", "video/jpm"), - _mj2("mj2", "video/mj2"), - _mp4("mp4", "video/mp4"), - _mpeg("mpeg", "video/mpeg"), - _ogv("ogv", "video/ogg"), - _qt("qt", "video/quicktime"), - _uvh("uvh", "video/vnd.dece.hd"), - _uvm("uvm", "video/vnd.dece.mobile"), - _uvp("uvp", "video/vnd.dece.pd"), - _uvs("uvs", "video/vnd.dece.sd"), - _uvv("uvv", "video/vnd.dece.video"), - _fvt("fvt", "video/vnd.fvt"), - _mxu("mxu", "video/vnd.mpegurl"), - _pyv("pyv", "video/vnd.ms-playready.media.pyv"), - _uvu("uvu", "video/vnd.uvvu.mp4"), - _viv("viv", "video/vnd.vivo"), - _webm("webm", "video/webm"), - _f4v("f4v", "video/x-f4v"), - _fli("fli", "video/x-fli"), - _flv("flv", "video/x-flv"), - _m4v("m4v", "video/x-m4v"), - _asf("asf", "video/x-ms-asf"), - _wm("wm", "video/x-ms-wm"), - _wmv("wmv", "video/x-ms-wmv"), - _wmx("wmx", "video/x-ms-wmx"), - _wvx("wvx", "video/x-ms-wvx"), - _avi("avi", "video/x-msvideo"), - _movie("movie", "video/x-sgi-movie"), - _ice("ice", "x-conference/x-cooltalk"), - _par("par", "text/plain-bas"), - _yaml("yaml", "text/yaml"), - _dmg("dmg", "application/x-apple-diskimage"), - _xww("form", "application/x-www-form-urlencoded"); + private final String mime; + private final String extension; - private final String mime; - private final String extension; + MediaType(String extension, String mime) { + this.mime = mime; + this.extension = extension; + } - MediaType(String extension, String mime) { - this.mime = mime; - this.extension = extension; + public static MediaType getByExtension(String extension) { + for (MediaType type : values()) { + if (type.extension.equals(extension)) { + return type; + } } + return null; + } - public static MediaType getByExtension(String extension) { - for (MediaType type : values()) { - if (type.extension.equals(extension)) { - return type; - } - } - return null; - } - - public String getMIME() { - return mime; - } + public String getMIME() { + return mime; + } - public String getExtension() { - return extension; - } + public String getExtension() { + return extension; + } } diff --git a/src/main/java/express/utils/Status.java b/src/main/java/express/utils/Status.java index dce7217..b06f346 100644 --- a/src/main/java/express/utils/Status.java +++ b/src/main/java/express/utils/Status.java @@ -1,120 +1,120 @@ package express.utils; /** - * @author Simon Reinisch * Enum with all status codes. + * + * @author Simon Reinisch */ public enum Status { - // Informational - _100(100, "Continue"), - _101(101, "Switching Protocols"), - _102(102, "Processing"), - - // Success - _200(200, "OK"), - _201(201, "Created"), - _202(202, "Accepted"), - _203(203, "Non-authoritative Information"), - _204(204, "No Content"), - _205(205, "Reset Content"), - _206(206, "Partial Content"), - _207(207, "Multi-Status"), - _208(208, "Already Reported"), - _226(226, "IM Used"), - - // Redirection - _300(300, "Multiple Choices"), - _301(301, "Moved Permanently"), - _302(302, "Found"), - _303(303, "See Other"), - _304(304, "Not Modified"), - _305(305, "Use Proxy"), - _307(307, "Temporary Redirect"), - _308(308, "Permanent Redirect"), - - // Client Error - _400(400, "Bad Request"), - _401(401, "Unauthorized"), - _402(402, "Payment Required"), - _403(403, "Forbidden"), - _404(404, "Not Found"), - _405(405, "Method Not Allowed"), - _406(406, "Not Acceptable"), - _407(407, "Proxy Authentication Required"), - _408(408, "Request Timeout"), - _409(409, "Conflict"), - _410(410, "Gone"), - _411(411, "Length Required"), - _412(412, "Precondition Failed"), - _413(413, "Payload Too Large"), - _414(414, "Request-URI Too Long"), - _415(415, "Unsupported Media Type"), - _416(416, "Requested Range Not Satisfiable"), - _417(417, "Expectation Failed"), - _418(418, "I'm a teapot"), - _421(421, "Misdirected Request"), - _422(422, "Unprocessable Entity"), - _423(423, "Locked"), - _424(424, "Failed Dependency"), - _426(426, "Upgrade Required"), - _428(428, "Precondition Required"), - _429(429, "Too Many Requests"), - _431(431, "Request Header Fields Too Large"), - _444(444, "Connection Closed Without Response"), - _451(451, "Unavailable For Legal Reasons"), - _499(499, "Client Closed Request"), - - // Server Error - _500(500, "Internal Server Error"), - _501(501, "Not Implemented"), - _502(502, "Bad Gateway"), - _503(503, "Service Unavailable"), - _504(504, "Gateway Timeout"), - _505(505, "HTTP Version Not Supported"), - _506(506, "Variant Also Negotiates"), - _507(507, "Insufficient Storage"), - _508(508, "Loop Detected"), - _510(510, "Not Extended"), - _511(511, "Network Authentication Required"), - _599(599, "Network Connect Timeout Error"); - - - static { - - // Check values - for (Status s : values()) { - if (s.name().charAt(0) != '_') { - throw new IllegalStateException("Status code '" + s + "' need to start with underscore."); - } - } + // Informational + _100(100, "Continue"), + _101(101, "Switching Protocols"), + _102(102, "Processing"), + + // Success + _200(200, "OK"), + _201(201, "Created"), + _202(202, "Accepted"), + _203(203, "Non-authoritative Information"), + _204(204, "No Content"), + _205(205, "Reset Content"), + _206(206, "Partial Content"), + _207(207, "Multi-Status"), + _208(208, "Already Reported"), + _226(226, "IM Used"), + + // Redirection + _300(300, "Multiple Choices"), + _301(301, "Moved Permanently"), + _302(302, "Found"), + _303(303, "See Other"), + _304(304, "Not Modified"), + _305(305, "Use Proxy"), + _307(307, "Temporary Redirect"), + _308(308, "Permanent Redirect"), + + // Client Error + _400(400, "Bad Request"), + _401(401, "Unauthorized"), + _402(402, "Payment Required"), + _403(403, "Forbidden"), + _404(404, "Not Found"), + _405(405, "Method Not Allowed"), + _406(406, "Not Acceptable"), + _407(407, "Proxy Authentication Required"), + _408(408, "Request Timeout"), + _409(409, "Conflict"), + _410(410, "Gone"), + _411(411, "Length Required"), + _412(412, "Precondition Failed"), + _413(413, "Payload Too Large"), + _414(414, "Request-URI Too Long"), + _415(415, "Unsupported Media Type"), + _416(416, "Requested Range Not Satisfiable"), + _417(417, "Expectation Failed"), + _418(418, "I'm a teapot"), + _421(421, "Misdirected Request"), + _422(422, "Unprocessable Entity"), + _423(423, "Locked"), + _424(424, "Failed Dependency"), + _426(426, "Upgrade Required"), + _428(428, "Precondition Required"), + _429(429, "Too Many Requests"), + _431(431, "Request Header Fields Too Large"), + _444(444, "Connection Closed Without Response"), + _451(451, "Unavailable For Legal Reasons"), + _499(499, "Client Closed Request"), + + // Server Error + _500(500, "Internal Server Error"), + _501(501, "Not Implemented"), + _502(502, "Bad Gateway"), + _503(503, "Service Unavailable"), + _504(504, "Gateway Timeout"), + _505(505, "HTTP Version Not Supported"), + _506(506, "Variant Also Negotiates"), + _507(507, "Insufficient Storage"), + _508(508, "Loop Detected"), + _510(510, "Not Extended"), + _511(511, "Network Authentication Required"), + _599(599, "Network Connect Timeout Error"); + + static { + + // Check values + for (Status s : values()) { + if (s.name().charAt(0) != '_') { + throw new IllegalStateException("Status code '" + s + "' need to start with underscore."); + } } + } - private String description; - private int code; - - Status(int code, String description) { - this.code = code; - this.description = description; - } + private String description; + private int code; - public static Status valueOf(int code) { + Status(int code, String description) { + this.code = code; + this.description = description; + } - // Find status which matches code - for (Status status : values()) { - if (status.code == code) { - return status; - } - } + public static Status valueOf(int code) { - return null; + // Find status which matches code + for (Status status : values()) { + if (status.code == code) { + return status; + } } - public String getDescription() { - return description; - } + return null; + } - public int getCode() { - return code; - } + public String getDescription() { + return description; + } + + public int getCode() { + return code; + } } diff --git a/src/main/java/express/utils/Utils.java b/src/main/java/express/utils/Utils.java index eca1030..3115062 100644 --- a/src/main/java/express/utils/Utils.java +++ b/src/main/java/express/utils/Utils.java @@ -12,85 +12,85 @@ public final class Utils { - private Utils() {} + private Utils() {} - /** - * Write all data from an InputStream in an String - * - * @param is The source InputStream - * @return The data as string - */ - public static String streamToString(InputStream is) { - try { - BufferedReader br = new BufferedReader(new InputStreamReader(is)); - StringBuilder sb = new StringBuilder(); - String line; + /** + * Write all data from an InputStream in an String + * + * @param is The source InputStream + * @return The data as string + */ + public static String streamToString(InputStream is) { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + StringBuilder sb = new StringBuilder(); + String line; - while ((line = br.readLine()) != null) { - sb.append(line); - } + while ((line = br.readLine()) != null) { + sb.append(line); + } - return sb.toString(); - } catch (IOException ignored) { - } - - return null; + return sb.toString(); + } catch (IOException ignored) { } - /** - * Returns the MIME-Type of an file. - * - * @param file The file. - * @return The MIME-Type. - */ - public static MediaType getContentType(Path file) { - String ex = getExtension(file); - MediaType contentType = MediaType.getByExtension(ex); + return null; + } - if (contentType == null) { - return MediaType._bin; - } + /** + * Returns the MIME-Type of an file. + * + * @param file The file. + * @return The MIME-Type. + */ + public static MediaType getContentType(Path file) { + String ex = getExtension(file); + MediaType contentType = MediaType.getByExtension(ex); - return contentType; + if (contentType == null) { + return MediaType._bin; } - /** - * Generates an random token with SecureRandom - * - * @param byteLength The token length - * @param radix The base - * @return An token with the base of radix - */ - public static String randomToken(int byteLength, int radix) { - SecureRandom secureRandom = new SecureRandom(); - byte[] token = new byte[byteLength]; - secureRandom.nextBytes(token); - return new BigInteger(1, token).toString(radix); //hex encoding - } + return contentType; + } - /** - * @return Your ip. - * @throws UnknownHostException If resolving fails - */ - public static String getYourIp() throws UnknownHostException { - return Inet4Address.getLocalHost().getHostAddress(); - } + /** + * Generates an random token with SecureRandom + * + * @param byteLength The token length + * @param radix The base + * @return An token with the base of radix + */ + public static String randomToken(int byteLength, int radix) { + SecureRandom secureRandom = new SecureRandom(); + byte[] token = new byte[byteLength]; + secureRandom.nextBytes(token); + return new BigInteger(1, token).toString(radix); // hex encoding + } - /** - * Extract the extension from the file. - * - * @param file The file. - * @return The extension. - */ - public static String getExtension(Path file) { - String path = file.getFileName().toString(); - int index = path.lastIndexOf('.') + 1; + /** + * @return Your ip. + * @throws UnknownHostException If resolving fails + */ + public static String getYourIp() throws UnknownHostException { + return Inet4Address.getLocalHost().getHostAddress(); + } - // No extension present - if (index == 0) { - return null; - } + /** + * Extract the extension from the file. + * + * @param file The file. + * @return The extension. + */ + public static String getExtension(Path file) { + String path = file.getFileName().toString(); + int index = path.lastIndexOf('.') + 1; - return path.substring(index); + // No extension present + if (index == 0) { + return null; } + + return path.substring(index); + } }