com.kolich.curacao.util.helpers.UrlPathHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.kolich.curacao.util.helpers.UrlPathHelper.java

Source

/**
 * Copyright (c) 2015 Mark S. Kolich
 * http://mark.koli.ch
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

package com.kolich.curacao.util.helpers;

import com.kolich.curacao.CuracaoContext;

import javax.servlet.http.HttpServletRequest;

import static org.apache.commons.lang3.StringUtils.isNotBlank;

public final class UrlPathHelper {

    // Cannot instantiate.
    private UrlPathHelper() {
    }

    private static class LazyHolder {
        private static final UrlPathHelper instance__ = new UrlPathHelper();
    }

    public static final UrlPathHelper getInstance() {
        return LazyHolder.instance__;
    }

    public final String getPathWithinApplication(final CuracaoContext ctx) {
        return getPathWithinApplication(ctx.request_, ctx.servletCtx_.getContextPath());
    }

    /**
     * Return the path within the web application for the given request.
     * @param request current HTTP request
     * @return the path within the web application
     */
    public final String getPathWithinApplication(final HttpServletRequest request, final String contextPath) {
        final String requestUri = getRequestUri(request);
        final String path = getRemainingPath(requestUri, contextPath, true);
        if (path != null) {
            // Normal case: URI contains context path.
            return (isNotBlank(path) ? path : "/");
        } else {
            return requestUri;
        }
    }

    /**
     * Return the request URI for the given request, detecting an include request
     * URL if called within a RequestDispatcher include.
     * <p>As the value returned by {@code request.getRequestURI()} is <i>not</i>
     * decoded by the servlet container, this method will decode it.
     * <p>The URI that the web container resolves <i>should</i> be correct, but some
     * containers like JBoss/Jetty incorrectly include ";" strings like ";jsessionid"
     * in the URI. This method cuts off such incorrect appendices.
     * @param request current HTTP request
     * @return the request URI
     */
    private final String getRequestUri(final HttpServletRequest request) {
        final String uri = request.getRequestURI();
        // <https://github.com/markkolich/curacao/issues/17>
        // Apparently it's possible for HttpServletRequest.getRequestURI() to
        // return null.  Looking at Jetty 9 source specifically, there is a case
        // in which the Servlet container could return null although the servlet
        // spec seems to imply that returning null is invalid/impossible.  I've
        // decided not to do anything about this and just ignore the fact that
        // the servlet container could be wrong and have bugs, and therefore,
        // it's not Curacao's job to work around those bugs by adding null
        // checks everywhere.
        return decodeAndCleanUriString(uri);
    }

    /**
     * Decode the supplied URI string and strips any extraneous portion after a ';'.
     */
    private final String decodeAndCleanUriString(final String uri) {
        return removeJsessionid(removeSemicolonContent(uri));
    }

    private final String removeSemicolonContent(final String requestUri) {
        String uri = requestUri;
        int semicolonIndex = uri.indexOf(';');
        while (semicolonIndex != -1) {
            final int slashIndex = uri.indexOf('/', semicolonIndex);
            final String start = uri.substring(0, semicolonIndex);
            uri = (slashIndex != -1) ? start + uri.substring(slashIndex) : start;
            semicolonIndex = uri.indexOf(';', semicolonIndex);
        }
        return uri;
    }

    private final String removeJsessionid(final String requestUri) {
        String uri = requestUri;
        int startIndex = uri.indexOf(";jsessionid=");
        if (startIndex != -1) {
            final int endIndex = uri.indexOf(';', startIndex + 12);
            final String start = uri.substring(0, startIndex);
            uri = (endIndex != -1) ? start + uri.substring(endIndex) : start;
        }
        return uri;
    }

    /**
     * Match the given "mapping" to the start of the "requestUri" and if there
     * is a match return the extra part. This method is needed because the
     * context path and the servlet path returned by the HttpServletRequest are
     * stripped of semicolon content unlike the requesUri.
     */
    private final String getRemainingPath(final String requestUri, final String mapping, final boolean ignoreCase) {
        int index1 = 0;
        int index2 = 0;
        for (; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) {
            char c1 = requestUri.charAt(index1);
            char c2 = mapping.charAt(index2);
            if (c1 == ';') {
                index1 = requestUri.indexOf('/', index1);
                if (index1 == -1) {
                    return null;
                }
                c1 = requestUri.charAt(index1);
            }
            if (c1 == c2) {
                continue;
            }
            if (ignoreCase && (Character.toLowerCase(c1) == Character.toLowerCase(c2))) {
                continue;
            }
            return null;
        }
        if (index2 != mapping.length()) {
            return null;
        }
        if (index1 == requestUri.length()) {
            return "";
        } else if (requestUri.charAt(index1) == ';') {
            index1 = requestUri.indexOf('/', index1);
        }
        return (index1 != -1) ? requestUri.substring(index1) : "";
    }

}