1 | package com.renomad.minum.web; | |
2 | ||
3 | import com.renomad.minum.state.Constants; | |
4 | import com.renomad.minum.logging.ILogger; | |
5 | import com.renomad.minum.security.ITheBrig; | |
6 | import com.renomad.minum.state.Context; | |
7 | ||
8 | import javax.net.ssl.KeyManagerFactory; | |
9 | import javax.net.ssl.SSLContext; | |
10 | import java.io.IOException; | |
11 | import java.io.InputStream; | |
12 | import java.net.ServerSocket; | |
13 | import java.net.Socket; | |
14 | import java.net.URL; | |
15 | import java.nio.file.Path; | |
16 | import java.security.*; | |
17 | import java.util.concurrent.ExecutorService; | |
18 | ||
19 | import static com.renomad.minum.web.HttpServerType.ENCRYPTED_HTTP; | |
20 | import static com.renomad.minum.web.HttpServerType.PLAIN_TEXT_HTTP; | |
21 | ||
22 | /** | |
23 | * This class contains the basic internet capabilities. | |
24 | * <br><br> | |
25 | * Think of this class as managing some of the lowest-level internet | |
26 | * communications we need to handle to support a web application. Sockets, | |
27 | * Servers, Threads, that kind of stuff. | |
28 | */ | |
29 | final class WebEngine { | |
30 | ||
31 | private final ITheBrig theBrig; | |
32 | private final Constants constants; | |
33 | private final Context context; | |
34 | private final ExecutorService executorService; | |
35 | private final WebFramework webFramework; | |
36 | ||
37 | WebEngine(Context context, WebFramework webFramework) { | |
38 | this.logger = context.getLogger(); | |
39 | this.logger.logDebug(() -> "Using a supplied logger in WebEngine"); | |
40 |
1
1. <init> : negated conditional → KILLED |
this.theBrig = context.getFullSystem() != null ? context.getFullSystem().getTheBrig() : null; |
41 | this.constants = context.getConstants(); | |
42 | this.context = context; | |
43 | this.executorService = context.getExecutorService(); | |
44 | this.webFramework = webFramework; | |
45 | } | |
46 | ||
47 | private final ILogger logger; | |
48 | static final String HTTP_CRLF = "\r\n"; | |
49 | ||
50 | IServer startServer() { | |
51 | int port = constants.serverPort; | |
52 | ServerSocket ss; | |
53 | try { | |
54 | ss = new ServerSocket(port); | |
55 | } catch (Exception e) { | |
56 | throw new WebServerException(e); | |
57 | } | |
58 | logger.logDebug(() -> String.format("Just created a new ServerSocket: %s", ss)); | |
59 | IServer server = new Server(ss, context, "http server", theBrig, webFramework, executorService, PLAIN_TEXT_HTTP); | |
60 | logger.logDebug(() -> String.format("Just created a new Server: %s", server)); | |
61 |
1
1. startServer : removed call to com/renomad/minum/web/IServer::start → KILLED |
server.start(); |
62 | String hostname = constants.hostName; | |
63 | logger.logDebug(() -> String.format("%s started at http://%s:%s", server, hostname, port)); | |
64 |
1
1. startServer : replaced return value with null for com/renomad/minum/web/WebEngine::startServer → TIMED_OUT |
return server; |
65 | } | |
66 | ||
67 | IServer startSslServer() { | |
68 | ||
69 | /* | |
70 | * If we find the keystore and pass in the system properties | |
71 | */ | |
72 | final var useExternalKeystore = isProvidedKeystoreProperties(constants.keystorePath, constants.keystorePassword, logger); | |
73 | KeyStoreResult keystoreResult = getKeyStoreResult(useExternalKeystore, constants.keystorePath, constants.keystorePassword, logger); | |
74 | ||
75 | int port = constants.secureServerPort; | |
76 | ServerSocket ss = createSslSocketWithSpecificKeystore(port, keystoreResult.keystoreUrl(), keystoreResult.keystorePassword()); | |
77 | logger.logDebug(() -> String.format("Just created a new ServerSocket: %s", ss)); | |
78 | IServer server = new Server(ss, context, "https server", theBrig, webFramework, executorService, ENCRYPTED_HTTP); | |
79 | logger.logDebug(() -> String.format("Just created a new SSL Server: %s", server)); | |
80 |
1
1. startSslServer : removed call to com/renomad/minum/web/IServer::start → KILLED |
server.start(); |
81 | String hostname = constants.hostName; | |
82 | logger.logDebug(() -> String.format("%s started at https://%s:%s", server, hostname, port)); | |
83 |
1
1. startSslServer : replaced return value with null for com/renomad/minum/web/WebEngine::startSslServer → KILLED |
return server; |
84 | } | |
85 | ||
86 | static KeyStoreResult getKeyStoreResult( | |
87 | boolean useExternalKeystore, | |
88 | String keystorePath, | |
89 | String keystorePassword, | |
90 | ILogger logger) { | |
91 |
1
1. getKeyStoreResult : negated conditional → SURVIVED |
if (useExternalKeystore) { |
92 | logger.logDebug(() -> "Using keystore and password referenced in minum.config"); | |
93 | } else { | |
94 | logger.logDebug(() -> "Using the default (self-signed / testing-only) certificate"); | |
95 | } | |
96 | ||
97 | final URL keystoreUrl; | |
98 | try { | |
99 |
1
1. getKeyStoreResult : negated conditional → KILLED |
keystoreUrl = useExternalKeystore ? |
100 | Path.of(keystorePath).toUri().toURL() : | |
101 | WebEngine.class.getResource("/certs/keystore"); | |
102 | } catch (Exception e) { | |
103 | throw new WebServerException("Error while building keystoreUrl: " + e); | |
104 | } | |
105 |
1
1. getKeyStoreResult : negated conditional → KILLED |
final String keystorePasswordFinal = useExternalKeystore ? |
106 | keystorePassword : | |
107 | "passphrase"; | |
108 |
1
1. getKeyStoreResult : replaced return value with null for com/renomad/minum/web/WebEngine::getKeyStoreResult → KILLED |
return new KeyStoreResult(keystoreUrl, keystorePasswordFinal); |
109 | } | |
110 | ||
111 | record KeyStoreResult(URL keystoreUrl, String keystorePassword) { } | |
112 | ||
113 | ||
114 | /** | |
115 | * Look into the system properties to see whether values have been | |
116 | * set for the keystore and keystorePassword keys. | |
117 | * <p> | |
118 | * the key for keystore is: javax.net.ssl.keyStore | |
119 | * the key for keystorePassword is: javax.net.ssl.keyStorePassword | |
120 | * <p> | |
121 | * It's smart, if you are creating a server that will run | |
122 | * with a genuine signed certificate, to have those files | |
123 | * stored somewhere and then set these system properties. That | |
124 | * way, it's a characteristic of a particular server - it's not | |
125 | * needed to bundle the certificate with the actual server in | |
126 | * any way. | |
127 | * <p> | |
128 | * We *do* bundle a cert, but it's for testing and is self-signed. | |
129 | */ | |
130 | static Boolean isProvidedKeystoreProperties(String keystorePath, String keystorePassword, ILogger logger) { | |
131 | ||
132 | // get the directory to the keystore from a system property | |
133 |
2
1. isProvidedKeystoreProperties : negated conditional → KILLED 2. isProvidedKeystoreProperties : negated conditional → KILLED |
boolean hasKeystore = ! (keystorePath == null || keystorePath.isBlank()); |
134 |
1
1. isProvidedKeystoreProperties : negated conditional → KILLED |
if (! hasKeystore) { |
135 | logger.logDebug(() -> "Keystore system property was not set"); | |
136 | } | |
137 | ||
138 | // get the password to that keystore from a system property | |
139 |
2
1. isProvidedKeystoreProperties : negated conditional → KILLED 2. isProvidedKeystoreProperties : negated conditional → KILLED |
boolean hasKeystorePassword = ! (keystorePassword == null || keystorePassword.isBlank()); |
140 |
1
1. isProvidedKeystoreProperties : negated conditional → KILLED |
if (! hasKeystorePassword) { |
141 | logger.logDebug(() -> "keystorePassword system property was not set"); | |
142 | } | |
143 | ||
144 |
3
1. isProvidedKeystoreProperties : replaced Boolean return with True for com/renomad/minum/web/WebEngine::isProvidedKeystoreProperties → KILLED 2. isProvidedKeystoreProperties : negated conditional → KILLED 3. isProvidedKeystoreProperties : negated conditional → KILLED |
return hasKeystore && hasKeystorePassword; |
145 | } | |
146 | ||
147 | ||
148 | /** | |
149 | * Create an SSL Socket using a specified keystore | |
150 | */ | |
151 | ServerSocket createSslSocketWithSpecificKeystore(int sslPort, URL keystoreUrl, String keystorePassword) { | |
152 | try (InputStream keystoreInputStream = keystoreUrl.openStream()) { | |
153 | final var keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | |
154 | char[] passwordCharArray = keystorePassword.toCharArray(); | |
155 |
1
1. createSslSocketWithSpecificKeystore : removed call to java/security/KeyStore::load → KILLED |
keyStore.load(keystoreInputStream, passwordCharArray); |
156 | ||
157 | final var keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); | |
158 |
1
1. createSslSocketWithSpecificKeystore : removed call to javax/net/ssl/KeyManagerFactory::init → KILLED |
keyManagerFactory.init(keyStore, passwordCharArray); |
159 | ||
160 | final var keyManagers = keyManagerFactory.getKeyManagers(); | |
161 | ||
162 | final var sslContext = SSLContext.getInstance("TLSv1.3"); | |
163 |
1
1. createSslSocketWithSpecificKeystore : removed call to javax/net/ssl/SSLContext::init → KILLED |
sslContext.init(keyManagers, null, new SecureRandom()); |
164 | ||
165 | final var socketFactory = sslContext.getServerSocketFactory(); | |
166 | return socketFactory.createServerSocket(sslPort); | |
167 | } catch (Exception ex) { | |
168 | logger.logDebug(ex::getMessage); | |
169 | throw new WebServerException(ex); | |
170 | } | |
171 | } | |
172 | ||
173 | /** | |
174 | * Create a client {@link ISocketWrapper} connected to the running host server | |
175 | */ | |
176 | ISocketWrapper startClient(Socket socket) throws IOException { | |
177 | logger.logDebug(() -> String.format("Just created new client socket: %s", socket)); | |
178 |
1
1. startClient : replaced return value with null for com/renomad/minum/web/WebEngine::startClient → KILLED |
return new SocketWrapper(socket, null, logger, constants.socketTimeoutMillis, constants.hostName); |
179 | } | |
180 | ||
181 | /** | |
182 | * Intentionally return just the default object toString, this is only used | |
183 | * to differentiate between multiple instances in memory. | |
184 | */ | |
185 | @Override | |
186 | public String toString() { | |
187 |
1
1. toString : replaced return value with "" for com/renomad/minum/web/WebEngine::toString → KILLED |
return super.toString(); |
188 | } | |
189 | ||
190 | } | |
Mutations | ||
40 |
1.1 |
|
61 |
1.1 |
|
64 |
1.1 |
|
80 |
1.1 |
|
83 |
1.1 |
|
91 |
1.1 |
|
99 |
1.1 |
|
105 |
1.1 |
|
108 |
1.1 |
|
133 |
1.1 2.2 |
|
134 |
1.1 |
|
139 |
1.1 2.2 |
|
140 |
1.1 |
|
144 |
1.1 2.2 3.3 |
|
155 |
1.1 |
|
158 |
1.1 |
|
163 |
1.1 |
|
178 |
1.1 |
|
187 |
1.1 |