Package winstone

Source Code of winstone.StaticResourceServlet

/*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license (CDDL), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Servlet to handle static resources. Simply finds and sends them, or
* dispatches to the error servlet.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: StaticResourceServlet.java,v 1.17 2004/12/31 07:21:00
*          rickknowles Exp $
*/
public class StaticResourceServlet extends HttpServlet {
    // final String JSP_FILE = "org.apache.catalina.jsp_file";
    final static String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
    final static String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
    final static String CACHED_RESOURCE_DATE_HEADER = "If-Modified-Since";
    final static String LAST_MODIFIED_DATE_HEADER = "Last-Modified";
    final static String RANGE_HEADER = "Range";
    final static String ACCEPT_RANGES_HEADER = "Accept-Ranges";
    final static String CONTENT_RANGE_HEADER = "Content-Range";
    final static String RESOURCE_FILE = "winstone.LocalStrings";
    private DateFormat sdfFileDate = new SimpleDateFormat("dd-MM-yyyy HH:mm");
    private File webRoot;
    private String prefix;
    private boolean directoryList;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        this.webRoot = new File(config.getInitParameter("webRoot"));
        this.prefix = config.getInitParameter("prefix");
        String dirList = config.getInitParameter("directoryList");
        this.directoryList = (dirList == null)
                || dirList.equalsIgnoreCase("true")
                || dirList.equalsIgnoreCase("yes");
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        boolean isInclude = (request.getAttribute(INCLUDE_SERVLET_PATH) != null);
        boolean isForward = (request.getAttribute(FORWARD_SERVLET_PATH) != null);
        String path = null;

        if (isInclude)
            path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
        else  {
            path = request.getServletPath();
        }

        // URL decode path
        path = WinstoneRequest.decodeURLToken(path);

        long cachedResDate = request.getDateHeader(CACHED_RESOURCE_DATE_HEADER);
        Logger.log(Logger.DEBUG, Launcher.RESOURCES,
                "StaticResourceServlet.PathRequested", new String[] {
                        getServletConfig().getServletName(), path });

        // Check for the resource
        File res = path.equals("") ? this.webRoot : new File(
                this.webRoot, path);

        // Send a 404 if not found
        if (!res.exists())
            response.sendError(HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
                    .getString("StaticResourceServlet.PathNotFound", path));

