001: package pygmy.handlers;
002:
003: import pygmy.core.*;
004:
005: import java.io.*;
006: import java.util.logging.Logger;
007: import java.text.ParseException;
008: import java.net.HttpURLConnection;
009:
010: /**
011: * <p>
012: * This is the most basic Handler of a web server. It serves up files from a specified directory. For all urls
013: * matching the url-prefix parameter, it translates those into files on the root files system starting at the root
014: * parameter. It does pay attention to the Range http header parameter, and will send a range of bytes from a
015: * request file. It also sets the <i>file-path</i> request property to the translate local system path of the file.
016: * Other handlers could use this so that they don't have to translate the URL into a path. This handler ignores
017: * directory requests, but will serve up default files ( like index.html, if the config parameter is set ). It does
018: * not provide a directory listing see {@link DirectoryHandler} for that functionality.
019: * </p>
020: *
021: * <table class="inner">
022: * <tr class="header"><td>Parameter Name</td><td>Explanation</td><td>Default Value</td><td>Required</td></tr>
023: * <tr class="row"><td>url-prefix</td><td>The prefix to filter request urls.</td><td>None</td><td>Yes</td></tr>
024: * <tr class="altrow"><td>root</td><td>A local system path to the root of the folder to share.</td><td>None</td><td>Yes</td></tr>
025: * <tr class="row"><td>default-file</td><td>The name of the default file that should be used if no file is specified in the URL.</td><td>index.html</td><td>No</td></tr>
026: * </table>
027: */
028: public class FileHandler extends AbstractHandler implements Handler {
029: public static final Logger log = Logger.getLogger(FileHandler.class
030: .getName());
031:
032: public static final ConfigOption ROOT_OPTION = new ConfigOption(
033: "root", true, "The path to the directory share files.");
034: public static final ConfigOption DEFAULT_FILE_OPTION = new ConfigOption(
035: "default-file", "index.html",
036: "The default file to send if no file is specified.");
037:
038: public static final String IF_MODIFIED = "If-Modified-Since";
039: public static final String LAST_MODIFIED_KEY = "Last-Modified";
040: public static final String RANGE_HEADER_KEY = "Range";
041:
042: private String root;
043: private String defaultFile;
044:
045: public boolean initialize(String handlerName, Server server) {
046: super .initialize(handlerName, server);
047: root = ROOT_OPTION.getProperty(server, handlerName);
048: defaultFile = DEFAULT_FILE_OPTION.getProperty(server,
049: handlerName);
050: return true;
051: }
052:
053: protected boolean handleBody(HttpRequest request,
054: HttpResponse response) throws IOException {
055: File file = Http.translatePath(root, request.getUrl()
056: .substring(getUrlPrefix().length()));
057: if (!Http.isSecure(root, file)) {
058: log.warning("Access denied to " + file.getAbsolutePath());
059: return false;
060: }
061: request.putProperty("file-path", file.getAbsolutePath());
062: if (file.isDirectory()) {
063: file = new File(file, defaultFile);
064: }
065: if (file.exists() == false) {
066: log.warning("File " + file.getAbsolutePath()
067: + " was not found.");
068: return false;
069: }
070: String type = getMimeType(file.getName());
071: if (type != null) {
072: sendFile(request, response, file, type);
073: return true;
074: } else {
075: log.warning("Mime type for file " + file.getAbsolutePath()
076: + " was not found.");
077: return false;
078: }
079: }
080:
081: static public void sendFile(HttpRequest request,
082: HttpResponse response, File file, String type)
083: throws IOException {
084: if (file.isFile() == false) {
085: response.sendError(HttpURLConnection.HTTP_NOT_FOUND,
086: " not a normal file");
087: return;
088: }
089: if (file.canRead() == false) {
090: response.sendError(HttpURLConnection.HTTP_FORBIDDEN,
091: " Permission Denied");
092: return;
093: }
094:
095: if (request.getRequestHeader(IF_MODIFIED) != null) {
096: try {
097: long modified = Http.parseTime(request
098: .getRequestHeader(IF_MODIFIED));
099: if (file.lastModified() <= modified) {
100: response
101: .setStatusCode(HttpURLConnection.HTTP_NOT_MODIFIED);
102: return;
103: }
104: } catch (ParseException ignore) {
105: // ignore the date.
106: }
107: }
108: InputStream in = new BufferedInputStream(new FileInputStream(
109: file));
110: response.addHeader(LAST_MODIFIED_KEY, Http.formatTime(file
111: .lastModified()));
112: long[] range = getRange(request, file);
113: response.setMimeType(type);
114: response.sendResponse(in, range[0], range[1]);
115: }
116:
117: private static long[] getRange(HttpRequest request, File file) {
118: long range[] = new long[2];
119: range[0] = 0;
120: range[1] = file.length();
121: String rangeStr = request.getRequestHeader(RANGE_HEADER_KEY,
122: "bytes=0-");
123: int equalSplit = rangeStr.indexOf("=") + 1;
124: int split = rangeStr.indexOf("-");
125: if (split < -1) {
126: try {
127: range[0] = Integer.parseInt(rangeStr
128: .substring(equalSplit));
129: } catch (NumberFormatException e) {
130: }
131: } else {
132: range[0] = Integer.parseInt(rangeStr.substring(equalSplit,
133: split));
134: if (split + 1 < rangeStr.length()) {
135: try {
136: range[1] = Integer.parseInt(rangeStr.substring(
137: split + 1, rangeStr.length()));
138: } catch (NumberFormatException e) {
139: }
140: }
141: }
142: return range;
143: }
144: }
|