fitnesse.responders.files.FileResponder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fitnesse Show documentation
Show all versions of fitnesse Show documentation
The fully integrated standalone wiki, and acceptance testing framework.
// 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 fitnesse.authentication.SecureOperation;
import fitnesse.authentication.SecureResponder;
import fitnesse.util.Clock;
import util.FileUtil;
import util.StreamReader;
import fitnesse.FitNesseContext;
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 SecureResponder {
// 1000-trick: remove milliseconds.
private static final Date LAST_MODIFIED_FOR_RESOURCES = new Date((Clock.currentTimeInMillis() / 1000) * 1000 );
private static final int RESOURCE_SIZE_LIMIT = 262144*2;
private static final FileNameMap fileNameMap = URLConnection.getFileNameMap();
String resource;
File requestedFile;
Date lastModifiedDate;
@Override
public Response makeResponse(FitNesseContext context, Request request) throws Exception {
String rootPath = context.getRootPagePath();
try {
resource = URLDecoder.decode(request.getResource(), FileUtil.CHARENCODING);
} catch (UnsupportedEncodingException e) {
return new ErrorResponder(e).makeResponse(context, request);
}
requestedFile = new File(rootPath, resource);
if (!isInFilesDirectory(new File(rootPath), requestedFile)) {
return new ErrorResponder("Invalid path: " + resource).makeResponse(context, request);
} else if (requestedFile.isDirectory()) {
return new DirectoryResponder(resource, requestedFile).makeResponse(context, request);
}
if (requestedFile.exists()) {
return makeFileResponse(request);
} else if (canLoadFromClasspath()) {
return makeClasspathResponse(context, request);
} else {
return new NotFoundResponder().makeResponse(context, request);
}
}
private boolean canLoadFromClasspath() {
return resource.startsWith("files/fitnesse/");
}
private Response makeClasspathResponse(FitNesseContext context, Request request) throws Exception {
determineLastModifiedInfo(LAST_MODIFIED_FOR_RESOURCES);
if (isNotModified(request))
return createNotModifiedResponse();
String classpathResource = "/fitnesse/resources/" + resource.substring("files/fitnesse/".length());
InputStream input;
input = Thread.currentThread().getContextClassLoader().getResourceAsStream(classpathResource);
if (input == null) {
//remove leading slash so path will work with resources inside a JAR file
while (classpathResource.startsWith("/"))
{
classpathResource = classpathResource.substring(1);
}
input = Thread.currentThread().getContextClassLoader().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);
lastModifiedDate = LAST_MODIFIED_FOR_RESOURCES;
response.setLastModifiedHeader(lastModifiedDate);
return response;
}
private Response makeFileResponse(Request request) throws FileNotFoundException {
InputStreamResponse response = new InputStreamResponse();
determineLastModifiedInfo(new Date(requestedFile.lastModified()));
if (isNotModified(request))
return createNotModifiedResponse();
else {
response.setBody(requestedFile);
setContentType(requestedFile.getName(), response);
response.setLastModifiedHeader(lastModifiedDate);
}
return response;
}
private boolean isNotModified(Request request) {
if (request.hasHeader("If-Modified-Since")) {
String queryDateString = request.getHeader("If-Modified-Since");
try {
Date queryDate = Response.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.notModified(lastModifiedDate, Clock.currentDate());
return response;
}
private void determineLastModifiedInfo(Date lastModified) {
// remove milliseconds
lastModifiedDate = new Date((lastModified.getTime() / 1000) * 1000);
}
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 if ((filename.endsWith(".jpg")) || (filename.endsWith(".jpeg"))) {
contentType = "image/jpeg";
} else if (filename.endsWith(".png")) {
contentType = "image/png";
} else if (filename.endsWith(".gif")) {
contentType = "image/gif";
} else if (filename.endsWith(".svg")) {
contentType = "image/svg+xml";
} else {
contentType = "text/plain";
}
}
return contentType;
}
public static boolean isInFilesDirectory(File rootPath, File file) throws IOException {
return isInSubDirectory(new File(rootPath, "files").getCanonicalFile(),
file.getCanonicalFile());
}
public static boolean isInFilesFitNesseDirectory(File rootPath, File file) throws IOException {
return isInSubDirectory(new File(new File(rootPath, "files"), "fitnesse").getCanonicalFile(),
file.getCanonicalFile());
}
private static boolean isInSubDirectory(File dir, File file) {
return file != null && (file.equals(dir) || isInSubDirectory(dir, file.getParentFile()));
}
@Override
public SecureOperation getSecureOperation() {
return new SecureOperation() {
@Override
public boolean shouldAuthenticate(FitNesseContext context, Request request) {
try {
return new File(context.getRootPagePath(), URLDecoder.decode(request.getResource(), FileUtil.CHARENCODING)).isDirectory();
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("Invalid URL encoding", e);
}
}
};
}
}