From 55c9501e57f15003ba630c656dc9ed06dc78a8d9 Mon Sep 17 00:00:00 2001 From: Viktor Lofgren Date: Wed, 10 Jan 2024 10:46:51 +0100 Subject: [PATCH] (search) Serve proper content type for static resources --- .../service/server/StaticResources.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/code/common/service/src/main/java/nu/marginalia/service/server/StaticResources.java b/code/common/service/src/main/java/nu/marginalia/service/server/StaticResources.java index 332b9a55..ff684525 100644 --- a/code/common/service/src/main/java/nu/marginalia/service/server/StaticResources.java +++ b/code/common/service/src/main/java/nu/marginalia/service/server/StaticResources.java @@ -10,6 +10,10 @@ import spark.staticfiles.MimeType; import java.io.FileNotFoundException; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Map; +import java.util.TreeMap; + +import static java.util.Map.entry; public class StaticResources { private final long startTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC); @@ -17,8 +21,15 @@ public class StaticResources { @SneakyThrows public void serveStatic(String domain, String path, Request req, Response rsp) { try { + if (path.startsWith("..") || domain.startsWith("..")) { + Spark.halt(403); + } + ClassPathResource resource = new ClassPathResource("static/" + domain + "/" + path); handleEtagStatic(resource, req, rsp); + + rsp.type(getContentType(path)); + resource.getInputStream().transferTo(rsp.raw().getOutputStream()); } catch (IllegalArgumentException | FileNotFoundException ex) { @@ -26,6 +37,30 @@ public class StaticResources { } } + private String getContentType(String path) { + // Opensearch description "must" be served as application/opensearchdescription+xml + if (path.endsWith("opensearch.xml")) + return "application/opensearchdescription+xml"; + + // Could probably be done with a suffix map instead + + if (path.endsWith(".html") || path.endsWith(".htm")) return "text/html"; + if (path.endsWith(".css")) return "text/css"; + if (path.endsWith(".js")) return "application/javascript"; + if (path.endsWith(".svg")) return "image/svg+xml"; + if (path.endsWith(".png")) return "image/png"; + if (path.endsWith(".jpg") || path.endsWith(".jpeg")) return "image/jpeg"; + if (path.endsWith(".webp")) return "image/webp"; + if (path.endsWith(".ico")) return "image/x-icon"; + if (path.endsWith(".woff")) return "font/woff"; + if (path.endsWith(".woff2")) return "font/woff2"; + if (path.endsWith(".txt")) return "text/plain"; + if (path.endsWith(".xml")) return "application/xml"; + if (path.endsWith(".json")) return "application/json"; + + return "application/octet-stream"; + } + @SneakyThrows private void handleEtagStatic(ClassPathResource resource, Request req, Response rsp) { rsp.header("Cache-Control", "public,max-age=3600");