diff --git a/Plan/dependency-reduced-pom.xml b/Plan/dependency-reduced-pom.xml
index 617d9d013..4441b3970 100644
--- a/Plan/dependency-reduced-pom.xml
+++ b/Plan/dependency-reduced-pom.xml
@@ -20,6 +20,7 @@
**/*.js
**/*.css
locale/*.txt
+ **/*.ico
diff --git a/Plan/pom.xml b/Plan/pom.xml
index 29e3b0339..09de51fe9 100644
--- a/Plan/pom.xml
+++ b/Plan/pom.xml
@@ -223,6 +223,7 @@
**/*.js
**/*.css
locale/*.txt
+ **/*.ico
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/file/PlanFiles.java b/Plan/src/main/java/com/djrapitops/plan/system/file/PlanFiles.java
index 748f6fc56..61e7b4e7d 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/file/PlanFiles.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/file/PlanFiles.java
@@ -14,6 +14,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.util.List;
/**
@@ -110,6 +111,10 @@ public class PlanFiles implements SubSystem {
));
}
+ public InputStream readCustomizableResource(String fileName) {
+ return FileUtil.stream(plugin, new File(plugin.getDataFolder(), fileName.replace("/", File.separator)), fileName);
+ }
+
private String flatten(List lines) {
StringBuilder flat = new StringBuilder();
for (String line : lines) {
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java
index 340e42e6d..6c1fb0be3 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java
@@ -66,7 +66,6 @@ public class ResponseHandler extends TreePageHandler {
}
public void registerPages() {
- registerPage("favicon.ico", responseFactory.redirectResponse("https://puu.sh/tK0KL/6aa2ba141b.ico"), 5);
registerPage("debug", debugPageHandler);
registerPage("players", playersPageHandler);
registerPage("player", playerPageHandler);
@@ -124,6 +123,9 @@ public class ResponseHandler extends TreePageHandler {
if (targetString.endsWith(".js")) {
return ResponseCache.loadResponse(PageId.JS.of(targetString), () -> responseFactory.javaScriptResponse(targetString));
}
+ if (targetString.endsWith("favicon.ico")) {
+ return ResponseCache.loadResponse(PageId.FAVICON.id(), responseFactory::faviconResponse);
+ }
boolean isNotInfoRequest = target.isEmpty() || !target.get(0).equals("info");
boolean isAuthRequired = webServer.get().isAuthRequired() && isNotInfoRequest;
if (isAuthRequired) {
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/cache/PageId.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/cache/PageId.java
index 7aa38dd38..e00924ebe 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/cache/PageId.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/cache/PageId.java
@@ -26,7 +26,7 @@ public enum PageId {
JS("js:"),
CSS("css:"),
- FAVICON_REDIRECT("Redirect:Favicon"),
+ FAVICON("Favicon"),
PLAYER_PLUGINS_TAB("playerPluginsTab:"),
NETWORK_CONTENT("networkContent");
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ByteResponse.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ByteResponse.java
new file mode 100644
index 000000000..b5b7501ba
--- /dev/null
+++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ByteResponse.java
@@ -0,0 +1,44 @@
+package com.djrapitops.plan.system.webserver.response;
+
+import com.djrapitops.plan.system.file.PlanFiles;
+import com.djrapitops.plan.system.locale.Locale;
+import com.djrapitops.plan.system.settings.theme.Theme;
+import com.sun.net.httpserver.HttpExchange;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * {@link Response} for raw bytes.
+ *
+ * @author Rsl1122
+ */
+public class ByteResponse extends Response {
+
+ private final PlanFiles files;
+ private final String fileName;
+
+ public ByteResponse(ResponseType type, String fileName, PlanFiles files) {
+ super(type);
+ this.fileName = fileName;
+ this.files = files;
+
+ setHeader("HTTP/1.1 200 OK");
+ }
+
+ @Override
+ public void send(HttpExchange exchange, Locale locale, Theme theme) throws IOException {
+ responseHeaders.set("Accept-Ranges", "bytes");
+ exchange.sendResponseHeaders(getCode(), 0);
+
+ try (OutputStream out = exchange.getResponseBody();
+ InputStream bis = files.readCustomizableResource(fileName)) {
+ byte[] buffer = new byte[2048];
+ int count;
+ while ((count = bis.read(buffer)) != -1) {
+ out.write(buffer, 0, count);
+ }
+ }
+ }
+}
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseFactory.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseFactory.java
index 729019d30..9e9137b8e 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseFactory.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseFactory.java
@@ -115,6 +115,10 @@ public class ResponseFactory {
return new RedirectResponse(location);
}
+ public Response faviconResponse() {
+ return new ByteResponse(ResponseType.X_ICON, "web/favicon.ico", files);
+ }
+
public ErrorResponse pageNotFound404() {
return notFound404(locale.getString(ErrorPageLang.UNKNOWN_PAGE_404));
}
diff --git a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseType.java b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseType.java
index 85ea8b115..85a1f0799 100644
--- a/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseType.java
+++ b/Plan/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseType.java
@@ -13,7 +13,9 @@ public enum ResponseType {
HTML("text/html; charset=utf-8"),
CSS("text/css"),
JSON("application/json"),
- JAVASCRIPT("application/javascript");
+ JAVASCRIPT("application/javascript"),
+ IMAGE("image/gif"),
+ X_ICON("image/x-icon");
private final String type;
diff --git a/Plan/src/main/java/com/djrapitops/plan/utilities/file/FileUtil.java b/Plan/src/main/java/com/djrapitops/plan/utilities/file/FileUtil.java
index 3d6cd366e..e50f5c684 100644
--- a/Plan/src/main/java/com/djrapitops/plan/utilities/file/FileUtil.java
+++ b/Plan/src/main/java/com/djrapitops/plan/utilities/file/FileUtil.java
@@ -4,10 +4,7 @@ import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.utilities.MiscUtils;
import com.djrapitops.plugin.logging.L;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -34,6 +31,31 @@ public class FileUtil {
return lines(plugin, defaults);
}
+ public static InputStream stream(PlanPlugin plugin, File savedFile, String defaults) {
+ try {
+ if (savedFile.exists()) {
+ return stream(savedFile);
+ } else {
+ String fileName = savedFile.getName();
+ File found = attemptToFind(fileName, new File(plugin.getDataFolder(), "web"));
+ if (found != null) {
+ return stream(found);
+ }
+ }
+ } catch (FileNotFoundException ignore) {
+ // File was not found, use jar version
+ }
+ return stream(plugin, defaults);
+ }
+
+ private static InputStream stream(PlanPlugin plugin, String resource) {
+ return plugin.getResource(resource);
+ }
+
+ private static InputStream stream(File savedFile) throws FileNotFoundException {
+ return new FileInputStream(savedFile);
+ }
+
/**
* Breadth-First search through the file tree to find the file.
*
diff --git a/Plan/src/main/resources/web/favicon.ico b/Plan/src/main/resources/web/favicon.ico
new file mode 100644
index 000000000..cea02dd44
Binary files /dev/null and b/Plan/src/main/resources/web/favicon.ico differ