package peergos.server.net;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import peergos.shared.crypto.hash.Hash;
import peergos.shared.util.ArrayOps;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.zip.GZIPOutputStream;
public abstract class StaticHandler implements HttpHandler
{
private final boolean isGzip;
public StaticHandler(boolean isGzip) {
this.isGzip = isGzip;
}
public abstract Asset getAsset(String resourcePath) throws IOException;
public static class Asset {
public final byte[] data;
public final String hash;
public Asset(byte[] data) {
this.data = data;
byte[] digest = Hash.sha256(data);
this.hash = ArrayOps.bytesToHex(Arrays.copyOfRange(digest, 0, 4));
}
}
protected boolean isGzip() {
return isGzip;
}
@Override
public void handle(HttpExchange httpExchange) throws IOException {
String path = httpExchange.getRequestURI().getPath();
try {
path = path.substring(1);
path = path.replaceAll("//", "/");
if (path.length() == 0)
path = "index.html";
Asset res = getAsset(path);
if (isGzip)
httpExchange.getResponseHeaders().set("Content-Encoding", "gzip");
if (path.endsWith(".js"))
httpExchange.getResponseHeaders().set("Content-Type", "text/javascript");
else if (path.endsWith(".html"))
httpExchange.getResponseHeaders().set("Content-Type", "text/html");
else if (path.endsWith(".css"))
httpExchange.getResponseHeaders().set("Content-Type", "text/css");
else if (path.endsWith(".json"))
httpExchange.getResponseHeaders().set("Content-Type", "application/json");
if (httpExchange.getRequestMethod().equals("HEAD")) {
httpExchange.getResponseHeaders().set("Content-Length", "" + res.data.length);
httpExchange.sendResponseHeaders(200, -1);
return;
}
if (res.data.length > 100 * 1024) {
httpExchange.getResponseHeaders().set("Cache-Control", "public, max-age=3600");
httpExchange.getResponseHeaders().set("ETag", res.hash);
}
httpExchange.sendResponseHeaders(200, res.data.length);
httpExchange.getResponseBody().write(res.data);
httpExchange.getResponseBody().close();
} catch (NullPointerException t) {
System.err.println("Error retrieving: " + path);
} catch (Throwable t) {
System.err.println("Error retrieving: " + path);
httpExchange.sendResponseHeaders(404, 0);
httpExchange.getResponseBody().close();
}
}
protected static byte[] readResource(InputStream in, boolean gzip) throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
OutputStream gout = gzip ? new GZIPOutputStream(bout) : new DataOutputStream(bout);
byte[] tmp = new byte[4096];
int r;
while ((r=in.read(tmp)) >= 0)
gout.write(tmp, 0, r);
gout.flush();
gout.close();
in.close();
return bout.toByteArray();
}
public StaticHandler withCache() {
Map<String, Asset> cache = new ConcurrentHashMap<>();
StaticHandler that = this;
return new StaticHandler(isGzip) {
@Override
public Asset getAsset(String resourcePath) throws IOException {
if (! cache.containsKey(resourcePath))
cache.put(resourcePath, that.getAsset(resourcePath));
return cache.get(resourcePath);
}
};
}
}