        // Check we are below the webroot
        else if (!isDescendant(this.webRoot, res, this.webRoot)) {
            Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES, "StaticResourceServlet.OutsideWebroot",
                    new String[] {res.getCanonicalPath(), this.webRoot.toString()});
            response.sendError(HttpServletResponse.SC_FORBIDDEN, Launcher.RESOURCES
                    .getString("StaticResourceServlet.PathInvalid", path));
        }

        // Check we are not below the web-inf
        else if (!isInclude && !isForward && isDescendant(new File(this.webRoot, "WEB-INF"), res, this.webRoot))
            response.sendError(HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
                    .getString("StaticResourceServlet.PathInvalid", path));

        // Check we are not below the meta-inf
        else if (!isInclude && !isForward && isDescendant(new File(this.webRoot, "META-INF"), res, this.webRoot))
            response.sendError(HttpServletResponse.SC_NOT_FOUND, Launcher.RESOURCES
                    .getString("StaticResourceServlet.PathInvalid", path));

        // check for the directory case
        else if (res.isDirectory()) {
            if (path.endsWith("/")) {
                // Try to match each of the welcome files
                // String matchedWelcome = matchWelcomeFiles(path, res);
                // if (matchedWelcome != null)
                // response.sendRedirect(this.prefix + path + matchedWelcome);
                // else
                if (this.directoryList)
                    generateDirectoryList(request, response, path);
                else
                    response.sendError(HttpServletResponse.SC_FORBIDDEN,
                            Launcher.RESOURCES.getString("StaticResourceServlet.AccessDenied"));
            } else
                response.sendRedirect(this.prefix + path + "/");
        }

        // Send a 304 if not modified
        else if (!isInclude && (cachedResDate != -1)
                && (cachedResDate < (System.currentTimeMillis() / 1000L * 1000L))
                && (cachedResDate >= (res.lastModified() / 1000L * 1000L))) {
            String mimeType = getServletContext().getMimeType(
                    res.getName().toLowerCase());
            if (mimeType != null)
                response.setContentType(mimeType);
            response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            response.setContentLength(0);
            response.flushBuffer();
        }

        // Write out the resource if not range or is included
        else if ((request.getHeader(RANGE_HEADER) == null) || isInclude) {
            String mimeType = getServletContext().getMimeType(
                    res.getName().toLowerCase());
            if (mimeType != null)
                response.setContentType(mimeType);
            InputStream resStream = new FileInputStream(res);

            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentLength((int) res.length());
//            response.addHeader(ACCEPT_RANGES_HEADER, "bytes");
            response.addDateHeader(LAST_MODIFIED_DATE_HEADER, res.lastModified());
            OutputStream out = null;
            Writer outWriter = null;
            try {
                out = response.getOutputStream();
            } catch (IllegalStateException err) {
                outWriter = response.getWriter();
            } catch (IllegalArgumentException err) {
                outWriter = response.getWriter();
            }
            byte buffer[] = new byte[4096];
            int read = resStream.read(buffer);
            while (read > 0) {
                if (out != null) {
                    out.write(buffer, 0, read);
                } else {
                    outWriter.write(new String(buffer, 0, read,
                            response.getCharacterEncoding()));
                }
                read = resStream.read(buffer);
            }
            resStream.close();
        } else if (request.getHeader(RANGE_HEADER).startsWith("bytes=")) {
            String mimeType = getServletContext().getMimeType(
                    res.getName().toLowerCase());
            if (mimeType != null)
                response.setContentType(mimeType);
            InputStream resStream = new FileInputStream(res);

            List ranges = new ArrayList();
            StringTokenizer st = new StringTokenizer(request.getHeader(
                    RANGE_HEADER).substring(6).trim(), ",", false);
            int totalSent = 0;
            String rangeText = "";
            while (st.hasMoreTokens()) {
                String rangeBlock = st.nextToken();
                int start = 0;
                int end = (int) res.length();
                int delim = rangeBlock.indexOf('-');
                if (delim != 0)
                    start = Integer.parseInt(rangeBlock.substring(0, delim)
                            .trim());
                if (delim != rangeBlock.length() - 1)
                    end = Integer.parseInt(rangeBlock.substring(delim + 1)
                            .trim());
                totalSent += (end - start);
                rangeText += "," + start + "-" + end;
                ranges.add(start + "-" + end);
            }
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            response.addHeader(CONTENT_RANGE_HEADER, "bytes "
                    + rangeText.substring(1) + "/" + res.length());
            response.setContentLength(totalSent);

            response.addHeader(ACCEPT_RANGES_HEADER, "bytes");
            response.addDateHeader(LAST_MODIFIED_DATE_HEADER, res
                    .lastModified());
            OutputStream out = response.getOutputStream();
            int bytesRead = 0;
            for (Iterator i = ranges.iterator(); i.hasNext();) {
                String rangeBlock = (String) i.next();
                int delim = rangeBlock.indexOf('-');
                int start = Integer.parseInt(rangeBlock.substring(0, delim));
                int end = Integer.parseInt(rangeBlock.substring(delim + 1));
                int read = 0;
                while ((read != -1) && (bytesRead <= res.length())) {
                    read = resStream.read();
                    if ((bytesRead >= start) && (bytesRead < end))
                        out.write(read);
                    bytesRead++;
                }
            }
            resStream.close();
        } else
            response
                    .sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
    }

    /**
     * Generate a list of the files in this directory
     */
    private void generateDirectoryList(HttpServletRequest request,
            HttpServletResponse response, String path) throws ServletException,
            IOException {
        // Get the file list
        File dir = path.equals("") ? this.webRoot : new File(
                this.webRoot, path);
        File children[] = dir.listFiles();
        Arrays.sort(children);

        // Build row content
        StringWriter rowString = new StringWriter();
        String oddColour = Launcher.RESOURCES
                .getString("StaticResourceServlet.DirectoryList.OddColour");
        String evenColour = Launcher.RESOURCES
                .getString("StaticResourceServlet.DirectoryList.EvenColour");
        String rowTextColour = Launcher.RESOURCES
                .getString("StaticResourceServlet.DirectoryList.RowTextColour");

        String directoryLabel = Launcher.RESOURCES
                .getString("StaticResourceServlet.DirectoryList.DirectoryLabel");
        String parentDirLabel = Launcher.RESOURCES
                .getString("StaticResourceServlet.DirectoryList.ParentDirectoryLabel");
        String noDateLabel = Launcher.RESOURCES
                .getString("StaticResourceServlet.DirectoryList.NoDateLabel");

        int rowCount = 0;

        // Write the parent dir row
        if (!path.equals("") && !path.equals("/")) {
            rowString.write(Launcher.RESOURCES.getString(
                    "StaticResourceServlet.DirectoryList.Row", new String[] {
                            rowTextColour, evenColour, parentDirLabel, "..",
                            noDateLabel, directoryLabel }));
            rowCount++;
        }

        // Write the rows for each file
        for (int n = 0; n < children.length; n++) {
            if (!children[n].getName().equalsIgnoreCase("web-inf") &&
                    !children[n].getName().equalsIgnoreCase("meta-inf")) {
                File file = children[n];
                String date = noDateLabel;
                String size = directoryLabel;
                if (!file.isDirectory()) {
                    size = "" + file.length();
                    synchronized (sdfFileDate) {
                        date = sdfFileDate.format(new Date(file.lastModified()));
                    }
                }
                rowString.write(Launcher.RESOURCES.getString(
                        "StaticResourceServlet.DirectoryList.Row",
                        new String[] {
                                rowTextColour,
                                rowCount % 2 == 0 ? evenColour : oddColour,
                                file.getName() + (file.isDirectory() ? "/" : ""),
                                "./" + file.getName() + (file.isDirectory() ? "/" : ""),
                                date, size}));
                rowCount++;
            }
        }
       
        // Build wrapper body
        String out = Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.Body",
                new String[] {
                        Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.HeaderColour"),
                        Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.HeaderTextColour"),
                        Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.LabelColour"),
                        Launcher.RESOURCES.getString("StaticResourceServlet.DirectoryList.LabelTextColour"),
                        new Date() + "",
                        Launcher.RESOURCES.getString("ServerVersion"),
                        path.equals("") ? "/" : path,
                        rowString.toString() });

        response.setContentLength(out.getBytes().length);
        response.setContentType("text/html");
        Writer w = response.getWriter();
        w.write(out);
        w.close();
    }
   
    public static boolean isDescendant(File parent, File child, File commonBase) throws IOException {
        if (child.equals(parent)) {
            return true;
        } else {
            // Start by checking canonicals
            String canonicalParent = parent.getAbsoluteFile().getCanonicalPath();
            String canonicalChild = child.getAbsoluteFile().getCanonicalPath();
            if (canonicalChild.startsWith(canonicalParent)) {
                return true;
            }
           
            // If canonicals don't match, we're dealing with symlinked files, so if we can
            // build a path from the parent to the child,
            String childOCValue = constructOurCanonicalVersion(child, commonBase);
            String parentOCValue = constructOurCanonicalVersion(parent, commonBase);
            return childOCValue.startsWith(parentOCValue);
        }
    }
   
    public static String constructOurCanonicalVersion(File current, File stopPoint) {
        int backOnes = 0;
        StringBuffer ourCanonicalVersion = new StringBuffer();
        while ((current != null) && !current.equals(stopPoint)) {
            if (current.getName().equals("..")) {
                backOnes++;
            } else if (current.getName().equals(".")) {
                // skip - do nothing
            } else if (backOnes > 0) {
                backOnes--;
            } else {
                ourCanonicalVersion.insert(0, "/" + current.getName());
            }
            current = current.getParentFile();
        }
        return ourCanonicalVersion.toString();
    }
}
TOP

Related Classes of winstone.StaticResourceServlet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.