All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.undertow.server.handlers.resource.DirectoryUtils Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package io.undertow.server.handlers.resource;

import io.undertow.UndertowLogger;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.DateUtils;
import io.undertow.util.ETag;
import io.undertow.util.ETagUtils;
import io.undertow.util.FlexBase64;
import io.undertow.util.Headers;
import io.undertow.util.Methods;
import io.undertow.util.RedirectBuilder;
import io.undertow.util.StatusCodes;
import org.xnio.channels.Channels;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * @author Stuart Douglas
 */
public class DirectoryUtils {

    /**
     * Serve static resource for the directory listing
     *
     * @param exchange The exchange
     * @return true if resources were served
     */
    public static boolean sendRequestedBlobs(HttpServerExchange exchange) {
        ByteBuffer buffer = null;
        String type = null;
        String etag = null;
        String quotedEtag = null;
        if ("css".equals(exchange.getQueryString())) {
            buffer = Blobs.FILE_CSS_BUFFER.duplicate();
            type = "text/css";
            etag = Blobs.FILE_CSS_ETAG;
            quotedEtag = Blobs.FILE_CSS_ETAG_QUOTED;
        } else if ("js".equals(exchange.getQueryString())) {
            buffer = Blobs.FILE_JS_BUFFER.duplicate();
            type = "application/javascript";
            etag = Blobs.FILE_JS_ETAG;
            quotedEtag = Blobs.FILE_JS_ETAG_QUOTED;
        }

        if (buffer != null) {

            if(!ETagUtils.handleIfNoneMatch(exchange, new ETag(false, etag), false)) {
                exchange.setStatusCode(StatusCodes.NOT_MODIFIED);
                return true;
            }

            exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, String.valueOf(buffer.limit()));
            exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, type);
            exchange.getResponseHeaders().put(Headers.ETAG, quotedEtag);
            if (Methods.HEAD.equals(exchange.getRequestMethod())) {
                exchange.endExchange();
                return true;
            }
            exchange.getResponseSender().send(buffer);

            return true;
        }

        return false;
    }

    public static StringBuilder renderDirectoryListing(String path, Resource resource) {
        if (!path.endsWith("/")){
            path += "/";
        }
        StringBuilder builder = new StringBuilder();
        builder.append("\n\n\n")
                .append("\n\n");
        builder.append("\n\n\n");
        builder.append("\n")
                .append("\n\n")
                .append("\n\n\n\n");

        int state = 0;
        String parent = null;
        if(path.length() > 1) {
            for (int i = path.length() - 1; i >= 0; i--) {
                if (state == 1) {
                    if (path.charAt(i) == '/') {
                        state = 2;
                    }
                } else if (path.charAt(i) != '/') {
                    if (state == 2) {
                        parent = path.substring(0, i + 1);
                        break;
                    }
                    state = 1;
                }
            }
            if(parent == null) {
                parent = "/";
            }
        }


        SimpleDateFormat format = new SimpleDateFormat("MMM dd, yyyy HH:mm:ss", Locale.US);
        int i = 0;
        if (parent != null) {
            i++;
            builder.append("\n");
        }

        for (Resource entry : resource.list()) {
            builder.append("\n");
        }
        builder.append("\n
Directory Listing - ").append(path).append("
NameLast ModifiedSize
Powered by Undertow
[..]"); builder.append(format.format((resource.getLastModified() == null ? new Date(0L) : resource.getLastModified()))) .append("--
").append(entry.getName()).append(""); builder.append(format.format((entry.getLastModified() == null) ? new Date(0L) : entry.getLastModified())) .append(""); if (entry.isDirectory()) { builder.append("--"); } else { formatSize(builder, entry.getContentLength()); } builder.append("
\n\n"); return builder; } public static void renderDirectoryListing(HttpServerExchange exchange, Resource resource) { String requestPath = exchange.getRequestPath(); if (! requestPath.endsWith("/")) { exchange.setStatusCode(StatusCodes.FOUND); exchange.getResponseHeaders().put(Headers.LOCATION, RedirectBuilder.redirect(exchange, exchange.getRelativePath() + "/", true)); exchange.endExchange(); return; } StringBuilder builder = renderDirectoryListing(requestPath, resource); try { ByteBuffer output = ByteBuffer.wrap(builder.toString().getBytes(StandardCharsets.UTF_8)); exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/html; charset=UTF-8"); exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, String.valueOf(output.limit())); exchange.getResponseHeaders().put(Headers.LAST_MODIFIED, DateUtils.toDateString(new Date())); exchange.getResponseHeaders().put(Headers.CACHE_CONTROL, "must-revalidate"); Channels.writeBlocking(exchange.getResponseChannel(), output); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } catch (IOException e) { UndertowLogger.REQUEST_IO_LOGGER.ioException(e); exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); } exchange.endExchange(); } private static StringBuilder formatSize(StringBuilder builder, Long size) { if(size == null) { builder.append("???"); return builder; } int n = 1024 * 1024 * 1024; int type = 0; while (size < n && n >= 1024) { n /= 1024; type++; } long top = (size * 100) / n; long bottom = top % 100; top /= 100; builder.append(top); if (bottom > 0) { builder.append(".").append(bottom / 10); bottom %= 10; if (bottom > 0) { builder.append(bottom); } } switch (type) { case 0: builder.append(" GB"); break; case 1: builder.append(" MB"); break; case 2: builder.append(" KB"); break; } return builder; } private DirectoryUtils() { } /** * Constant Content * * @author Jason T. Greene */ public static class Blobs { public static final String FILE_JS="function growit() {\n" + " var table = document.getElementById(\"thetable\");\n" + "\n" + " var i = table.rows.length - 1;\n" + " while (i-- > 0) {\n" + " if (table.rows[i].id == \"eraseme\") {\n" + " table.deleteRow(i);\n" + " } else {\n" + " break;\n" + " }\n" + " }\n" + " table.style.height=\"\";\n" + " var i = 0;\n" + " while (table.offsetHeight < window.innerHeight - 24) {\n" + " i++;\n" + " var tbody = table.tBodies[0];\n" + " var row = tbody.insertRow(tbody.rows.length);\n" + " row.id=\"eraseme\";\n" + " var cell = row.insertCell(0);\n" + " if (table.rows.length % 2 != 0) {\n" + " row.className=\"even eveninvis\";\n" + " } else {\n" + " row.className=\"odd oddinvis\";\n" + " }\n" + "\n" + " cell.colSpan=3;\n" + " cell.appendChild(document.createTextNode(\"i\"));\n" + " }\n" + " table.style.height=\"100%\";\n" + " if (i > 0) {\n" + " document.documentElement.style.overflowY=\"hidden\";\n" + " } else {\n" + " document.documentElement.style.overflowY=\"auto\";\n" + " }\n" + "}"; public static final String FILE_JS_ETAG = md5(FILE_JS.getBytes(StandardCharsets.US_ASCII)); public static final String FILE_JS_ETAG_QUOTED = '"' + FILE_JS_ETAG + '"'; public static final String FILE_CSS = "body {\n" + " font-family: \"Lucida Grande\", \"Lucida Sans Unicode\", \"Trebuchet MS\", Helvetica, Arial, Verdana, sans-serif;\n" + " margin: 5px;\n" + "}\n" + "\n" + "th.loc {\n" + " background-image: linear-gradient(bottom, rgb(153,151,153) 8%, rgb(199,199,199) 54%);\n" + " background-image: -o-linear-gradient(bottom, rgb(153,151,153) 8%, rgb(199,199,199) 54%);\n" + " background-image: -moz-linear-gradient(bottom, rgb(153,151,153) 8%, rgb(199,199,199) 54%);\n" + " background-image: -webkit-linear-gradient(bottom, rgb(153,151,153) 8%, rgb(199,199,199) 54%);\n" + " background-image: -ms-linear-gradient(bottom, rgb(153,151,153) 8%, rgb(199,199,199) 54%);\n" + " \n" + " background-image: -webkit-gradient(\n" + " linear,\n" + " left bottom,\n" + " left top,\n" + " color-stop(0.08, rgb(153,151,153)),\n" + " color-stop(0.54, rgb(199,199,199))\n" + " );\n" + " color: black;\n" + " padding: 2px;\n" + " font-weight: normal;\n" + " border: solid 1px;\n" + " font-size: 150%;\n" + " text-align: left;\n" + "}\n" + "\n" + "th.label {\n" + " border: solid 1px;\n" + " text-align: left;\n" + " padding: 4px;\n" + " padding-left: 8px;\n" + " font-weight: normal;\n" + " font-size: small;\n" + " background-color: #e8e8e8;\n" + "}\n" + "\n" + "th.offset {\n" + " padding-left: 32px;\n" + "}\n" + "\n" + "th.footer {\n" + " font-size: 75%;\n" + " text-align: right;\n" + "}\n" + "\n" + "a.icon {\n" + " padding-left: 24px;\n" + " text-decoration: none;\n" + " color: black;\n" + "}\n" + "\n" + "a.icon:hover {\n" + " text-decoration: underline;\n" + "}\n" + "\n" + "table {\n" + " border: 1px solid;\n" + " border-spacing: 0px;\n" + " width: 100%;\n" + " border-collapse: collapse;\n" + "}\n" + "\n" + "tr.odd {\n" + " background-color: #f3f6fa;\n" + "}\n" + "\n" + "tr.odd td {\n" + " padding: 2px;\n" + " padding-left: 8px;\n" + " font-size: smaller;\n" + "}\n" + "\n" + "tr.even {\n" + " background-color: #ffffff;\n" + "}\n" + "\n" + "tr.even td {\n" + " padding: 2px;\n" + " padding-left: 8px;\n" + " font-size: smaller;\n" + "}\n" + "\n" + "tr.eveninvis td {\n" + " color: #ffffff;\n" + "}\n" + "\n" + "tr.oddinvis td {\n" + " color: #f3f6fa\n" + "}\n" + "\n" + "a.up {\n" + " background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABI0lEQVQ4y2P4//8/Ay7sM4nhPwjjUwMm0ua//Y+M0+e//QrSGDAfgvEZAjdgydHXcAzTXLjWDoxhhqBbhGLA1N0vwBhdM7ohMHVwA8yrzn4zLj/936j8FE7N6IaA1IL0gPQy2DVc+rnp3FeCmtENAekB6WXw7Lz1tWD5x/+wEIdhdI3o8iA9IL0MYZMfvq9a9+V/w+avcIzLAGQ1ID0gvQxJc56/aNn29X/vnm9wjMsAZDWtQD0gvQwFy94+6N37/f/Moz/gGJcByGpAekB6GarXf7427ciP/0vP/YRjdP/CMLIakB6QXobKDd9PN+769b91P2kYpAekl2HJhb8r11/583/9ZRIxUM+8U783MQCBGBDXAHEbibgGrBdfTiMGU2wAAPz+nxp+TnhDAAAAAElFTkSuQmCC') left center no-repeat; background-size: 16px 16px;\n" + "}\n" + "\n" + "a.dir {\n" + " background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXZwQWcAAAAQAAAAEABcxq3DAAAA+UlEQVQ4jWP4//8/AyUYTKTNf/sfGafPf/s1be47G5IMWHL0NRxP2f3mbcaCtz/RDUbHKAZM3f2CJAw3wLzq7Dfj8tP/jcpPkYRBekB6GewaLv3cdO7r/y0XSMMgPSC9DJ6dt74WLP/4v3TVZ5IwSA9IL0PY5Ifvq9Z9+d+w+StJGKQHpJchac7zFy3bvv7v3fONJNwK1APSy5C/7O2D3r3f/888+oMkDNID0stQvf7ztWlHfvxfeu4nSRikB6SXoXLD99ONu379b91PGgbpAellWHLh38r1V/78X3+ZRAzUM/fUr00MQCAGxDVA3EYirgHrpUpupAQDAPs+7c1tGDnPAAAAAElFTkSuQmCC') left center no-repeat; background-size: 16px 16px;\n" + "}\n" + "\n" + "a.file {\n" + " background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXZwQWcAAAAQAAAAEABcxq3DAAABM0lEQVQ4y5WSTW6DMBCF3xvzc4wuOEIO0kVAuUB7vJ4g3KBdoHSRROomEpusUaoAcaYLfmKoqVRLIxnJ7/M3YwJVBcknACv8b+1U9SvoP1bXa/3WNDVIAQmQBLsNOEsGQYAwDNcARgDqusbl+wIRA2NkBEyqP0s+kCOAQhhjICJdkaDIJDwEvQAhH+G+SHagWTsi4jHoAWYIOxYDZDjnb8Fn4Akvz6AHcAbx3Tp5ETwI3RwckyVtv4Fr4VEe9qq6bDB5tlnYWou2bWGtRRRF6jdwAm5Za1FVFc7nM0QERVG8A9hPDRaGpapomgZlWSJJEuR5ftpsNq8ADr9amC+SuN/vuN1uIIntdnvKsuwZwKf2wxgBxpjpX+dA4jjW4/H4kabpixt2AbvAmDX+XnsAB509ww+A8mAar+XXgQAAAABJRU5ErkJggg==') left center no-repeat;\n" + "}"; public static final String FILE_CSS_ETAG = md5(FILE_CSS.getBytes(StandardCharsets.US_ASCII)); public static final String FILE_CSS_ETAG_QUOTED = '"' + FILE_CSS_ETAG + '"'; public static final ByteBuffer FILE_CSS_BUFFER; public static final ByteBuffer FILE_JS_BUFFER; static { try { byte[] bytes = FILE_CSS.getBytes(StandardCharsets.US_ASCII); FILE_CSS_BUFFER = ByteBuffer.allocateDirect(bytes.length); FILE_CSS_BUFFER.put(bytes); FILE_CSS_BUFFER.flip(); bytes = FILE_JS.getBytes(StandardCharsets.US_ASCII); FILE_JS_BUFFER = ByteBuffer.allocateDirect(bytes.length); FILE_JS_BUFFER.put(bytes); FILE_JS_BUFFER.flip(); } catch (Exception e) { throw new IllegalStateException(e); } } } /** * Generate the MD5 hash out of the given {@link ByteBuffer} */ private static String md5(byte[] buffer) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(buffer); byte[] digest = md.digest(); return new String(FlexBase64.encodeBytes(digest, 0, digest.length, false), StandardCharsets.US_ASCII); } catch (NoSuchAlgorithmException e) { // Should never happen throw new InternalError("MD5 not supported on this platform"); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy