package com.vtence.molecule; import com.vtence.molecule.lib.matchers.Matcher; import com.vtence.molecule.middlewares.FilterMap; import com.vtence.molecule.middlewares.Router; import com.vtence.molecule.routing.RouteBuilder; import com.vtence.molecule.servers.Servers; import javax.net.ssl.SSLContext; import java.io.File; import java.io.IOException; import java.net.URI; import java.security.GeneralSecurityException; import java.util.function.Consumer; import static com.vtence.molecule.ssl.KeyStoreType.DEFAULT; import static com.vtence.molecule.ssl.SecureProtocol.TLS; public class WebServer { private static final String LOCAL_ADDRESS = "0.0.0.0"; private static final int DEFAULT_PORT = 8080; private final Server server; private final MiddlewareStack stack; private SSLContext ssl; /** * Creates a WebServer listening on the default interface (0.0.0.0) and port (8080). */ public static WebServer create() { return create(DEFAULT_PORT); } /** * Creates a WebServer listening on the default interface (0.0.0.0) and the specified port. * * @param port the port to listen on */ public static WebServer create(int port) { return create(LOCAL_ADDRESS, port); } /** * Creates a WebServer listening on the specified interface and port. * * @param host the hostname to bind to * @param port the port to listen on */ public static WebServer create(String host, int port) { return new WebServer(Servers.create(host, port)); } /** * Creates a WebServer with the specified server instance. * * @param server the server instance to use */ public WebServer(Server server) { this.server = server; this.stack = new MiddlewareStack(); } /** * Secures connections made to this WebServer with TLS using the specified key store and credentials. * The key store must be of the default platform type and use the default key manager algorithm. * * @param keyStore the location of the key store containing the certificate and keys * @param storePassword the password that opens the key store * @param keyPassword the password for using the keys * @see com.vtence.molecule.ssl.SecureProtocol * @see com.vtence.molecule.ssl.KeyStoreType */ public WebServer enableSSL(File keyStore, String storePassword, String keyPassword) throws GeneralSecurityException, IOException { return enableSSL(TLS.initialize(DEFAULT.loadKeys(keyStore, storePassword, keyPassword))); } /** * Secures connections made to this WebServer using the provided security context. This allows to use * security settings that differ from platform defaults (such as using PKCS12 instead of JKS). * * @param context the security context for securing connections * @see com.vtence.molecule.ssl.SecureProtocol * @see com.vtence.molecule.ssl.KeyStoreType */ public WebServer enableSSL(SSLContext context) { this.ssl = context; return this; } /** * Notifies the given error consumer when an uncaught exceptions occurs. * * @param reporter the failure reporter to notify in case of uncaught exceptions */ public WebServer failureReporter(FailureReporter reporter) { server.reportErrorsTo(reporter); return this; } /** * Adds a middleware to this WebServer's stack of configured middlewares. * * @param middleware the middleware to add to the stack */ public WebServer add(Middleware middleware) { stack.use(middleware); return this; } /** * Adds a middleware filter to this WebServer's stack of configured middlewares. The server will apply the * given filter to all requests with a path starting with the specified prefix. * * @param path the path to trigger filtering * @param filter the filter to apply to incoming requests that match the path prefix */ public WebServer filter(String path, Middleware filter) { stack.use(new FilterMap().map(path, filter)); return this; } /** * Adds a middleware filter to this WebServer's stack of configured middlewares. The server will apply the given * filter to all requests matched by the specified matcher. * * @param requestMatcher the matcher to trigger filtering * @param filter the filter to apply to incoming requests that are matched */ public WebServer filter(Matcher<? super Request> requestMatcher, Middleware filter) { stack.use(new FilterMap().map(requestMatcher, filter)); return this; } /** * Mounts the specified application at the given path. This WebServer will route all incoming requests * which target that path or any sub-path to the specified application. * * @param path the mount point * @param app the application to attach to the mount point */ public WebServer mount(String path, Application app) { stack.mount(path, app); return this; } /** * Configures an optional warmup sequence to run once at this WebServer startup. * * @param warmup the warmup sequence to boot the application */ public WebServer warmup(Consumer<Application> warmup) { stack.warmup(warmup); return this; } /** * Boots and starts this WebServer using the previously configured middlewares and create a router * with the specified routes. The router will run at the root mount point. * <p> * The server will start accepting and processing incoming requests. * * @param routes the routes to run at the root mount point (/) */ public Server start(RouteBuilder routes) throws IOException { return start(Router.draw(routes)); } /** * Boots and starts this WebServer using the previously configured middlewares * and the specified application at the root the mount point. * <p> * The server will start accepting and processing incoming requests. * * @param application the application to run at the root mount point (/) */ public Server start(Application application) throws IOException { stack.run(application); return start(); } /** * Boots and starts this WebServer using the previously configured middlewares and mount points. * The server will start accepting and processing incoming requests. * <p> * <i>Note that you need to configure at least a mount point before starting the server.</i> * </p> * * @see WebServer#mount(String, Application) */ public Server start() throws IOException { stack.boot(); if (ssl != null) { server.run(stack, ssl); } else { server.run(stack); } return server; } /** * Stops this WebServer and releases its resources. The server will no longer accept or process incoming * requests. */ public void stop() throws IOException { server.shutdown(); } /** * Returns the uri of this WebServer root. * * @return the server root URI */ public URI uri() { return URI.create((ssl != null ? "https://" : "http://") + server.host() + ":" + server.port()); } }