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

com.ning.http.client.uri.UriParser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2014 AsyncHttpClient Project. All rights reserved.
 *
 * This program is licensed to you under the Apache License Version 2.0,
 * and you may not use this file except in compliance with the Apache License Version 2.0.
 * You may obtain a copy of the Apache License Version 2.0 at
 *     http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Apache License Version 2.0 is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
 */
package com.ning.http.client.uri;

final class UriParser {

    public String scheme;
    public String host;
    public int port = -1;
    public String query;
    public String authority;
    public String path;
    public String userInfo;

    private int start, end = 0;
    private String urlWithoutQuery;

    private void trimRight(String originalUrl) {
        end = originalUrl.length();
        while (end > 0 && originalUrl.charAt(end - 1) <= ' ')
            end--;
    }

    private void trimLeft(String originalUrl) {
        while (start < end && originalUrl.charAt(start) <= ' ')
            start++;

        if (originalUrl.regionMatches(true, start, "url:", 0, 4))
            start += 4;
    }

    private boolean isFragmentOnly(String originalUrl) {
        return start < originalUrl.length() && originalUrl.charAt(start) == '#';
    }

    private boolean isValidProtocolChar(char c) {
        return Character.isLetterOrDigit(c) && c != '.' && c != '+' && c != '-';
    }

    private boolean isValidProtocolChars(String protocol) {
        for (int i = 1; i < protocol.length(); i++) {
            if (!isValidProtocolChar(protocol.charAt(i)))
                return false;
        }
        return true;
    }

    private boolean isValidProtocol(String protocol) {
        return protocol.length() > 0 && Character.isLetter(protocol.charAt(0)) && isValidProtocolChars(protocol);
    }

    private void computeInitialScheme(String originalUrl) {
        for (int i = start; i < end; i++) {
            char c = originalUrl.charAt(i);
            if (c == ':') {
                String s = originalUrl.substring(start, i);
                if (isValidProtocol(s)) {
                  scheme = s.toLowerCase();
                    start = i + 1;
                }
                break;
            } else if (c == '/')
                break;
        }
    }

    private boolean overrideWithContext(Uri context, String originalUrl) {

        boolean isRelative = false;

        // only use context if the schemes match
        if (context != null && (scheme == null || scheme.equalsIgnoreCase(context.getScheme()))) {

            // see RFC2396 5.2.3
            String contextPath = context.getPath();
            if (isNotEmpty(contextPath) && contextPath.charAt(0) == '/')
              scheme = null;

            if (scheme == null) {
                scheme = context.getScheme();
                userInfo = context.getUserInfo();
                host = context.getHost();
                port = context.getPort();
                path = contextPath;
                isRelative = true;
            }
        }
        return isRelative;
    }

    private void computeFragment(String originalUrl) {
        int charpPosition = originalUrl.indexOf('#', start);
        if (charpPosition >= 0) {
            end = charpPosition;
        }
    }

    private void inheritContextQuery(Uri context, boolean isRelative) {
        // see RFC2396 5.2.2: query and fragment inheritance
        if (isRelative && start == end) {
            query = context.getQuery();
        }
    }

    private boolean splitUrlAndQuery(String originalUrl) {
        boolean queryOnly = false;
        urlWithoutQuery = originalUrl;
        if (start < end) {
            int askPosition = originalUrl.indexOf('?');
            queryOnly = askPosition == start;
            if (askPosition != -1 && askPosition < end) {
                query = originalUrl.substring(askPosition + 1, end);
                if (end > askPosition)
                    end = askPosition;
                urlWithoutQuery = originalUrl.substring(0, askPosition);
            }
        }

        return queryOnly;
    }

    private boolean currentPositionStartsWith4Slashes() {
        return urlWithoutQuery.regionMatches(start, "////", 0, 4);
    }

    private boolean currentPositionStartsWith2Slashes() {
        return urlWithoutQuery.regionMatches(start, "//", 0, 2);
    }

    private void computeAuthority() {
        int authorityEndPosition = urlWithoutQuery.indexOf('/', start);
        if (authorityEndPosition < 0) {
            authorityEndPosition = urlWithoutQuery.indexOf('?', start);
            if (authorityEndPosition < 0)
                authorityEndPosition = end;
        }
        host = authority = urlWithoutQuery.substring(start, authorityEndPosition);
        start = authorityEndPosition;
    }

    private void computeUserInfo() {
        int atPosition = authority.indexOf('@');
        if (atPosition != -1) {
            userInfo = authority.substring(0, atPosition);
            host = authority.substring(atPosition + 1);
        } else
            userInfo = null;
    }

    private boolean isMaybeIPV6() {
        // If the host is surrounded by [ and ] then its an IPv6
        // literal address as specified in RFC2732
        return host.length() > 0 && host.charAt(0) == '[';
    }

