Skip to content

Commit f4c6adc

Browse files
committed
Rewrite integrated webserver to be non-blocking
1 parent 9917e5d commit f4c6adc

25 files changed

+837
-978
lines changed

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/config/WebserverConfig.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ public class WebserverConfig {
4545

4646
private int port = 8100;
4747

48-
private int maxConnectionCount = 100;
49-
5048
public boolean isEnabled() {
5149
return enabled;
5250
}
@@ -73,8 +71,4 @@ public int getPort() {
7371
return port;
7472
}
7573

76-
public int getMaxConnectionCount() {
77-
return maxConnectionCount;
78-
}
79-
8074
}

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/plugin/Plugin.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@
3737
import de.bluecolored.bluemap.common.rendermanager.RenderManager;
3838
import de.bluecolored.bluemap.common.serverinterface.ServerEventListener;
3939
import de.bluecolored.bluemap.common.serverinterface.ServerInterface;
40-
import de.bluecolored.bluemap.common.web.FileRequestHandler;
41-
import de.bluecolored.bluemap.common.web.MapRequestHandler;
42-
import de.bluecolored.bluemap.common.web.RoutingRequestHandler;
43-
import de.bluecolored.bluemap.common.webserver.WebServer;
40+
import de.bluecolored.bluemap.common.web.*;
4441
import de.bluecolored.bluemap.core.debug.StateDumper;
4542
import de.bluecolored.bluemap.core.logger.Logger;
4643
import de.bluecolored.bluemap.core.map.BmMap;
@@ -57,6 +54,7 @@
5754
import java.io.OutputStream;
5855
import java.io.OutputStreamWriter;
5956
import java.io.Writer;
57+
import java.net.InetSocketAddress;
6058
import java.net.UnknownHostException;
6159
import java.nio.file.Path;
6260
import java.util.*;
@@ -189,23 +187,26 @@ private void load(@Nullable ResourcePack preloadedResourcePack) throws IOExcepti
189187
routingRequestHandler.register(
190188
"maps/" + Pattern.quote(id) + "/(.*)",
191189
"$1",
192-
mapRequestHandler
190+
new BlueMapResponseModifier(mapRequestHandler)
193191
);
194192
}
195193

194+
webServer = new WebServer(routingRequestHandler);
195+
webServer.start();
196+
196197
try {
197-
webServer = new WebServer(
198+
webServer.bind(new InetSocketAddress(
198199
webserverConfig.resolveIp(),
199-
webserverConfig.getPort(),
200-
webserverConfig.getMaxConnectionCount(),
201-
routingRequestHandler,
202-
false
203-
);
200+
webserverConfig.getPort()
201+
));
204202
} catch (UnknownHostException ex) {
205203
throw new ConfigurationException("BlueMap failed to resolve the ip in your webserver-config.\n" +
206204
"Check if that is correctly configured.", ex);
205+
} catch (IOException ex) {
206+
throw new ConfigurationException("BlueMap failed to initialize the webserver.\n" +
207+
"Check your webserver-config if everything is configured correctly.\n" +
208+
"(Make sure you DON'T use the same port for bluemap that you also use for your minecraft server)", ex);
207209
}
208-
webServer.start();
209210
}
210211

211212
//initialize render manager
@@ -376,7 +377,13 @@ public void unload() {
376377
}
377378
renderManager = null;
378379

379-
if (webServer != null) webServer.close();
380+
if (webServer != null) {
381+
try {
382+
webServer.close();
383+
} catch (IOException ex) {
384+
Logger.global.logError("Failed to close the webserver!", ex);
385+
}
386+
}
380387
webServer = null;
381388

382389
//close bluemap

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/BlueMapResponseModifier.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
*/
2525
package de.bluecolored.bluemap.common.web;
2626

27-
import de.bluecolored.bluemap.common.webserver.HttpRequest;
28-
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
29-
import de.bluecolored.bluemap.common.webserver.HttpResponse;
30-
import de.bluecolored.bluemap.common.webserver.HttpStatusCode;
27+
import de.bluecolored.bluemap.common.web.http.HttpRequest;
28+
import de.bluecolored.bluemap.common.web.http.HttpRequestHandler;
29+
import de.bluecolored.bluemap.common.web.http.HttpResponse;
30+
import de.bluecolored.bluemap.common.web.http.HttpStatusCode;
3131
import de.bluecolored.bluemap.core.BlueMap;
3232

3333
public class BlueMapResponseModifier implements HttpRequestHandler {
@@ -37,7 +37,7 @@ public class BlueMapResponseModifier implements HttpRequestHandler {
3737

3838
public BlueMapResponseModifier(HttpRequestHandler delegate) {
3939
this.delegate = delegate;
40-
this.serverName = "BlueMap " + BlueMap.VERSION + " " + BlueMap.GIT_HASH;
40+
this.serverName = "BlueMap/" + BlueMap.VERSION;
4141
}
4242

4343
@Override

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/FileRequestHandler.java

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,18 @@
2424
*/
2525
package de.bluecolored.bluemap.common.web;
2626

27-
import de.bluecolored.bluemap.common.webserver.HttpRequest;
28-
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
29-
import de.bluecolored.bluemap.common.webserver.HttpResponse;
30-
import de.bluecolored.bluemap.common.webserver.HttpStatusCode;
27+
import de.bluecolored.bluemap.common.web.http.*;
3128
import org.apache.commons.lang3.time.DateFormatUtils;
3229

3330
import java.io.File;
3431
import java.io.FileInputStream;
3532
import java.io.FileNotFoundException;
3633
import java.nio.file.InvalidPathException;
3734
import java.nio.file.Path;
38-
import java.util.*;
35+
import java.util.Calendar;
36+
import java.util.GregorianCalendar;
37+
import java.util.Locale;
38+
import java.util.TimeZone;
3939
import java.util.concurrent.TimeUnit;
4040

4141
public class FileRequestHandler implements HttpRequestHandler {
@@ -50,13 +50,9 @@ public FileRequestHandler(Path webRoot) {
5050

5151
@Override
5252
public HttpResponse handle(HttpRequest request) {
53-
if (
54-
!request.getMethod().equalsIgnoreCase("GET")
55-
) return new HttpResponse(HttpStatusCode.BAD_REQUEST);
56-
57-
HttpResponse response = generateResponse(request);
58-
59-
return response;
53+
if (!request.getMethod().equalsIgnoreCase("GET"))
54+
return new HttpResponse(HttpStatusCode.BAD_REQUEST);
55+
return generateResponse(request);
6056
}
6157

6258
private HttpResponse generateResponse(HttpRequest request) {
@@ -113,10 +109,10 @@ private HttpResponse generateResponse(HttpRequest request) {
113109

114110
// check modified
115111
long lastModified = file.lastModified();
116-
Set<String> modStringSet = request.getHeader("If-Modified-Since");
117-
if (!modStringSet.isEmpty()){
112+
HttpHeader modHeader = request.getHeader("If-Modified-Since");
113+
if (modHeader != null){
118114
try {
119-
long since = stringToTimestamp(modStringSet.iterator().next());
115+
long since = stringToTimestamp(modHeader.getValue());
120116
if (since + 1000 >= lastModified){
121117
return new HttpResponse(HttpStatusCode.NOT_MODIFIED);
122118
}
@@ -125,9 +121,9 @@ private HttpResponse generateResponse(HttpRequest request) {
125121

126122
//check ETag
127123
String eTag = Long.toHexString(file.length()) + Integer.toHexString(file.hashCode()) + Long.toHexString(lastModified);
128-
Set<String> etagStringSet = request.getHeader("If-None-Match");
129-
if (!etagStringSet.isEmpty()){
130-
if(etagStringSet.iterator().next().equals(eTag)) {
124+
HttpHeader etagHeader = request.getHeader("If-None-Match");
125+
if (etagHeader != null){
126+
if(etagHeader.getValue().equals(eTag)) {
131127
return new HttpResponse(HttpStatusCode.NOT_MODIFIED);
132128
}
133129
}

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/JsonDataRequestHandler.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
*/
2525
package de.bluecolored.bluemap.common.web;
2626

27-
import de.bluecolored.bluemap.common.webserver.HttpRequest;
28-
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
29-
import de.bluecolored.bluemap.common.webserver.HttpResponse;
30-
import de.bluecolored.bluemap.common.webserver.HttpStatusCode;
27+
import de.bluecolored.bluemap.common.web.http.HttpRequest;
28+
import de.bluecolored.bluemap.common.web.http.HttpRequestHandler;
29+
import de.bluecolored.bluemap.common.web.http.HttpResponse;
30+
import de.bluecolored.bluemap.common.web.http.HttpStatusCode;
3131
import de.bluecolored.bluemap.core.BlueMap;
3232

3333
import java.util.function.Supplier;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package de.bluecolored.bluemap.common.web;
2+
3+
import de.bluecolored.bluemap.common.web.http.HttpRequest;
4+
import de.bluecolored.bluemap.common.web.http.HttpRequestHandler;
5+
import de.bluecolored.bluemap.common.web.http.HttpResponse;
6+
import de.bluecolored.bluemap.core.logger.Logger;
7+
8+
public class LoggingRequestHandler implements HttpRequestHandler {
9+
10+
private final HttpRequestHandler delegate;
11+
private final Logger logger;
12+
13+
public LoggingRequestHandler(HttpRequestHandler delegate) {
14+
this(delegate, Logger.global);
15+
}
16+
17+
public LoggingRequestHandler(HttpRequestHandler delegate, Logger logger) {
18+
this.delegate = delegate;
19+
this.logger = logger;
20+
}
21+
22+
@Override
23+
public HttpResponse handle(HttpRequest request) {
24+
String log = request.getSource() + " \""
25+
+ request.getMethod()
26+
+ " " + request.getAddress()
27+
+ " " + request.getVersion()
28+
+ "\" ";
29+
30+
HttpResponse response = delegate.handle(request);
31+
32+
log += response.getStatusCode().toString();
33+
if (response.getStatusCode().getCode() < 400) {
34+
logger.logInfo(log);
35+
} else {
36+
logger.logWarning(log);
37+
}
38+
39+
return response;
40+
}
41+
42+
}

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/MapStorageRequestHandler.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626

2727
import com.flowpowered.math.vector.Vector2i;
2828
import de.bluecolored.bluemap.api.ContentTypeRegistry;
29-
import de.bluecolored.bluemap.common.webserver.HttpRequest;
30-
import de.bluecolored.bluemap.common.webserver.HttpRequestHandler;
31-
import de.bluecolored.bluemap.common.webserver.HttpResponse;
32-
import de.bluecolored.bluemap.common.webserver.HttpStatusCode;
29+
import de.bluecolored.bluemap.common.web.http.*;
3330
import de.bluecolored.bluemap.core.logger.Logger;
3431
import de.bluecolored.bluemap.core.map.BmMap;
35-
import de.bluecolored.bluemap.core.storage.*;
32+
import de.bluecolored.bluemap.core.storage.CompressedInputStream;
33+
import de.bluecolored.bluemap.core.storage.Compression;
34+
import de.bluecolored.bluemap.core.storage.Storage;
35+
import de.bluecolored.bluemap.core.storage.TileInfo;
3636
import org.apache.commons.io.IOUtils;
3737
import org.apache.commons.lang3.time.DateFormatUtils;
3838

@@ -82,19 +82,19 @@ public HttpResponse handle(HttpRequest request) {
8282

8383
// check e-tag
8484
String eTag = calculateETag(path, tileInfo);
85-
Set<String> etagStringSet = request.getHeader("If-None-Match");
86-
if (!etagStringSet.isEmpty()){
87-
if(etagStringSet.iterator().next().equals(eTag)) {
85+
HttpHeader etagHeader = request.getHeader("If-None-Match");
86+
if (etagHeader != null){
87+
if(etagHeader.getValue().equals(eTag)) {
8888
return new HttpResponse(HttpStatusCode.NOT_MODIFIED);
8989
}
9090
}
9191

9292
// check modified-since
9393
long lastModified = tileInfo.getLastModified();
94-
Set<String> modStringSet = request.getHeader("If-Modified-Since");
95-
if (!modStringSet.isEmpty()){
94+
HttpHeader modHeader = request.getHeader("If-Modified-Since");
95+
if (modHeader != null){
9696
try {
97-
long since = stringToTimestamp(modStringSet.iterator().next());
97+
long since = stringToTimestamp(modHeader.getValue());
9898
if (since + 1000 >= lastModified){
9999
return new HttpResponse(HttpStatusCode.NOT_MODIFIED);
100100
}
@@ -144,14 +144,14 @@ private void writeToResponse(CompressedInputStream data, HttpResponse response,
144144
Compression compression = data.getCompression();
145145
if (
146146
compression != Compression.NONE &&
147-
request.getLowercaseHeader("Accept-Encoding").contains(compression.getTypeId())
147+
request.hasHeaderValue("Accept-Encoding", compression.getTypeId())
148148
) {
149149
response.addHeader("Content-Encoding", compression.getTypeId());
150150
response.setData(data);
151151
} else if (
152152
compression != Compression.GZIP &&
153-
!response.getHeader("Content-Type").contains("image/png") &&
154-
request.getLowercaseHeader("Accept-Encoding").contains(Compression.GZIP.getTypeId())
153+
!response.hasHeaderValue("Content-Type", "image/png") &&
154+
request.hasHeaderValue("Accept-Encoding", Compression.GZIP.getTypeId())
155155
) {
156156
response.addHeader("Content-Encoding", Compression.GZIP.getTypeId());
157157
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();

BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/web/RoutingRequestHandler.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
*/
2525
package de.bluecolored.bluemap.common.web;
2626

27-
import de.bluecolored.bluemap.common.webserver.*;
27+
import de.bluecolored.bluemap.common.web.http.HttpRequest;
28+
import de.bluecolored.bluemap.common.web.http.HttpRequestHandler;
29+
import de.bluecolored.bluemap.common.web.http.HttpResponse;
30+
import de.bluecolored.bluemap.common.web.http.HttpStatusCode;
2831
import org.intellij.lang.annotations.Language;
2932

3033
import java.util.LinkedList;
@@ -61,14 +64,13 @@ public HttpResponse handle(HttpRequest request) {
6164

6265
// normalize path
6366
if (path.startsWith("/")) path = path.substring(1);
64-
if (path.endsWith("/")) path = path.substring(0, path.length() - 1);
67+
if (path.isEmpty()) path = "/";
6568

6669
for (Route route : routes) {
6770
Matcher matcher = route.getRoutePattern().matcher(path);
6871
if (matcher.matches()) {
69-
RewrittenHttpRequest rewrittenRequest = new RewrittenHttpRequest(request);
70-
rewrittenRequest.setPath(matcher.replaceFirst(route.getReplacementRoute()));
71-
return route.handler.handle(rewrittenRequest);
72+
request.setPath(matcher.replaceFirst(route.getReplacementRoute()));
73+
return route.getHandler().handle(request);
7274
}
7375
}
7476

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package de.bluecolored.bluemap.common.web;
2+
3+
import de.bluecolored.bluemap.common.web.http.HttpConnection;
4+
import de.bluecolored.bluemap.common.web.http.HttpRequestHandler;
5+
import de.bluecolored.bluemap.common.web.http.SelectionConsumer;
6+
import de.bluecolored.bluemap.common.web.http.Server;
7+
8+
import java.io.IOException;
9+
10+
public class WebServer extends Server {
11+
12+
private final HttpRequestHandler requestHandler;
13+
14+
public WebServer(HttpRequestHandler requestHandler) throws IOException {
15+
this.requestHandler = requestHandler;
16+
}
17+
18+
@Override
19+
public SelectionConsumer createConnectionHandler() {
20+
return new HttpConnection(requestHandler);
21+
}
22+
23+
}

0 commit comments

Comments
 (0)