All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.renomad.minum.web.WebEngine Maven / Gradle / Ivy

There is a newer version: 8.0.7
Show newest version
package com.renomad.minum.web;

import com.renomad.minum.state.Constants;
import com.renomad.minum.logging.ILogger;
import com.renomad.minum.security.ITheBrig;
import com.renomad.minum.state.Context;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.nio.file.Path;
import java.security.*;
import java.util.concurrent.ExecutorService;

import static com.renomad.minum.web.HttpServerType.ENCRYPTED_HTTP;
import static com.renomad.minum.web.HttpServerType.PLAIN_TEXT_HTTP;

/**
 * This class contains the basic internet capabilities.
 * 

* Think of this class as managing some of the lowest-level internet * communications we need to handle to support a web application. Sockets, * Servers, Threads, that kind of stuff. */ final class WebEngine { private final ITheBrig theBrig; private final Constants constants; private final Context context; private final ExecutorService executorService; private final WebFramework webFramework; WebEngine(Context context, WebFramework webFramework) { this.logger = context.getLogger(); this.logger.logDebug(() -> "Using a supplied logger in WebEngine"); this.theBrig = context.getFullSystem() != null ? context.getFullSystem().getTheBrig() : null; this.constants = context.getConstants(); this.context = context; this.executorService = context.getExecutorService(); this.webFramework = webFramework; } private final ILogger logger; static final String HTTP_CRLF = "\r\n"; IServer startServer() { int port = constants.serverPort; ServerSocket ss; try { ss = new ServerSocket(port); } catch (Exception e) { throw new WebServerException(e); } logger.logDebug(() -> String.format("Just created a new ServerSocket: %s", ss)); IServer server = new Server(ss, context, "http server", theBrig, webFramework, executorService, PLAIN_TEXT_HTTP); logger.logDebug(() -> String.format("Just created a new Server: %s", server)); server.start(); String hostname = constants.hostName; logger.logDebug(() -> String.format("%s started at http://%s:%s", server, hostname, port)); return server; } IServer startSslServer() { /* * If we find the keystore and pass in the system properties */ final var useExternalKeystore = isProvidedKeystoreProperties(constants.keystorePath, constants.keystorePassword, logger); KeyStoreResult keystoreResult = getKeyStoreResult(useExternalKeystore, constants.keystorePath, constants.keystorePassword, logger); int port = constants.secureServerPort; ServerSocket ss = createSslSocketWithSpecificKeystore(port, keystoreResult.keystoreUrl(), keystoreResult.keystorePassword()); logger.logDebug(() -> String.format("Just created a new ServerSocket: %s", ss)); IServer server = new Server(ss, context, "https server", theBrig, webFramework, executorService, ENCRYPTED_HTTP); logger.logDebug(() -> String.format("Just created a new SSL Server: %s", server)); server.start(); String hostname = constants.hostName; logger.logDebug(() -> String.format("%s started at https://%s:%s", server, hostname, port)); return server; } static KeyStoreResult getKeyStoreResult( boolean useExternalKeystore, String keystorePath, String keystorePassword, ILogger logger) { if (useExternalKeystore) { logger.logDebug(() -> "Using keystore and password referenced in minum.config"); } else { logger.logDebug(() -> "Using the default (self-signed / testing-only) certificate"); } final URL keystoreUrl; try { keystoreUrl = useExternalKeystore ? Path.of(keystorePath).toUri().toURL() : WebEngine.class.getResource("/certs/keystore"); } catch (Exception e) { throw new WebServerException("Error while building keystoreUrl: " + e); } final String keystorePasswordFinal = useExternalKeystore ? keystorePassword : "passphrase"; return new KeyStoreResult(keystoreUrl, keystorePasswordFinal); } record KeyStoreResult(URL keystoreUrl, String keystorePassword) { } /** * Look into the system properties to see whether values have been * set for the keystore and keystorePassword keys. *

* the key for keystore is: javax.net.ssl.keyStore * the key for keystorePassword is: javax.net.ssl.keyStorePassword *

* It's smart, if you are creating a server that will run * with a genuine signed certificate, to have those files * stored somewhere and then set these system properties. That * way, it's a characteristic of a particular server - it's not * needed to bundle the certificate with the actual server in * any way. *

* We *do* bundle a cert, but it's for testing and is self-signed. */ static Boolean isProvidedKeystoreProperties(String keystorePath, String keystorePassword, ILogger logger) { // get the directory to the keystore from a system property boolean hasKeystore = ! (keystorePath == null || keystorePath.isBlank()); if (! hasKeystore) { logger.logDebug(() -> "Keystore system property was not set"); } // get the password to that keystore from a system property boolean hasKeystorePassword = ! (keystorePassword == null || keystorePassword.isBlank()); if (! hasKeystorePassword) { logger.logDebug(() -> "keystorePassword system property was not set"); } return hasKeystore && hasKeystorePassword; } /** * Create an SSL Socket using a specified keystore */ ServerSocket createSslSocketWithSpecificKeystore(int sslPort, URL keystoreUrl, String keystorePassword) { try (InputStream keystoreInputStream = keystoreUrl.openStream()) { final var keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); char[] passwordCharArray = keystorePassword.toCharArray(); keyStore.load(keystoreInputStream, passwordCharArray); final var keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, passwordCharArray); final var keyManagers = keyManagerFactory.getKeyManagers(); final var sslContext = SSLContext.getInstance("TLSv1.3"); sslContext.init(keyManagers, null, new SecureRandom()); final var socketFactory = sslContext.getServerSocketFactory(); return socketFactory.createServerSocket(sslPort); } catch (Exception ex) { logger.logDebug(ex::getMessage); throw new WebServerException(ex); } } /** * Create a client {@link ISocketWrapper} connected to the running host server */ ISocketWrapper startClient(Socket socket) throws IOException { logger.logDebug(() -> String.format("Just created new client socket: %s", socket)); return new SocketWrapper(socket, null, logger, constants.socketTimeoutMillis, constants.hostName); } /** * Intentionally return just the default object toString, this is only used * to differentiate between multiple instances in memory. */ @Override public String toString() { return super.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy