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

com.networknt.url.HttpURL Maven / Gradle / Ivy

The newest version!
/* Copyright 2010-2018 Norconex Inc.
 *
 * 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 com.networknt.url;

import com.networknt.utility.CharUtils;
import com.networknt.utility.RegExUtils;
import com.networknt.utility.StringUtils;

import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Objects;

/**
 * This class act as a mutable URL, which could be a replacement
 * or "wrapper" to the {@link URL} class. It can also be used as a safer way
 * to build a {@link URL} or a {@link URI} instance as it will properly escape
 * appropriate characters before creating those.
 *
 * @author Pascal Essiembre
 */
//TODO rename MutableURL
public class HttpURL implements Serializable {

    private static final long serialVersionUID = -8886393027925815099L;

    /** Default URL HTTP Port. */
    public static final int DEFAULT_HTTP_PORT = 80;
    /** Default Secure URL HTTP Port. */
    public static final int DEFAULT_HTTPS_PORT = 443;

    /** Constant for "http" protocol. */
    public static final String PROTOCOL_HTTP = "http";
    /** Constant for "https" protocol. */
    public static final String PROTOCOL_HTTPS = "https";

    private QueryString queryString;
    private String host;
    private int port = -1;
    private String path;
    private String protocol;
    private final String encoding;
    private String fragment;

    /**
     * Creates a blank HttpURL using UTF-8 for URL encoding.
     */
    public HttpURL() {
        this("", null);
    }

    /**
     * Creates a new HttpURL from the URL object using UTF-8 for URL encoding.
     * @param url a URL
     */
    public HttpURL(URL url) {
        this(url.toString());
    }
    /**
     * Creates a new HttpURL from the URL string using UTF-8 for URL encoding.
     * @param url a URL
     */
    public HttpURL(String url) {
        this(url, null);
    }

    /**
     * Creates a new HttpURL from the URL object using the provided encoding
     * for URL encoding.
     * @param url a URL
     * @param encoding character encoding
     * @since 1.7.0
     */
    public HttpURL(URL url, String encoding) {
        this(url.toString(), encoding);
    }
    /**
     * Creates a new HttpURL from the URL string using the provided encoding
     * for URL encoding.
     * @param url a URL string
     * @param encoding character encoding
     * @since 1.7.0
     */
    public HttpURL(String url, String encoding) {
        if (StringUtils.isBlank(encoding)) {
            this.encoding = StandardCharsets.UTF_8.toString();
        } else {
            this.encoding = encoding;
        }

        String u = StringUtils.trimToEmpty(url);
        if (u.matches("[a-zA-Z][a-zA-Z0-9\\+\\-\\.]*:.*")) {
            URL urlwrap;
            try {
                urlwrap = new URL(u);
            } catch (MalformedURLException e) {
                throw new RuntimeException("Could not interpret URL: " + u, e);
            }
            protocol = StringUtils.substringBefore(u, ":");
            host = urlwrap.getHost();
            port = urlwrap.getPort();
            if (port < 0) {
                if (u.toLowerCase().startsWith(PROTOCOL_HTTPS)) {
                    port = DEFAULT_HTTPS_PORT;
                } else if (u.toLowerCase().startsWith(PROTOCOL_HTTP)) {
                    port = DEFAULT_HTTP_PORT;
                }
            }
            path = urlwrap.getPath();
            fragment = urlwrap.getRef();
        } else {
            path = u.replaceFirst("^(.*?)([\\?\\#])(.*)", "$1");
            if (u.contains("#")) {
                fragment = u.replaceFirst("^(.*?)(\\#)(.*)", "$3");
            }
        }

        // Parameters
        if (u.contains("?")) {
            queryString = new QueryString(u, encoding);
        }
    }


    /**
     * Gets the character encoding. Default is UTF-8.
     * @return character encoding
     * @since 1.7.0
     */
    public String getEncoding() {
        return encoding;
    }

    /**
     * Gets the URL path.
     * @return URL path
     */
    public String getPath() {
        return path;
    }
    /**
     * Sets the URL path.
     * @param path url path
     */
    public void setPath(String path) {
        this.path = path;
    }

    /**
     * Gets the URL query string.
     * @return URL query string, or null if none
     */
    public QueryString getQueryString() {
        return queryString;
    }
    /**
     * Sets the URL query string.
     * @param queryString the query string
     */
    public void setQueryString(QueryString queryString) {
        this.queryString = queryString;
    }

