How to serve static content using the sun simple httpserver

I use the HttpServerFactory jersey to create a simple built-in HttpServer which hosts a couple of leisure services. We just need something small, quick and easy. I need to host a small static HTML page in the same server instance. Is there an easy way to add a static handler to the server? Is there a predefined handler that I can use? This seems like a pretty common task, I would not want to rewrite the code if it already exists.

 server = HttpServerFactory.create(url); server.setExecutor(Executors.newCachedThreadPool()); server.createContext("/staticcontent", new HttpHandler() { @Override public void handle(HttpExchange arg0) throws IOException { //What goes here? } }); server.start(); 
+9
source share
2 answers

This will work, although it will allow anyone to walk through the tree, requesting ../../../ you can change. / wwwroot to any valid Java file path.

 static class MyHandler implements HttpHandler { public void handle(HttpExchange t) throws IOException { String root = "./wwwroot"; URI uri = t.getRequestURI(); System.out.println("looking for: "+ root + uri.getPath()); String path = uri.getPath(); File file = new File(root + path).getCanonicalFile(); if (!file.isFile()) { // Object does not exist or is not a file: reject with 404 error. String response = "404 (Not Found)\n"; t.sendResponseHeaders(404, response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); } else { // Object exists and is a file: accept with response code 200. String mime = "text/html"; if(path.substring(path.length()-3).equals(".js")) mime = "application/javascript"; if(path.substring(path.length()-3).equals("css")) mime = "text/css"; Headers h = t.getResponseHeaders(); h.set("Content-Type", mime); t.sendResponseHeaders(200, 0); OutputStream os = t.getResponseBody(); FileInputStream fs = new FileInputStream(file); final byte[] buffer = new byte[0x10000]; int count = 0; while ((count = fs.read(buffer)) >= 0) { os.write(buffer,0,count); } fs.close(); os.close(); } } } 
+7
source

Here is the safe version. You can add several MIME types, depending on which of them are common (or use a different method if your platform has this ).

 package de.phihag.miniticker; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; public class StaticFileHandler implements HttpHandler { private static final Map<String,String> MIME_MAP = new HashMap<>(); static { MIME_MAP.put("appcache", "text/cache-manifest"); MIME_MAP.put("css", "text/css"); MIME_MAP.put("gif", "image/gif"); MIME_MAP.put("html", "text/html"); MIME_MAP.put("js", "application/javascript"); MIME_MAP.put("json", "application/json"); MIME_MAP.put("jpg", "image/jpeg"); MIME_MAP.put("jpeg", "image/jpeg"); MIME_MAP.put("mp4", "video/mp4"); MIME_MAP.put("pdf", "application/pdf"); MIME_MAP.put("png", "image/png"); MIME_MAP.put("svg", "image/svg+xml"); MIME_MAP.put("xlsm", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); MIME_MAP.put("xml", "application/xml"); MIME_MAP.put("zip", "application/zip"); MIME_MAP.put("md", "text/plain"); MIME_MAP.put("txt", "text/plain"); MIME_MAP.put("php", "text/plain"); }; private String filesystemRoot; private String urlPrefix; private String directoryIndex; /** * @param urlPrefix The prefix of all URLs. * This is the first argument to createContext. Must start and end in a slash. * @param filesystemRoot The root directory in the filesystem. * Only files under this directory will be served to the client. * For instance "./staticfiles". * @param directoryIndex File to show when a directory is requested, eg "index.html". */ public StaticFileHandler(String urlPrefix, String filesystemRoot, String directoryIndex) { if (!urlPrefix.startsWith("/")) { throw new RuntimeException("pathPrefix does not start with a slash"); } if (!urlPrefix.endsWith("/")) { throw new RuntimeException("pathPrefix does not end with a slash"); } this.urlPrefix = urlPrefix; assert filesystemRoot.endsWith("/"); try { this.filesystemRoot = new File(filesystemRoot).getCanonicalPath(); } catch (IOException e) { throw new RuntimeException(e); } this.directoryIndex = directoryIndex; } /** * Create and register a new static file handler. * @param hs The HTTP server where the file handler will be registered. * @param path The path in the URL prefixed to all requests, such as "/static/" * @param filesystemRoot The filesystem location. * For instance "/var/www/mystaticfiles/". * A request to "/static/x/y.html" will be served from the filesystem file "/var/www/mystaticfiles/x/y.html" * @param directoryIndex File to show when a directory is requested, eg "index.html". */ public static void create(HttpServer hs, String path, String filesystemRoot, String directoryIndex) { StaticFileHandler sfh = new StaticFileHandler(path, filesystemRoot, directoryIndex); hs.createContext(path, sfh); } public void handle(HttpExchange he) throws IOException { String method = he.getRequestMethod(); if (! ("HEAD".equals(method) || "GET".equals(method))) { sendError(he, 501, "Unsupported HTTP method"); return; } String wholeUrlPath = he.getRequestURI().getPath(); if (wholeUrlPath.endsWith("/")) { wholeUrlPath += directoryIndex; } if (! wholeUrlPath.startsWith(urlPrefix)) { throw new RuntimeException("Path is not in prefix - incorrect routing?"); } String urlPath = wholeUrlPath.substring(urlPrefix.length()); File f = new File(filesystemRoot, urlPath); File canonicalFile; try { canonicalFile = f.getCanonicalFile(); } catch (IOException e) { // This may be more benign (ie not an attack, just a 403), // but we don't want the attacker to be able to discern the difference. reportPathTraversal(he); return; } String canonicalPath = canonicalFile.getPath(); if (! canonicalPath.startsWith(filesystemRoot)) { reportPathTraversal(he); return; } FileInputStream fis; try { fis = new FileInputStream(canonicalFile); } catch (FileNotFoundException e) { // The file may also be forbidden to us instead of missing, but we're leaking less information this way sendError(he, 404, "File not found"); return; } String mimeType = lookupMime(urlPath); he.getResponseHeaders().set("Content-Type", mimeType); if ("GET".equals(method)) { he.sendResponseHeaders(200, canonicalFile.length()); OutputStream os = he.getResponseBody(); copyStream(fis, os); os.close(); } else { assert("HEAD".equals(method)); he.sendResponseHeaders(200, -1); } fis.close(); } private void copyStream(InputStream is, OutputStream os) throws IOException { byte[] buf = new byte[4096]; int n; while ((n = is.read(buf)) >= 0) { os.write(buf, 0, n); } } private void sendError(HttpExchange he, int rCode, String description) throws IOException { String message = "HTTP error " + rCode + ": " + description; byte[] messageBytes = message.getBytes("UTF-8"); he.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8"); he.sendResponseHeaders(rCode, messageBytes.length); OutputStream os = he.getResponseBody(); os.write(messageBytes); os.close(); } // This is one function to avoid giving away where we failed private void reportPathTraversal(HttpExchange he) throws IOException { sendError(he, 400, "Path traversal attempt detected"); } private static String getExt(String path) { int slashIndex = path.lastIndexOf('/'); String basename = (slashIndex < 0) ? path : path.substring(slashIndex + 1); int dotIndex = basename.lastIndexOf('.'); if (dotIndex >= 0) { return basename.substring(dotIndex + 1); } else { return ""; } } private static String lookupMime(String path) { String ext = getExt(path).toLowerCase(); return MIME_MAP.getOrDefault(ext, "application/octet-stream"); } } 
+3
source

All Articles