// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.responders.files; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.FileNameMap; import java.net.URLConnection; import java.net.URLDecoder; import java.text.ParseException; import java.util.Date; import util.Clock; import util.StreamReader; import fitnesse.FitNesseContext; import fitnesse.Responder; import fitnesse.http.InputStreamResponse; import fitnesse.http.Request; import fitnesse.http.Response; import fitnesse.http.SimpleResponse; import fitnesse.responders.ErrorResponder; import fitnesse.responders.NotFoundResponder; public class FileResponder implements Responder { private static final int RESOURCE_SIZE_LIMIT = 262144; private static FileNameMap fileNameMap = URLConnection.getFileNameMap(); final public String resource; final public File requestedFile; public Date lastModifiedDate; public String lastModifiedDateString; public static Responder makeResponder(Request request, String rootPath) { String resource = request.getResource(); try { resource = URLDecoder.decode(resource, "UTF-8"); } catch (UnsupportedEncodingException e) { return new ErrorResponder(e); } File requestedFile = new File(rootPath + "/" + resource); if (requestedFile.isDirectory()) return new DirectoryResponder(resource, requestedFile); else return new FileResponder(resource, requestedFile); } public FileResponder(String resource, File requestedFile) { this.resource = resource; this.requestedFile = requestedFile; } public Response makeResponse(FitNesseContext context, Request request) throws IOException { if (requestedFile.exists()) { return makeFileResponse(context, request); } else if (canLoadFromClasspath(resource)) { return makeClasspathResponse(context, request); } else { return new NotFoundResponder().makeResponse(context, request); } } private boolean canLoadFromClasspath(String resource2) { return resource.startsWith("files/fitnesse/"); } private Response makeClasspathResponse(FitNesseContext context, Request request) throws IOException { String classpathResource = "/fitnesse/resources/" + resource.substring("files/fitnesse/".length()); InputStream input = getClass().getResourceAsStream(classpathResource); if (input == null) { return new NotFoundResponder().makeResponse(context, request); } StreamReader reader = new StreamReader(input); // Set a hard limit on the amount of data that can be read: byte[] content = reader.readBytes(RESOURCE_SIZE_LIMIT); SimpleResponse response = new SimpleResponse(); response.setContent(content); setContentType(classpathResource, response); return response; } private Response makeFileResponse(FitNesseContext context, Request request) throws FileNotFoundException { InputStreamResponse response = new InputStreamResponse(); determineLastModifiedInfo(); if (isNotModified(request)) return createNotModifiedResponse(); else { response.setBody(requestedFile); setContentType(requestedFile.getName(), response); response.setLastModifiedHeader(lastModifiedDateString); } return response; } private boolean isNotModified(Request request) { if (request.hasHeader("If-Modified-Since")) { String queryDateString = (String) request.getHeader("If-Modified-Since"); try { Date queryDate = SimpleResponse.makeStandardHttpDateFormat().parse(queryDateString); if (!queryDate.before(lastModifiedDate)) return true; } catch (ParseException e) { //Some browsers use local date formats that we can't parse. //So just ignore this exception if we can't parse the date. } } return false; } private Response createNotModifiedResponse() { Response response = new SimpleResponse(); response.setStatus(304); response.addHeader("Date", SimpleResponse.makeStandardHttpDateFormat().format(Clock.currentDate())); response.addHeader("Cache-Control", "private"); response.setLastModifiedHeader(lastModifiedDateString); return response; } private void determineLastModifiedInfo() { lastModifiedDate = new Date(requestedFile.lastModified()); lastModifiedDateString = SimpleResponse.makeStandardHttpDateFormat().format(lastModifiedDate); try // remove milliseconds { lastModifiedDate = SimpleResponse.makeStandardHttpDateFormat().parse(lastModifiedDateString); } catch (java.text.ParseException jtpe) { jtpe.printStackTrace(); } } private void setContentType(String filename, Response response) { String contentType = getContentType(filename); response.setContentType(contentType); } public static String getContentType(String filename) { String contentType = fileNameMap.getContentTypeFor(filename); if (contentType == null) { if (filename.endsWith(".css")) { contentType = "text/css"; } else if (filename.endsWith(".js")) { contentType = "text/javascript"; } else if (filename.endsWith(".jar")) { contentType = "application/x-java-archive"; } else { contentType = "text/plain"; } } return contentType; } }