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 | import com.renomad.minum.utils.ConcurrentSet; | |
8 | import com.renomad.minum.utils.StacktraceUtils; | |
9 | import com.renomad.minum.utils.ThrowingRunnable; | |
10 | ||
11 | import java.io.IOException; | |
12 | import java.net.ServerSocket; | |
13 | import java.net.Socket; | |
14 | import java.util.concurrent.ExecutorService; | |
15 | import java.util.concurrent.Future; | |
16 | ||
17 | /** | |
18 | * The purpose here is to make it marginally easier to | |
19 | * work with a ServerSocket. | |
20 | * <p> | |
21 | * First, instantiate this class using a running serverSocket | |
22 | * Then, by running the start method, we gain access to | |
23 | * the server's socket. This way we can easily test / control | |
24 | * the server side but also tie it in with an ExecutorService | |
25 | * for controlling lots of server threads. | |
26 | */ | |
27 | final class Server implements IServer { | |
28 | private final ServerSocket serverSocket; | |
29 | private final SetOfSws setOfSWs; | |
30 | private final ExecutorService es; | |
31 | private final HttpServerType serverType; | |
32 | private final ILogger logger; | |
33 | private final String serverName; | |
34 | private final ITheBrig theBrig; | |
35 | private final Constants constants; | |
36 | private final WebFramework webFramework; | |
37 | ||
38 | /** | |
39 | * This is the future returned when we submitted the | |
40 | * thread for the central server loop to the ExecutorService | |
41 | */ | |
42 | private Future<?> centralLoopFuture; | |
43 | ||
44 | Server(ServerSocket ss, Context context, String serverName, ITheBrig theBrig, WebFramework webFramework, ExecutorService es, HttpServerType serverType) { | |
45 | this.serverSocket = ss; | |
46 | this.logger = context.getLogger(); | |
47 | this.constants = context.getConstants(); | |
48 | this.webFramework = webFramework; | |
49 | this.serverName = serverName; | |
50 | this.theBrig = theBrig; | |
51 | setOfSWs = new SetOfSws(new ConcurrentSet<>(), logger, serverName); | |
52 | this.es = es; | |
53 | this.serverType = serverType; | |
54 | } | |
55 | ||
56 | @Override | |
57 | public void start() { | |
58 | ThrowingRunnable serverCode = buildMainServerLoop(es); | |
59 | Runnable t = ThrowingRunnable.throwingRunnableWrapper(serverCode, logger); | |
60 | this.centralLoopFuture = es.submit(t); | |
61 | } | |
62 | ||
63 | /** | |
64 | * This code is the innermost loop of the server, waiting for incoming | |
65 | * connections and then delegating their handling off to a handler. | |
66 | * | |
67 | * @param es The ExecutorService helping us with the threads | |
68 | */ | |
69 | private ThrowingRunnable buildMainServerLoop(ExecutorService es) { | |
70 |
1
1. buildMainServerLoop : replaced return value with null for com/renomad/minum/web/Server::buildMainServerLoop → KILLED |
return () -> { |
71 | Thread.currentThread().setName("Main Server"); | |
72 | try { | |
73 | // yes, this infinite loop can only exit by an exception. But this is | |
74 | // the beating heart of a server, and to the best of my current knowledge, | |
75 | // when a server socket is force-closed it's going to throw an exception, and | |
76 | // that's just part of its life cycle | |
77 | //noinspection InfiniteLoopStatement | |
78 | while (true) { | |
79 | logger.logTrace(() -> serverName + " waiting to accept connection"); | |
80 | Socket freshSocket = serverSocket.accept(); | |
81 | ISocketWrapper sw = new SocketWrapper(freshSocket, this, logger, constants.socketTimeoutMillis, constants.hostName); | |
82 | logger.logTrace(() -> String.format("client connected from %s", sw.getRemoteAddrWithPort())); | |
83 |
1
1. lambda$buildMainServerLoop$2 : removed call to com/renomad/minum/web/SetOfSws::add → TIMED_OUT |
setOfSWs.add(sw); |
84 | ThrowingRunnable innerServerCode = this.webFramework.makePrimaryHttpHandler(sw, theBrig); | |
85 | Runnable task = ThrowingRunnable.throwingRunnableWrapper(innerServerCode, logger); | |
86 | es.submit(task); | |
87 | } | |
88 | } catch (IOException ex) { | |
89 |
1
1. lambda$buildMainServerLoop$2 : removed call to com/renomad/minum/web/Server::handleServerException → TIMED_OUT |
handleServerException(ex, logger); |
90 | } | |
91 | }; | |
92 | } | |
93 | ||
94 | static void handleServerException(IOException ex, ILogger logger) { | |
95 |
2
1. handleServerException : negated conditional → KILLED 2. handleServerException : negated conditional → KILLED |
if (!(ex.getMessage().contains("Socket closed") || ex.getMessage().contains("Socket is closed"))) { |
96 | logger.logAsyncError(() -> StacktraceUtils.stackTraceToString(ex)); | |
97 | } | |
98 | } | |
99 | ||
100 | @Override | |
101 | public void close() throws IOException { | |
102 | // close all the running sockets | |
103 |
1
1. close : removed call to com/renomad/minum/web/SetOfSws::stopAllServers → TIMED_OUT |
setOfSWs.stopAllServers(); |
104 | logger.logTrace(() -> "close called on " + this); | |
105 | // close the primary server socket | |
106 |
1
1. close : removed call to java/net/ServerSocket::close → TIMED_OUT |
serverSocket.close(); |
107 | } | |
108 | ||
109 | @Override | |
110 | public String getHost() { | |
111 |
1
1. getHost : replaced return value with "" for com/renomad/minum/web/Server::getHost → TIMED_OUT |
return serverSocket.getInetAddress().getHostAddress(); |
112 | } | |
113 | ||
114 | @Override | |
115 | public int getPort() { | |
116 |
1
1. getPort : replaced int return with 0 for com/renomad/minum/web/Server::getPort → KILLED |
return serverSocket.getLocalPort(); |
117 | } | |
118 | ||
119 | @Override | |
120 | public void removeMyRecord(ISocketWrapper socketWrapper) { | |
121 |
1
1. removeMyRecord : removed call to com/renomad/minum/web/SetOfSws::remove → KILLED |
setOfSWs.remove(socketWrapper); |
122 | } | |
123 | ||
124 | /** | |
125 | * Returns the name of this server, which is set | |
126 | * when the server is instantiated. | |
127 | */ | |
128 | @Override | |
129 | public String toString() { | |
130 |
1
1. toString : replaced return value with "" for com/renomad/minum/web/Server::toString → TIMED_OUT |
return this.serverName; |
131 | } | |
132 | ||
133 | @Override | |
134 | public Future<?> getCentralLoopFuture() { | |
135 |
1
1. getCentralLoopFuture : replaced return value with null for com/renomad/minum/web/Server::getCentralLoopFuture → KILLED |
return centralLoopFuture; |
136 | } | |
137 | ||
138 | @Override | |
139 | public HttpServerType getServerType() { | |
140 |
1
1. getServerType : replaced return value with null for com/renomad/minum/web/Server::getServerType → KILLED |
return serverType; |
141 | } | |
142 | } | |
Mutations | ||
70 |
1.1 |
|
83 |
1.1 |
|
89 |
1.1 |
|
95 |
1.1 2.2 |
|
103 |
1.1 |
|
106 |
1.1 |
|
111 |
1.1 |
|
116 |
1.1 |
|
121 |
1.1 |
|
130 |
1.1 |
|
135 |
1.1 |
|
140 |
1.1 |