    private void computeIPV6() {
        int positionAfterClosingSquareBrace = host.indexOf(']') + 1;
        if (positionAfterClosingSquareBrace > 1) {

            port = -1;

            if (host.length() > positionAfterClosingSquareBrace) {
                if (host.charAt(positionAfterClosingSquareBrace) == ':') {
                    // see RFC2396: port can be null
                    int portPosition = positionAfterClosingSquareBrace + 1;
                    if (host.length() > portPosition) {
                        port = Integer.parseInt(host.substring(portPosition));
                    }
                } else
                    throw new IllegalArgumentException("Invalid authority field: " + authority);
            }

            host = host.substring(0, positionAfterClosingSquareBrace);

        } else
            throw new IllegalArgumentException("Invalid authority field: " + authority);
    }

    private void computeRegularHostPort() {
        int colonPosition = host.indexOf(':');
        port = -1;
        if (colonPosition >= 0) {
            // see RFC2396: port can be null
            int portPosition = colonPosition + 1;
            if (host.length() > portPosition)
                port = Integer.parseInt(host.substring(portPosition));
            host = host.substring(0, colonPosition);
        }
    }

    // /./
    private void removeEmbeddedDot() {
        path = path.replace("/./", "/");
    }

    // /../
    private void removeEmbedded2Dots() {
        int i = 0;
        while ((i = path.indexOf("/../", i)) >= 0) {
            if (i > 0) {
                end = path.lastIndexOf('/', i - 1);
                if (end >= 0 && path.indexOf("/../", end) != 0) {
                    path = path.substring(0, end) + path.substring(i + 3);
                    i = 0;
                } else if (end == 0) {
                    break;
                }
            } else
                i = i + 3;
        }
    }

    private void removeTailing2Dots() {
        while (path.endsWith("/..")) {
            end = path.lastIndexOf('/', path.length() - 4);
            if (end >= 0)
                path = path.substring(0, end + 1);
            else
                break;
        }
    }

    private void removeStartingDot() {
        if (path.startsWith("./") && path.length() > 2)
            path = path.substring(2);
    }

    private void removeTrailingDot() {
        if (path.endsWith("/."))
            path = path.substring(0, path.length() - 1);
    }

    private void initRelativePath() {
        int lastSlashPosition = path.lastIndexOf('/');
        String pathEnd = urlWithoutQuery.substring(start, end);

        if (lastSlashPosition == -1)
            path = authority != null ? "/" + pathEnd : pathEnd;
        else
            path = path.substring(0, lastSlashPosition + 1) + pathEnd;
    }

    private void handlePathDots() {
        if (path.indexOf('.') != -1) {
            removeEmbeddedDot();
            removeEmbedded2Dots();
            removeTailing2Dots();
            removeStartingDot();
            removeTrailingDot();
        }
    }

    private void parseAuthority() {
        if (!currentPositionStartsWith4Slashes() && currentPositionStartsWith2Slashes()) {
            start += 2;

            computeAuthority();
            computeUserInfo();

            if (host != null) {
                if (isMaybeIPV6())
                    computeIPV6();
                else
                    computeRegularHostPort();
            }

            if (port < -1)
                throw new IllegalArgumentException("Invalid port number :" + port);

            // see RFC2396 5.2.4: ignore context path if authority is defined
            if (isNotEmpty(authority))
                path = "";
        }
    }

    private void handleRelativePath() {
        initRelativePath();
        handlePathDots();
    }

    private void computeRegularPath() {
        if (urlWithoutQuery.charAt(start) == '/')
            path = urlWithoutQuery.substring(start, end);

        else if (isNotEmpty(path))
            handleRelativePath();

        else {
            String pathEnd = urlWithoutQuery.substring(start, end);
            path = authority != null ? "/" + pathEnd : pathEnd;
        }
    }

    private void computeQueryOnlyPath() {
        int lastSlashPosition = path.lastIndexOf('/');
        path = lastSlashPosition < 0 ? "/" : path.substring(0, lastSlashPosition) + "/";
    }

    private void computePath(boolean queryOnly) {
        // Parse the file path if any
        if (start < end)
            computeRegularPath();
        else if (queryOnly && path != null)
            computeQueryOnlyPath();
        else if (path == null)
            path = "";
    }

    public void parse(Uri context, final String originalUrl) {

        if (originalUrl == null)
            throw new NullPointerException("originalUrl");

        boolean isRelative = false;

        trimRight(originalUrl);
        trimLeft(originalUrl);
        if (!isFragmentOnly(originalUrl))
            computeInitialScheme(originalUrl);
        overrideWithContext(context, originalUrl);
        computeFragment(originalUrl);
        inheritContextQuery(context, isRelative);

        boolean queryOnly = splitUrlAndQuery(originalUrl);
        parseAuthority();
        computePath(queryOnly);
    }

    private static boolean isNotEmpty(String string) {
        return string != null && string.length() > 0;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy