org.asynchttpclient.uri.UriParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of async-http-client Show documentation
Show all versions of async-http-client Show documentation
The Async Http Client (AHC) classes.
/*
* 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 org.asynchttpclient.uri;
import static org.asynchttpclient.util.Assertions.assertNotNull;
import static org.asynchttpclient.util.MiscUtils.isNonEmpty;
final class UriParser {
public String scheme;
public String host;
public int port = -1;
public String query;
private String authority;
public String path;
public String userInfo;
private String originalUrl;
private int start, end, currentIndex = 0;
private void trimLeft() {
while (start < end && originalUrl.charAt(start) <= ' ') {
start++;
}
if (originalUrl.regionMatches(true, start, "url:", 0, 4)) {
start += 4;
}
}
private void trimRight() {
end = originalUrl.length();
while (end > 0 && originalUrl.charAt(end - 1) <= ' ') {
end--;
}
}
private boolean isFragmentOnly() {
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() {
for (int i = currentIndex; i < end; i++) {
char c = originalUrl.charAt(i);
if (c == ':') {
String s = originalUrl.substring(currentIndex, i);
if (isValidProtocol(s)) {
scheme = s.toLowerCase();
currentIndex = i + 1;
}
break;
} else if (c == '/') {
break;
}
}
}
private boolean overrideWithContext(Uri context) {
boolean isRelative = false;
// use context only if schemes match
if (context != null && (scheme == null || scheme.equalsIgnoreCase(context.getScheme()))) {
// see RFC2396 5.2.3
String contextPath = context.getPath();
if (isNonEmpty(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 int findWithinCurrentRange(char c) {
int pos = originalUrl.indexOf(c, currentIndex);
return pos > end ? -1 : pos;
}
private void trimFragment() {
int charpPosition = findWithinCurrentRange('#');
if (charpPosition >= 0) {
end = charpPosition;
}
}
private void inheritContextQuery(Uri context, boolean isRelative) {
// see RFC2396 5.2.2: query and fragment inheritance
if (isRelative && currentIndex == end) {
query = context.getQuery();
}
}
private boolean computeQuery() {
if (currentIndex < end) {
int askPosition = findWithinCurrentRange('?');
if (askPosition != -1) {
query = originalUrl.substring(askPosition + 1, end);
if (end > askPosition) {
end = askPosition;
}
return askPosition == currentIndex;
}
}
return false;
}
private boolean currentPositionStartsWith4Slashes() {
return originalUrl.regionMatches(currentIndex, "////", 0, 4);
}
private boolean currentPositionStartsWith2Slashes() {
return originalUrl.regionMatches(currentIndex, "//", 0, 2);
}
private void computeAuthority() {
int authorityEndPosition = findWithinCurrentRange('/');
if (authorityEndPosition == -1) {
authorityEndPosition = findWithinCurrentRange('?');
if (authorityEndPosition == -1) {
authorityEndPosition = end;
}
}
host = authority = originalUrl.substring(currentIndex, authorityEndPosition);
currentIndex = 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 handleRelativePath() {
int lastSlashPosition = path.lastIndexOf('/');
String pathEnd = originalUrl.substring(currentIndex, 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()) {
currentIndex += 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 (isNonEmpty(authority)) {
path = "";
}
}
}
private void computeRegularPath() {
if (originalUrl.charAt(currentIndex) == '/') {
path = originalUrl.substring(currentIndex, end);
} else if (isNonEmpty(path)) {
handleRelativePath();
} else {
String pathEnd = originalUrl.substring(currentIndex, end);
path = isNonEmpty(pathEnd) && pathEnd.charAt(0) != '/' ? "/" + pathEnd : pathEnd;
}
handlePathDots();
}
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 (currentIndex < end) {
computeRegularPath();
} else if (queryOnly && path != null) {
computeQueryOnlyPath();
} else if (path == null) {
path = "";
}
}
public void parse(Uri context, final String originalUrl) {
assertNotNull(originalUrl, "originalUrl");
this.originalUrl = originalUrl;
this.end = originalUrl.length();
trimLeft();
trimRight();
currentIndex = start;
if (!isFragmentOnly()) {
computeInitialScheme();
}
boolean isRelative = overrideWithContext(context);
trimFragment();
inheritContextQuery(context, isRelative);
boolean queryOnly = computeQuery();
parseAuthority();
computePath(queryOnly);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy