1 | package com.renomad.minum.web; | |
2 | ||
3 | import com.renomad.minum.logging.ILogger; | |
4 | import com.renomad.minum.state.Constants; | |
5 | import com.renomad.minum.state.Context; | |
6 | import com.renomad.minum.utils.ConcurrentSet; | |
7 | import com.renomad.minum.utils.StacktraceUtils; | |
8 | import com.renomad.minum.utils.ThrowingRunnable; | |
9 | ||
10 | import java.io.IOException; | |
11 | import java.net.ServerSocket; | |
12 | import java.net.Socket; | |
13 | import java.util.concurrent.BlockingQueue; | |
14 | import java.util.concurrent.ExecutorService; | |
15 | import java.util.concurrent.Future; | |
16 | import java.util.concurrent.LinkedBlockingQueue; | |
17 | ||
18 | import static com.renomad.minum.utils.ThrowingRunnable.throwingRunnableWrapper; | |
19 | ||
20 | /** | |
21 | * The purpose here is to make it marginally easier to | |
22 | * work with a ServerSocket. | |
23 | * <p> | |
24 | * First, instantiate this class using a running serverSocket | |
25 | * Then, by running the start method, we gain access to | |
26 | * the server's socket. This way we can easily test / control | |
27 | * the server side but also tie it in with an ExecutorService | |
28 | * for controlling lots of server threads. | |
29 | */ | |
30 | final class Server implements IServer { | |
31 | private final ServerSocket serverSocket; | |
32 | private final SetOfSws setOfSWs; | |
33 | private final ExecutorService es; | |
34 | private final HttpServerType serverType; | |
35 | private final ILogger logger; | |
36 | private final String serverName; | |
37 | private final WebFramework webFramework; | |
38 | private final BlockingQueue<Socket> socketQueue; | |
39 | private final Constants constants; | |
40 | ||
41 | /** | |
42 | * This is the future returned when we submitted the | |
43 | * thread for the central server loop to the ExecutorService | |
44 | */ | |
45 | private Future<?> centralLoopFuture; | |
46 | ||
47 | Server(ServerSocket ss, Context context, String serverName, WebFramework webFramework, ExecutorService es, HttpServerType serverType) { | |
48 | this.serverSocket = ss; | |
49 | this.logger = context.getLogger(); | |
50 | this.constants = context.getConstants(); | |
51 | this.webFramework = webFramework; | |
52 | this.serverName = serverName; | |
53 | setOfSWs = new SetOfSws(new ConcurrentSet<>(), logger, serverName); | |
54 | this.es = es; | |
55 | this.serverType = serverType; | |
56 | this.socketQueue = new LinkedBlockingQueue<>(); | |
57 | } | |
58 | ||
59 | @Override | |
60 | public void start() { | |
61 | ThrowingRunnable serverCode = this::outermostLoop; | |
62 | this.centralLoopFuture = es.submit(throwingRunnableWrapper(serverCode, logger)); | |
63 | ||
64 | ThrowingRunnable socketHandler = this::takeOffDequeForProcessing; | |
65 | es.submit(throwingRunnableWrapper(socketHandler, logger)); | |
66 | } | |
67 | ||
68 | /** | |
69 | * This code is the outermost loop of the server, waiting for incoming | |
70 | * connections and then delegating their handling off to a handler. | |
71 | */ | |
72 | private void outermostLoop() { | |
73 | Thread.currentThread().setName("Main Server"); | |
74 | try { | |
75 | // yes, this infinite loop can only exit by an exception. But this is | |
76 | // the beating heart of a server, and to the best of my current knowledge, | |
77 | // when a server socket is force-closed it's going to throw an exception, and | |
78 | // that's just part of its life cycle | |
79 | //noinspection InfiniteLoopStatement | |
80 | while (true) { | |
81 | Socket freshSocket = serverSocket.accept(); | |
82 | // see takeOffDeque for the code that pulls sockets out of this queue | |
83 | // and sends them for processing | |
84 | socketQueue.add(freshSocket); | |
85 | } | |
86 | } catch (IOException ex) { | |
87 |
1
1. outermostLoop : removed call to com/renomad/minum/web/Server::handleServerException → KILLED |
handleServerException(ex, logger); |
88 | } | |
89 | } | |
90 | ||
91 | /** | |
92 | * An infinite loop that pulls connected sockets out of the | |
93 | * deque for processing | |
94 | */ | |
95 | private void takeOffDequeForProcessing() throws InterruptedException { | |
96 | Thread.currentThread().setName("socket queue handler"); | |
97 | ||
98 | // this is a known infinite loop, meant to keep running all during the runtime | |
99 | //noinspection InfiniteLoopStatement | |
100 | while(true) { | |
101 | Socket socket = socketQueue.take(); | |
102 |
1
1. lambda$takeOffDequeForProcessing$0 : removed call to com/renomad/minum/web/Server::doHttpWork → KILLED |
es.submit(() -> this.doHttpWork(socket)); |
103 | } | |
104 | } | |
105 | ||
106 | ||
107 | void doHttpWork(Socket freshSocket) { | |
108 | // provide a name for this thread for easier debugging | |
109 | Thread.currentThread().setName("SocketWrapper thread for " + freshSocket.getInetAddress().getHostAddress()); | |
110 | ||
111 | try { | |
112 | // prepare the socket for later processing | |
113 | ISocketWrapper socketWrapper = new SocketWrapper(freshSocket, this, logger, constants.socketTimeoutMillis, constants.hostName); | |
114 | logger.logTrace(() -> String.format("client connected from %s", socketWrapper.getRemoteAddrWithPort())); | |
115 | ||
116 | // add to a set of wrapped sockets so we can precisely close them all at shutdown | |
117 |
1
1. doHttpWork : removed call to com/renomad/minum/web/Server::addToSetOfSws → KILLED |
addToSetOfSws(socketWrapper); |
118 | ||
119 |
1
1. doHttpWork : removed call to com/renomad/minum/web/WebFramework::httpProcessing → KILLED |
webFramework.httpProcessing(socketWrapper); |
120 | } catch (Exception ex) { | |
121 | logger.logAsyncError(() -> StacktraceUtils.stackTraceToString(ex)); | |
122 | } | |
123 | } | |
124 | ||
125 | static void handleServerException(IOException ex, ILogger logger) { | |
126 |
2
1. handleServerException : negated conditional → KILLED 2. handleServerException : negated conditional → KILLED |
if (!(ex.getMessage().contains("Socket closed") || ex.getMessage().contains("Socket is closed"))) { |
127 | logger.logAsyncError(() -> StacktraceUtils.stackTraceToString(ex)); | |
128 | } | |
129 | } | |
130 | ||
131 | @Override | |
132 | public void close() throws IOException { | |
133 | // close all the running sockets | |
134 |
1
1. close : removed call to com/renomad/minum/web/SetOfSws::stopAllServers → KILLED |
setOfSWs.stopAllServers(); |
135 | logger.logTrace(() -> "close called on " + this); | |
136 | // close the primary server socket | |
137 |
1
1. close : removed call to java/net/ServerSocket::close → KILLED |
serverSocket.close(); |
138 | } | |
139 | ||
140 | @Override | |
141 | public String getHost() { | |
142 |
1
1. getHost : replaced return value with "" for com/renomad/minum/web/Server::getHost → KILLED |
return serverSocket.getInetAddress().getHostAddress(); |
143 | } | |
144 | ||
145 | @Override | |
146 | public int getPort() { | |
147 |
1
1. getPort : replaced int return with 0 for com/renomad/minum/web/Server::getPort → KILLED |
return serverSocket.getLocalPort(); |
148 | } | |
149 | ||
150 | @Override | |
151 | public void removeMyRecord(ISocketWrapper socketWrapper) { | |
152 |
1
1. removeMyRecord : removed call to com/renomad/minum/web/SetOfSws::remove → KILLED |
setOfSWs.remove(socketWrapper); |
153 | } | |
154 | ||
155 | @Override | |
156 | public void addToSetOfSws(ISocketWrapper sw) { | |
157 |
1
1. addToSetOfSws : removed call to com/renomad/minum/web/SetOfSws::add → KILLED |
this.setOfSWs.add(sw); |
158 | } | |
159 | ||
160 | /** | |
161 | * Returns the name of this server, which is set | |
162 | * when the server is instantiated. | |
163 | */ | |
164 | @Override | |
165 | public String toString() { | |
166 |
1
1. toString : replaced return value with "" for com/renomad/minum/web/Server::toString → KILLED |
return this.serverName; |
167 | } | |
168 | ||
169 | @Override | |
170 | public Future<?> getCentralLoopFuture() { | |
171 |
1
1. getCentralLoopFuture : replaced return value with null for com/renomad/minum/web/Server::getCentralLoopFuture → KILLED |
return centralLoopFuture; |
172 | } | |
173 | ||
174 | @Override | |
175 | public HttpServerType getServerType() { | |
176 |
1
1. getServerType : replaced return value with null for com/renomad/minum/web/Server::getServerType → KILLED |
return serverType; |
177 | } | |
178 | } | |
Mutations | ||
87 |
1.1 |
|
102 |
1.1 |
|
117 |
1.1 |
|
119 |
1.1 |
|
126 |
1.1 2.2 |
|
134 |
1.1 |
|
137 |
1.1 |
|
142 |
1.1 |
|
147 |
1.1 |
|
152 |
1.1 |
|
157 |
1.1 |
|
166 |
1.1 |
|
171 |
1.1 |
|
176 |
1.1 |