    /**
     * Gets the host portion of the URL.
     * @return the host portion of the URL
     */
    public String getHost() {
        return host;
    }
    /**
     * Sets the host portion of the URL.
     * @param host the host portion of the URL
     */
    public void setHost(String host) {
        this.host = host;
    }

    /**
     * Gets the protocol portion of the URL (e.g. http, https);
     * @return the protocol portion of the URL
     */
    public String getProtocol() {
        return protocol;
    }
    /**
     * Sets the protocol portion of the URL.
     * @param protocol the protocol portion of the URL
     */
    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }
    /**
     * Whether this URL is secure (e.g. https).
     * @return true if protocol is secure
     */
    public boolean isSecure() {
        return getProtocol().equalsIgnoreCase(PROTOCOL_HTTPS);
    }

    /**
     * Gets the URL port. If the protocol is other than
     * http or https, the port is -1 when
     * not specified.
     * @return the URL port
     */
    public int getPort() {
        return port;
    }
    /**
     * Sets the URL port.
     * @param port the URL port
     */
    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Gets the URL fragment.
     * @return the fragment
     * @since 1.8.0
     */
    public String getFragment() {
        return fragment;
    }
    /**
     * Sets the URL fragment.
     * @param fragment the fragment to set
     * @since 1.8.0
     */
    public void setFragment(String fragment) {
        this.fragment = fragment;
    }

    /**
     * Gets the last URL path segment without the query string.
     * If there are segment to return,
     * an empty string will be returned instead.
     * @return the last URL path segment
     */
    public String getLastPathSegment() {
        if (StringUtils.isBlank(path)) {
            return StringUtils.EMPTY;
        }
        String segment = path;
        segment = StringUtils.substringAfterLast(segment, "/");
        return segment;
    }
    /**
     * Converts this HttpURL to a regular {@link URL}, making sure
     * appropriate characters are escaped properly.
     * @return a URL
     * @throws RuntimeException when URL is malformed
     */
    public URL toURL() {
        String url = toString();
        try {
            return new URL(url);
        } catch (MalformedURLException e) {
            throw new RuntimeException("Cannot convert to URL: " + url, e);
        }
    }

    /**
     * Gets the root of this HttpUrl. That is the left part of a URL up to
     * and including the host name. A null or empty string returns
     * a null document root.
     * @return left part of a URL up to (and including the host name
     * @throws RuntimeException when URL is malformed
     * @since 1.8.0
     */
    public String getRoot() {
        return getRoot(toString());
    }

    /**
     * Converts this HttpURL to a {@link URI}, making sure
     * appropriate characters are escaped properly.
     * @return a URI
     * @since 1.7.0
     * @throws RuntimeException when URL is malformed
     */
    public URI toURI() {
        String url = toString();
        try {
            return new URI(url);
        } catch (URISyntaxException e) {
            throw new RuntimeException("Cannot convert to URI: " + url, e);
        }
    }
    /**
     * 

* Converts the supplied URL to a {@link URL}, making sure * appropriate characters are encoded properly using UTF-8. This method * is a short form of:
* new HttpURL("http://example.com").toURL(); *

* @param url a URL string * @return a URL object * @since 1.7.0 * @throws RuntimeException when URL is malformed */ public static URL toURL(String url) { return new HttpURL(url).toURL(); } /** *

Converts the supplied URL to a {@link URI}, making sure * appropriate characters are encoded properly using UTF-8. This method * is a short form of:
* new HttpURL("http://example.com").toURI(); *

* @param url a URL string * @return a URI object * @since 1.7.0 * @throws RuntimeException when URL is malformed */ public static URI toURI(String url) { return new HttpURL(url).toURI(); } /** *

Gets the root of a URL. That is the left part of a URL up to and * including the host name. A null or empty string returns * a null document root. * This method is a short form of:
* new HttpURL("http://example.com/path").getRoot(); *

* @param url a URL string * @return left part of a URL up to (and including the host name * @since 1.8.0 */ public static String getRoot(String url) { if (StringUtils.isBlank(url)) { return null; } return RegExUtils.replacePattern(url, "(.*?://.*?)([/?#].*)", "$1"); } /** * Returns a string representation of this URL, properly encoded. * @return URL as a string */ @Override public String toString() { StringBuilder b = new StringBuilder(); if (StringUtils.isNotBlank(protocol)) { b.append(protocol); b.append("://"); } if (StringUtils.isNotBlank(host)) { b.append(host); } if (!isPortDefault() && port != -1) { b.append(':'); b.append(port); } if (StringUtils.isNotBlank(path)) { // If no scheme/host/port, leave the path as is if (b.length() > 0 && !path.startsWith("/")) { b.append('/'); } b.append(encodePath(path)); } if (queryString != null && !queryString.isEmpty()) { b.append(queryString.toString()); } if (fragment != null) { b.append("#"); b.append(encodePath(fragment)); } return b.toString(); } /** * Whether this URL uses the default port for the protocol. The default * port is 80 for "http" protocol, and 443 for "https". Other protocols * are not supported and this method will always return false * for them. * @return true if the URL is using the default port. * @since 1.8.0 */ public boolean isPortDefault() { return (PROTOCOL_HTTPS.equalsIgnoreCase(protocol) && port == DEFAULT_HTTPS_PORT) || (PROTOCOL_HTTP.equalsIgnoreCase(protocol) && port == DEFAULT_HTTP_PORT); } /** *

URL-Encodes the query string portion of a URL. The entire * string supplied is assumed to be a query string. * @param queryString URL query string * @return encoded path * @since 1.8.0 */ public static String encodeQueryString(String queryString) { if (StringUtils.isBlank(queryString)) { return queryString; } return new QueryString(queryString).toString(); } /** *

URL-Encodes a URL path. The entire string supplied is assumed * to be a URL path. Unsafe characters are percent-encoded using UTF-8 * (as specified by W3C standard). * @param path path portion of a URL * @return encoded path * @since 1.7.0 */ public static String encodePath(String path) { // Any characters that are not one of the following are // percent-encoded (including spaces): // a-z A-Z 0-9 . - _ ~ ! $ & ' ( ) * + , ; = : @ / % if (StringUtils.isBlank(path)) { return path; } StringBuilder sb = new StringBuilder(); for (char ch : path.toCharArray()) { // Space to plus sign if (ch == ' ') { sb.append("%20"); // Valid: keep it as is. } else if (CharUtils.isAsciiAlphanumeric(ch) || ".-_~!$&'()*+,;=:@/%".indexOf(ch) != -1) { sb.append(ch); // Invalid: encode it } else { byte[] bytes; bytes = Character.toString(ch).getBytes(StandardCharsets.UTF_8); for (byte b : bytes) { sb.append('%'); int upper = (((int) b) >> 4) & 0xf; sb.append(Integer.toHexString( upper).toUpperCase(Locale.US)); int lower = ((int) b) & 0xf; sb.append(Integer.toHexString( lower).toUpperCase(Locale.US)); } } } return sb.toString(); } /** * Converts a relative URL to an absolute one, based on the supplied * base URL. The base URL is assumed to be a valid URL. Behavior * is unexpected when base URL is invalid. * @param baseURL URL to the reference is relative to * @param relativeURL the relative URL portion to transform to absolute * @return absolute URL * @since 1.8.0 */ public static String toAbsolute(String baseURL, String relativeURL) { String relURL = relativeURL; // Relative to protocol if (relURL.startsWith("//")) { return StringUtils.substringBefore(baseURL, "//") + "//" + StringUtils.substringAfter(relURL, "//"); } // Relative to domain name if (relURL.startsWith("/")) { return getRoot(baseURL) + relURL; } // Relative to full full page URL minus ? or # if (relURL.startsWith("?") || relURL.startsWith("#")) { // this is a relative url and should have the full page base return baseURL.replaceFirst("(.*?)([\\?\\#])(.*)", "$1") + relURL; } // Relative to last directory/segment if (!relURL.contains("://")) { String base = baseURL.replaceFirst("(.*?)([\\?\\#])(.*)", "$1"); if (StringUtils.countMatches(base, '/') > 2) { base = base.replaceFirst("(.*/)(.*)", "$1"); } if (base.endsWith("/")) { // This is a URL relative to the last URL segment relURL = base + relURL; } else { relURL = base + "/" + relURL; } } // Not detected as relative, so return as is return relURL; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HttpURL httpURL = (HttpURL) o; return port == httpURL.port && Objects.equals(host, httpURL.host) && Objects.equals(path, httpURL.path) && Objects.equals(protocol, httpURL.protocol) && Objects.equals(encoding, httpURL.encoding) && Objects.equals(fragment, httpURL.fragment); } @Override public int hashCode() { return Objects.hash(host, port, path, protocol, encoding, fragment); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy