Skip to content

Instantly share code, notes, and snippets.

@otykhonruk
Created August 7, 2013 10:30
Show Gist options
  • Save otykhonruk/6172911 to your computer and use it in GitHub Desktop.
Save otykhonruk/6172911 to your computer and use it in GitHub Desktop.
HTTP server that serves content of zip files. Useful for huge zipped piles of html, for example Java SDK docs.
package alt;
import static java.net.HttpURLConnection.*; // http status codes
import java.io.*;
import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import java.util.zip.*;
import javax.activation.MimetypesFileTypeMap;
import com.sun.net.httpserver.*;
/**
* Serves zip files.
*/
public class ZipHttpServer {
private final Properties props;
private final Logger logger = Logger.getLogger(getClass().getName());
private HttpServer server;
private int port;
public ZipHttpServer(Properties props) {
this.props = props;
}
public void initialize() throws IOException {
port = Integer.parseInt(props.getProperty("alt.ziphttp.port", "8080"));
InetSocketAddress addr = new InetSocketAddress(port);
server = HttpServer.create(addr, 0);
for(String ctx : props.stringPropertyNames()) {
if(ctx.startsWith("/")) {
String[] def = props.getProperty(ctx).split("\\s+");
String fileName = def[0];
String docRoot = "";
if(def.length > 1) {
docRoot = def[1];
}
File file = new File(fileName);
if(file.canRead()) {
server.createContext(ctx, new ZipFileHandler(new ZipFile(file), docRoot, logger));
logger.info("Context initialized: " + ctx);
}
else {
logger.warning("Cannot create context " + ctx +": file " + fileName + " does not exists or not readable");
}
}
}
}
public void start() {
server.setExecutor(Executors.newCachedThreadPool());
server.start();
logger.info("Server listening on port " + port);
}
public static void main(String[] args) throws IOException {
Properties props = new Properties();
File propsFile = (args.length == 1) ? new File(args[0]) : new File("ziphttp.properties");
if (propsFile.canRead()) {
props.load(new FileReader(propsFile));
} else {
System.out.println("Usage: java alt.ZipHttpServer ziphttp.properties");
System.exit(1);
}
ZipHttpServer server = new ZipHttpServer(props);
server.initialize();
server.start();
}
}
/**
* Handles single zip file
*/
class ZipFileHandler implements HttpHandler {
private static final SimpleDateFormat RFC1123DateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
private static final MimetypesFileTypeMap types = new MimetypesFileTypeMap();
static { types.addMimeTypes("text/css css CSS"); } // basic map supplied by jre does not contain css mime type, causing browsers to ignore stylesheets
private final ZipFile zipfile;
private final String docroot;
private Logger logger;
/**
* Constructs handler for specified zip file
*
* @param file zip file
* @param docroot document root, directory within given file
* @param logger logger for this context
*/
ZipFileHandler(ZipFile file, String docroot, Logger logger) {
this.zipfile = file;
this.docroot = docroot;
this.logger = logger;
}
public void handle(HttpExchange exchange) throws IOException {
final String requestMethod = exchange.getRequestMethod();
final String ctxPath = exchange.getHttpContext().getPath();
final String reqPath = exchange.getRequestURI().getPath();
if (requestMethod.equalsIgnoreCase("GET")) {
String path = reqPath.replaceFirst(ctxPath, docroot);
ZipEntry entry = zipfile.getEntry(path);
// if entry is directory, append "index.html". TODO: IndexFiles
if((entry != null) && entry.isDirectory()) {
path += "index.html";
entry = zipfile.getEntry(path);
}
logger.finer("Request path: " + reqPath + "; rewritten path: " + path);
if (entry != null) { // 200 OK
// headers
Headers responseHeaders = exchange.getResponseHeaders();
responseHeaders.set("Content-Type", types.getContentType(path));
responseHeaders.set("Last-Modified", RFC1123DateFormat.format(new Date(entry.getTime())));
exchange.sendResponseHeaders(HTTP_OK, entry.getSize());
// body
OutputStream responseBody = exchange.getResponseBody();
InputStream is = zipfile.getInputStream(entry);
byte[] buf = new byte[4096];
int read;
while((read = is.read(buf)) != -1) {
responseBody.write(buf, 0, read);
}
is.close();
responseBody.close();
logger.fine(HTTP_OK + " OK " + reqPath);
} else { // 404 Not found
exchange.sendResponseHeaders(HTTP_NOT_FOUND, -1);
logger.fine(HTTP_NOT_FOUND + " NOT_FOUND " + reqPath);
}
} else {
exchange.sendResponseHeaders(HTTP_BAD_METHOD, -1);
logger.fine(HTTP_BAD_METHOD + " BAD_METHOD " + reqPath);
}
exchange.close();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment