1037 lines
39 KiB
Java
1037 lines
39 KiB
Java
package okhttp3;
|
|
|
|
import java.net.MalformedURLException;
|
|
import java.net.URI;
|
|
import java.net.URISyntaxException;
|
|
import java.net.URL;
|
|
import java.nio.charset.Charset;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.LinkedHashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import okhttp3.internal.Util;
|
|
import okhttp3.internal.publicsuffix.PublicSuffixDatabase;
|
|
import okio.Buffer;
|
|
|
|
/* loaded from: classes2.dex */
|
|
public final class HttpUrl {
|
|
static final String FORM_ENCODE_SET = " \"':;<=>@[]^`{}|/\\?#&!$(),~";
|
|
static final String FRAGMENT_ENCODE_SET = "";
|
|
static final String FRAGMENT_ENCODE_SET_URI = " \"#<>\\^`{|}";
|
|
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
|
static final String PASSWORD_ENCODE_SET = " \"':;<=>@[]^`{}|/\\?#";
|
|
static final String PATH_SEGMENT_ENCODE_SET = " \"<>^`{}|/\\?#";
|
|
static final String PATH_SEGMENT_ENCODE_SET_URI = "[]";
|
|
static final String QUERY_COMPONENT_ENCODE_SET = " !\"#$&'(),/:;<=>?@[]\\^`{|}~";
|
|
static final String QUERY_COMPONENT_ENCODE_SET_URI = "\\^`{|}";
|
|
static final String QUERY_COMPONENT_REENCODE_SET = " \"'<>#&=";
|
|
static final String QUERY_ENCODE_SET = " \"'<>#";
|
|
static final String USERNAME_ENCODE_SET = " \"':;<=>@[]^`{}|/\\?#";
|
|
private final String fragment;
|
|
final String host;
|
|
private final String password;
|
|
private final List<String> pathSegments;
|
|
final int port;
|
|
private final List<String> queryNamesAndValues;
|
|
final String scheme;
|
|
private final String url;
|
|
private final String username;
|
|
|
|
HttpUrl(Builder builder) {
|
|
this.scheme = builder.scheme;
|
|
this.username = percentDecode(builder.encodedUsername, false);
|
|
this.password = percentDecode(builder.encodedPassword, false);
|
|
this.host = builder.host;
|
|
this.port = builder.effectivePort();
|
|
this.pathSegments = percentDecode(builder.encodedPathSegments, false);
|
|
List<String> list = builder.encodedQueryNamesAndValues;
|
|
this.queryNamesAndValues = list != null ? percentDecode(list, true) : null;
|
|
String str = builder.encodedFragment;
|
|
this.fragment = str != null ? percentDecode(str, false) : null;
|
|
this.url = builder.toString();
|
|
}
|
|
|
|
static String canonicalize(String str, int i, int i2, String str2, boolean z, boolean z2, boolean z3, boolean z4, Charset charset) {
|
|
int i3 = i;
|
|
while (i3 < i2) {
|
|
int codePointAt = str.codePointAt(i3);
|
|
if (codePointAt >= 32 && codePointAt != 127 && (codePointAt < 128 || !z4)) {
|
|
if (str2.indexOf(codePointAt) == -1 && ((codePointAt != 37 || (z && (!z2 || percentEncoded(str, i3, i2)))) && (codePointAt != 43 || !z3))) {
|
|
i3 += Character.charCount(codePointAt);
|
|
}
|
|
}
|
|
Buffer buffer = new Buffer();
|
|
buffer.writeUtf8(str, i, i3);
|
|
canonicalize(buffer, str, i3, i2, str2, z, z2, z3, z4, charset);
|
|
return buffer.readUtf8();
|
|
}
|
|
return str.substring(i, i2);
|
|
}
|
|
|
|
public static int defaultPort(String str) {
|
|
if (str.equals("http")) {
|
|
return 80;
|
|
}
|
|
return str.equals("https") ? 443 : -1;
|
|
}
|
|
|
|
public static HttpUrl get(String str) {
|
|
return new Builder().parse(null, str).build();
|
|
}
|
|
|
|
static void namesAndValuesToQueryString(StringBuilder sb, List<String> list) {
|
|
int size = list.size();
|
|
for (int i = 0; i < size; i += 2) {
|
|
String str = list.get(i);
|
|
String str2 = list.get(i + 1);
|
|
if (i > 0) {
|
|
sb.append('&');
|
|
}
|
|
sb.append(str);
|
|
if (str2 != null) {
|
|
sb.append('=');
|
|
sb.append(str2);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static HttpUrl parse(String str) {
|
|
try {
|
|
return get(str);
|
|
} catch (IllegalArgumentException unused) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static void pathSegmentsToString(StringBuilder sb, List<String> list) {
|
|
int size = list.size();
|
|
for (int i = 0; i < size; i++) {
|
|
sb.append('/');
|
|
sb.append(list.get(i));
|
|
}
|
|
}
|
|
|
|
static String percentDecode(String str, boolean z) {
|
|
return percentDecode(str, 0, str.length(), z);
|
|
}
|
|
|
|
static boolean percentEncoded(String str, int i, int i2) {
|
|
int i3 = i + 2;
|
|
return i3 < i2 && str.charAt(i) == '%' && Util.decodeHexDigit(str.charAt(i + 1)) != -1 && Util.decodeHexDigit(str.charAt(i3)) != -1;
|
|
}
|
|
|
|
static List<String> queryStringToNamesAndValues(String str) {
|
|
ArrayList arrayList = new ArrayList();
|
|
int i = 0;
|
|
while (i <= str.length()) {
|
|
int indexOf = str.indexOf(38, i);
|
|
if (indexOf == -1) {
|
|
indexOf = str.length();
|
|
}
|
|
int indexOf2 = str.indexOf(61, i);
|
|
if (indexOf2 == -1 || indexOf2 > indexOf) {
|
|
arrayList.add(str.substring(i, indexOf));
|
|
arrayList.add(null);
|
|
} else {
|
|
arrayList.add(str.substring(i, indexOf2));
|
|
arrayList.add(str.substring(indexOf2 + 1, indexOf));
|
|
}
|
|
i = indexOf + 1;
|
|
}
|
|
return arrayList;
|
|
}
|
|
|
|
public String encodedFragment() {
|
|
if (this.fragment == null) {
|
|
return null;
|
|
}
|
|
return this.url.substring(this.url.indexOf(35) + 1);
|
|
}
|
|
|
|
public String encodedPassword() {
|
|
if (this.password.isEmpty()) {
|
|
return "";
|
|
}
|
|
return this.url.substring(this.url.indexOf(58, this.scheme.length() + 3) + 1, this.url.indexOf(64));
|
|
}
|
|
|
|
public String encodedPath() {
|
|
int indexOf = this.url.indexOf(47, this.scheme.length() + 3);
|
|
String str = this.url;
|
|
return this.url.substring(indexOf, Util.delimiterOffset(str, indexOf, str.length(), "?#"));
|
|
}
|
|
|
|
public List<String> encodedPathSegments() {
|
|
int indexOf = this.url.indexOf(47, this.scheme.length() + 3);
|
|
String str = this.url;
|
|
int delimiterOffset = Util.delimiterOffset(str, indexOf, str.length(), "?#");
|
|
ArrayList arrayList = new ArrayList();
|
|
while (indexOf < delimiterOffset) {
|
|
int i = indexOf + 1;
|
|
int delimiterOffset2 = Util.delimiterOffset(this.url, i, delimiterOffset, '/');
|
|
arrayList.add(this.url.substring(i, delimiterOffset2));
|
|
indexOf = delimiterOffset2;
|
|
}
|
|
return arrayList;
|
|
}
|
|
|
|
public String encodedQuery() {
|
|
if (this.queryNamesAndValues == null) {
|
|
return null;
|
|
}
|
|
int indexOf = this.url.indexOf(63) + 1;
|
|
String str = this.url;
|
|
return this.url.substring(indexOf, Util.delimiterOffset(str, indexOf, str.length(), '#'));
|
|
}
|
|
|
|
public String encodedUsername() {
|
|
if (this.username.isEmpty()) {
|
|
return "";
|
|
}
|
|
int length = this.scheme.length() + 3;
|
|
String str = this.url;
|
|
return this.url.substring(length, Util.delimiterOffset(str, length, str.length(), ":@"));
|
|
}
|
|
|
|
public boolean equals(Object obj) {
|
|
return (obj instanceof HttpUrl) && ((HttpUrl) obj).url.equals(this.url);
|
|
}
|
|
|
|
public String fragment() {
|
|
return this.fragment;
|
|
}
|
|
|
|
public int hashCode() {
|
|
return this.url.hashCode();
|
|
}
|
|
|
|
public String host() {
|
|
return this.host;
|
|
}
|
|
|
|
public boolean isHttps() {
|
|
return this.scheme.equals("https");
|
|
}
|
|
|
|
public Builder newBuilder() {
|
|
Builder builder = new Builder();
|
|
builder.scheme = this.scheme;
|
|
builder.encodedUsername = encodedUsername();
|
|
builder.encodedPassword = encodedPassword();
|
|
builder.host = this.host;
|
|
builder.port = this.port != defaultPort(this.scheme) ? this.port : -1;
|
|
builder.encodedPathSegments.clear();
|
|
builder.encodedPathSegments.addAll(encodedPathSegments());
|
|
builder.encodedQuery(encodedQuery());
|
|
builder.encodedFragment = encodedFragment();
|
|
return builder;
|
|
}
|
|
|
|
public String password() {
|
|
return this.password;
|
|
}
|
|
|
|
public List<String> pathSegments() {
|
|
return this.pathSegments;
|
|
}
|
|
|
|
public int pathSize() {
|
|
return this.pathSegments.size();
|
|
}
|
|
|
|
public int port() {
|
|
return this.port;
|
|
}
|
|
|
|
public String query() {
|
|
if (this.queryNamesAndValues == null) {
|
|
return null;
|
|
}
|
|
StringBuilder sb = new StringBuilder();
|
|
namesAndValuesToQueryString(sb, this.queryNamesAndValues);
|
|
return sb.toString();
|
|
}
|
|
|
|
public String queryParameter(String str) {
|
|
List<String> list = this.queryNamesAndValues;
|
|
if (list == null) {
|
|
return null;
|
|
}
|
|
int size = list.size();
|
|
for (int i = 0; i < size; i += 2) {
|
|
if (str.equals(this.queryNamesAndValues.get(i))) {
|
|
return this.queryNamesAndValues.get(i + 1);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public String queryParameterName(int i) {
|
|
List<String> list = this.queryNamesAndValues;
|
|
if (list != null) {
|
|
return list.get(i * 2);
|
|
}
|
|
throw new IndexOutOfBoundsException();
|
|
}
|
|
|
|
public Set<String> queryParameterNames() {
|
|
if (this.queryNamesAndValues == null) {
|
|
return Collections.emptySet();
|
|
}
|
|
LinkedHashSet linkedHashSet = new LinkedHashSet();
|
|
int size = this.queryNamesAndValues.size();
|
|
for (int i = 0; i < size; i += 2) {
|
|
linkedHashSet.add(this.queryNamesAndValues.get(i));
|
|
}
|
|
return Collections.unmodifiableSet(linkedHashSet);
|
|
}
|
|
|
|
public String queryParameterValue(int i) {
|
|
List<String> list = this.queryNamesAndValues;
|
|
if (list != null) {
|
|
return list.get((i * 2) + 1);
|
|
}
|
|
throw new IndexOutOfBoundsException();
|
|
}
|
|
|
|
public List<String> queryParameterValues(String str) {
|
|
if (this.queryNamesAndValues == null) {
|
|
return Collections.emptyList();
|
|
}
|
|
ArrayList arrayList = new ArrayList();
|
|
int size = this.queryNamesAndValues.size();
|
|
for (int i = 0; i < size; i += 2) {
|
|
if (str.equals(this.queryNamesAndValues.get(i))) {
|
|
arrayList.add(this.queryNamesAndValues.get(i + 1));
|
|
}
|
|
}
|
|
return Collections.unmodifiableList(arrayList);
|
|
}
|
|
|
|
public int querySize() {
|
|
List<String> list = this.queryNamesAndValues;
|
|
if (list != null) {
|
|
return list.size() / 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public String redact() {
|
|
return newBuilder("/...").username("").password("").build().toString();
|
|
}
|
|
|
|
public HttpUrl resolve(String str) {
|
|
Builder newBuilder = newBuilder(str);
|
|
if (newBuilder != null) {
|
|
return newBuilder.build();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public String scheme() {
|
|
return this.scheme;
|
|
}
|
|
|
|
public String toString() {
|
|
return this.url;
|
|
}
|
|
|
|
public String topPrivateDomain() {
|
|
if (Util.verifyAsIpAddress(this.host)) {
|
|
return null;
|
|
}
|
|
return PublicSuffixDatabase.get().getEffectiveTldPlusOne(this.host);
|
|
}
|
|
|
|
public URI uri() {
|
|
String builder = newBuilder().reencodeForUri().toString();
|
|
try {
|
|
return new URI(builder);
|
|
} catch (URISyntaxException e) {
|
|
try {
|
|
return URI.create(builder.replaceAll("[\\u0000-\\u001F\\u007F-\\u009F\\p{javaWhitespace}]", ""));
|
|
} catch (Exception unused) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
public URL url() {
|
|
try {
|
|
return new URL(this.url);
|
|
} catch (MalformedURLException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
public String username() {
|
|
return this.username;
|
|
}
|
|
|
|
public static final class Builder {
|
|
static final String INVALID_HOST = "Invalid URL host";
|
|
String encodedFragment;
|
|
List<String> encodedQueryNamesAndValues;
|
|
String host;
|
|
String scheme;
|
|
String encodedUsername = "";
|
|
String encodedPassword = "";
|
|
int port = -1;
|
|
final List<String> encodedPathSegments = new ArrayList();
|
|
|
|
public Builder() {
|
|
this.encodedPathSegments.add("");
|
|
}
|
|
|
|
private static String canonicalizeHost(String str, int i, int i2) {
|
|
return Util.canonicalizeHost(HttpUrl.percentDecode(str, i, i2, false));
|
|
}
|
|
|
|
private boolean isDot(String str) {
|
|
return str.equals(".") || str.equalsIgnoreCase("%2e");
|
|
}
|
|
|
|
private boolean isDotDot(String str) {
|
|
return str.equals("..") || str.equalsIgnoreCase("%2e.") || str.equalsIgnoreCase(".%2e") || str.equalsIgnoreCase("%2e%2e");
|
|
}
|
|
|
|
private static int parsePort(String str, int i, int i2) {
|
|
int parseInt;
|
|
try {
|
|
parseInt = Integer.parseInt(HttpUrl.canonicalize(str, i, i2, "", false, false, false, true, null));
|
|
} catch (NumberFormatException unused) {
|
|
}
|
|
if (parseInt <= 0 || parseInt > 65535) {
|
|
return -1;
|
|
}
|
|
return parseInt;
|
|
}
|
|
|
|
private void pop() {
|
|
if (!this.encodedPathSegments.remove(r0.size() - 1).isEmpty() || this.encodedPathSegments.isEmpty()) {
|
|
this.encodedPathSegments.add("");
|
|
} else {
|
|
this.encodedPathSegments.set(r0.size() - 1, "");
|
|
}
|
|
}
|
|
|
|
private static int portColonOffset(String str, int i, int i2) {
|
|
while (i < i2) {
|
|
char charAt = str.charAt(i);
|
|
if (charAt == ':') {
|
|
return i;
|
|
}
|
|
if (charAt == '[') {
|
|
do {
|
|
i++;
|
|
if (i < i2) {
|
|
}
|
|
} while (str.charAt(i) != ']');
|
|
}
|
|
i++;
|
|
}
|
|
return i2;
|
|
}
|
|
|
|
private void push(String str, int i, int i2, boolean z, boolean z2) {
|
|
String canonicalize = HttpUrl.canonicalize(str, i, i2, HttpUrl.PATH_SEGMENT_ENCODE_SET, z2, false, false, true, null);
|
|
if (isDot(canonicalize)) {
|
|
return;
|
|
}
|
|
if (isDotDot(canonicalize)) {
|
|
pop();
|
|
return;
|
|
}
|
|
if (this.encodedPathSegments.get(r11.size() - 1).isEmpty()) {
|
|
this.encodedPathSegments.set(r11.size() - 1, canonicalize);
|
|
} else {
|
|
this.encodedPathSegments.add(canonicalize);
|
|
}
|
|
if (z) {
|
|
this.encodedPathSegments.add("");
|
|
}
|
|
}
|
|
|
|
private void removeAllCanonicalQueryParameters(String str) {
|
|
for (int size = this.encodedQueryNamesAndValues.size() - 2; size >= 0; size -= 2) {
|
|
if (str.equals(this.encodedQueryNamesAndValues.get(size))) {
|
|
this.encodedQueryNamesAndValues.remove(size + 1);
|
|
this.encodedQueryNamesAndValues.remove(size);
|
|
if (this.encodedQueryNamesAndValues.isEmpty()) {
|
|
this.encodedQueryNamesAndValues = null;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void resolvePath(String str, int i, int i2) {
|
|
if (i == i2) {
|
|
return;
|
|
}
|
|
char charAt = str.charAt(i);
|
|
if (charAt == '/' || charAt == '\\') {
|
|
this.encodedPathSegments.clear();
|
|
this.encodedPathSegments.add("");
|
|
i++;
|
|
} else {
|
|
List<String> list = this.encodedPathSegments;
|
|
list.set(list.size() - 1, "");
|
|
}
|
|
while (true) {
|
|
int i3 = i;
|
|
if (i3 >= i2) {
|
|
return;
|
|
}
|
|
i = Util.delimiterOffset(str, i3, i2, "/\\");
|
|
boolean z = i < i2;
|
|
push(str, i3, i, z, true);
|
|
if (z) {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static int schemeDelimiterOffset(String str, int i, int i2) {
|
|
if (i2 - i < 2) {
|
|
return -1;
|
|
}
|
|
char charAt = str.charAt(i);
|
|
if ((charAt >= 'a' && charAt <= 'z') || (charAt >= 'A' && charAt <= 'Z')) {
|
|
while (true) {
|
|
i++;
|
|
if (i >= i2) {
|
|
break;
|
|
}
|
|
char charAt2 = str.charAt(i);
|
|
if (charAt2 < 'a' || charAt2 > 'z') {
|
|
if (charAt2 < 'A' || charAt2 > 'Z') {
|
|
if (charAt2 < '0' || charAt2 > '9') {
|
|
if (charAt2 != '+' && charAt2 != '-' && charAt2 != '.') {
|
|
if (charAt2 == ':') {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private static int slashCount(String str, int i, int i2) {
|
|
int i3 = 0;
|
|
while (i < i2) {
|
|
char charAt = str.charAt(i);
|
|
if (charAt != '\\' && charAt != '/') {
|
|
break;
|
|
}
|
|
i3++;
|
|
i++;
|
|
}
|
|
return i3;
|
|
}
|
|
|
|
public Builder addEncodedPathSegment(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("encodedPathSegment == null");
|
|
}
|
|
push(str, 0, str.length(), false, true);
|
|
return this;
|
|
}
|
|
|
|
public Builder addEncodedPathSegments(String str) {
|
|
if (str != null) {
|
|
return addPathSegments(str, true);
|
|
}
|
|
throw new NullPointerException("encodedPathSegments == null");
|
|
}
|
|
|
|
public Builder addEncodedQueryParameter(String str, String str2) {
|
|
if (str == null) {
|
|
throw new NullPointerException("encodedName == null");
|
|
}
|
|
if (this.encodedQueryNamesAndValues == null) {
|
|
this.encodedQueryNamesAndValues = new ArrayList();
|
|
}
|
|
this.encodedQueryNamesAndValues.add(HttpUrl.canonicalize(str, HttpUrl.QUERY_COMPONENT_REENCODE_SET, true, false, true, true));
|
|
this.encodedQueryNamesAndValues.add(str2 != null ? HttpUrl.canonicalize(str2, HttpUrl.QUERY_COMPONENT_REENCODE_SET, true, false, true, true) : null);
|
|
return this;
|
|
}
|
|
|
|
public Builder addPathSegment(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("pathSegment == null");
|
|
}
|
|
push(str, 0, str.length(), false, false);
|
|
return this;
|
|
}
|
|
|
|
public Builder addPathSegments(String str) {
|
|
if (str != null) {
|
|
return addPathSegments(str, false);
|
|
}
|
|
throw new NullPointerException("pathSegments == null");
|
|
}
|
|
|
|
public Builder addQueryParameter(String str, String str2) {
|
|
if (str == null) {
|
|
throw new NullPointerException("name == null");
|
|
}
|
|
if (this.encodedQueryNamesAndValues == null) {
|
|
this.encodedQueryNamesAndValues = new ArrayList();
|
|
}
|
|
this.encodedQueryNamesAndValues.add(HttpUrl.canonicalize(str, HttpUrl.QUERY_COMPONENT_ENCODE_SET, false, false, true, true));
|
|
this.encodedQueryNamesAndValues.add(str2 != null ? HttpUrl.canonicalize(str2, HttpUrl.QUERY_COMPONENT_ENCODE_SET, false, false, true, true) : null);
|
|
return this;
|
|
}
|
|
|
|
public HttpUrl build() {
|
|
if (this.scheme == null) {
|
|
throw new IllegalStateException("scheme == null");
|
|
}
|
|
if (this.host != null) {
|
|
return new HttpUrl(this);
|
|
}
|
|
throw new IllegalStateException("host == null");
|
|
}
|
|
|
|
int effectivePort() {
|
|
int i = this.port;
|
|
return i != -1 ? i : HttpUrl.defaultPort(this.scheme);
|
|
}
|
|
|
|
public Builder encodedFragment(String str) {
|
|
this.encodedFragment = str != null ? HttpUrl.canonicalize(str, "", true, false, false, false) : null;
|
|
return this;
|
|
}
|
|
|
|
public Builder encodedPassword(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("encodedPassword == null");
|
|
}
|
|
this.encodedPassword = HttpUrl.canonicalize(str, " \"':;<=>@[]^`{}|/\\?#", true, false, false, true);
|
|
return this;
|
|
}
|
|
|
|
public Builder encodedPath(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("encodedPath == null");
|
|
}
|
|
if (str.startsWith("/")) {
|
|
resolvePath(str, 0, str.length());
|
|
return this;
|
|
}
|
|
throw new IllegalArgumentException("unexpected encodedPath: " + str);
|
|
}
|
|
|
|
public Builder encodedQuery(String str) {
|
|
this.encodedQueryNamesAndValues = str != null ? HttpUrl.queryStringToNamesAndValues(HttpUrl.canonicalize(str, HttpUrl.QUERY_ENCODE_SET, true, false, true, true)) : null;
|
|
return this;
|
|
}
|
|
|
|
public Builder encodedUsername(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("encodedUsername == null");
|
|
}
|
|
this.encodedUsername = HttpUrl.canonicalize(str, " \"':;<=>@[]^`{}|/\\?#", true, false, false, true);
|
|
return this;
|
|
}
|
|
|
|
public Builder fragment(String str) {
|
|
this.encodedFragment = str != null ? HttpUrl.canonicalize(str, "", false, false, false, false) : null;
|
|
return this;
|
|
}
|
|
|
|
public Builder host(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("host == null");
|
|
}
|
|
String canonicalizeHost = canonicalizeHost(str, 0, str.length());
|
|
if (canonicalizeHost != null) {
|
|
this.host = canonicalizeHost;
|
|
return this;
|
|
}
|
|
throw new IllegalArgumentException("unexpected host: " + str);
|
|
}
|
|
|
|
Builder parse(HttpUrl httpUrl, String str) {
|
|
int delimiterOffset;
|
|
int i;
|
|
int skipLeadingAsciiWhitespace = Util.skipLeadingAsciiWhitespace(str, 0, str.length());
|
|
int skipTrailingAsciiWhitespace = Util.skipTrailingAsciiWhitespace(str, skipLeadingAsciiWhitespace, str.length());
|
|
int schemeDelimiterOffset = schemeDelimiterOffset(str, skipLeadingAsciiWhitespace, skipTrailingAsciiWhitespace);
|
|
if (schemeDelimiterOffset != -1) {
|
|
if (str.regionMatches(true, skipLeadingAsciiWhitespace, "https:", 0, 6)) {
|
|
this.scheme = "https";
|
|
skipLeadingAsciiWhitespace += 6;
|
|
} else {
|
|
if (!str.regionMatches(true, skipLeadingAsciiWhitespace, "http:", 0, 5)) {
|
|
throw new IllegalArgumentException("Expected URL scheme 'http' or 'https' but was '" + str.substring(0, schemeDelimiterOffset) + "'");
|
|
}
|
|
this.scheme = "http";
|
|
skipLeadingAsciiWhitespace += 5;
|
|
}
|
|
} else {
|
|
if (httpUrl == null) {
|
|
throw new IllegalArgumentException("Expected URL scheme 'http' or 'https' but no colon was found");
|
|
}
|
|
this.scheme = httpUrl.scheme;
|
|
}
|
|
int slashCount = slashCount(str, skipLeadingAsciiWhitespace, skipTrailingAsciiWhitespace);
|
|
char c = '?';
|
|
char c2 = '#';
|
|
if (slashCount >= 2 || httpUrl == null || !httpUrl.scheme.equals(this.scheme)) {
|
|
int i2 = skipLeadingAsciiWhitespace + slashCount;
|
|
boolean z = false;
|
|
boolean z2 = false;
|
|
while (true) {
|
|
delimiterOffset = Util.delimiterOffset(str, i2, skipTrailingAsciiWhitespace, "@/\\?#");
|
|
char charAt = delimiterOffset != skipTrailingAsciiWhitespace ? str.charAt(delimiterOffset) : (char) 65535;
|
|
if (charAt == 65535 || charAt == c2 || charAt == '/' || charAt == '\\' || charAt == c) {
|
|
break;
|
|
}
|
|
if (charAt == '@') {
|
|
if (z) {
|
|
i = delimiterOffset;
|
|
this.encodedPassword += "%40" + HttpUrl.canonicalize(str, i2, i, " \"':;<=>@[]^`{}|/\\?#", true, false, false, true, null);
|
|
} else {
|
|
int delimiterOffset2 = Util.delimiterOffset(str, i2, delimiterOffset, ':');
|
|
i = delimiterOffset;
|
|
String canonicalize = HttpUrl.canonicalize(str, i2, delimiterOffset2, " \"':;<=>@[]^`{}|/\\?#", true, false, false, true, null);
|
|
if (z2) {
|
|
canonicalize = this.encodedUsername + "%40" + canonicalize;
|
|
}
|
|
this.encodedUsername = canonicalize;
|
|
if (delimiterOffset2 != i) {
|
|
this.encodedPassword = HttpUrl.canonicalize(str, delimiterOffset2 + 1, i, " \"':;<=>@[]^`{}|/\\?#", true, false, false, true, null);
|
|
z = true;
|
|
}
|
|
z2 = true;
|
|
}
|
|
i2 = i + 1;
|
|
}
|
|
c = '?';
|
|
c2 = '#';
|
|
}
|
|
int portColonOffset = portColonOffset(str, i2, delimiterOffset);
|
|
int i3 = portColonOffset + 1;
|
|
if (i3 < delimiterOffset) {
|
|
this.host = canonicalizeHost(str, i2, portColonOffset);
|
|
this.port = parsePort(str, i3, delimiterOffset);
|
|
if (this.port == -1) {
|
|
throw new IllegalArgumentException("Invalid URL port: \"" + str.substring(i3, delimiterOffset) + '\"');
|
|
}
|
|
} else {
|
|
this.host = canonicalizeHost(str, i2, portColonOffset);
|
|
this.port = HttpUrl.defaultPort(this.scheme);
|
|
}
|
|
if (this.host == null) {
|
|
throw new IllegalArgumentException("Invalid URL host: \"" + str.substring(i2, portColonOffset) + '\"');
|
|
}
|
|
skipLeadingAsciiWhitespace = delimiterOffset;
|
|
} else {
|
|
this.encodedUsername = httpUrl.encodedUsername();
|
|
this.encodedPassword = httpUrl.encodedPassword();
|
|
this.host = httpUrl.host;
|
|
this.port = httpUrl.port;
|
|
this.encodedPathSegments.clear();
|
|
this.encodedPathSegments.addAll(httpUrl.encodedPathSegments());
|
|
if (skipLeadingAsciiWhitespace == skipTrailingAsciiWhitespace || str.charAt(skipLeadingAsciiWhitespace) == '#') {
|
|
encodedQuery(httpUrl.encodedQuery());
|
|
}
|
|
}
|
|
int delimiterOffset3 = Util.delimiterOffset(str, skipLeadingAsciiWhitespace, skipTrailingAsciiWhitespace, "?#");
|
|
resolvePath(str, skipLeadingAsciiWhitespace, delimiterOffset3);
|
|
if (delimiterOffset3 < skipTrailingAsciiWhitespace && str.charAt(delimiterOffset3) == '?') {
|
|
int delimiterOffset4 = Util.delimiterOffset(str, delimiterOffset3, skipTrailingAsciiWhitespace, '#');
|
|
this.encodedQueryNamesAndValues = HttpUrl.queryStringToNamesAndValues(HttpUrl.canonicalize(str, delimiterOffset3 + 1, delimiterOffset4, HttpUrl.QUERY_ENCODE_SET, true, false, true, true, null));
|
|
delimiterOffset3 = delimiterOffset4;
|
|
}
|
|
if (delimiterOffset3 < skipTrailingAsciiWhitespace && str.charAt(delimiterOffset3) == '#') {
|
|
this.encodedFragment = HttpUrl.canonicalize(str, 1 + delimiterOffset3, skipTrailingAsciiWhitespace, "", true, false, false, false, null);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public Builder password(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("password == null");
|
|
}
|
|
this.encodedPassword = HttpUrl.canonicalize(str, " \"':;<=>@[]^`{}|/\\?#", false, false, false, true);
|
|
return this;
|
|
}
|
|
|
|
public Builder port(int i) {
|
|
if (i > 0 && i <= 65535) {
|
|
this.port = i;
|
|
return this;
|
|
}
|
|
throw new IllegalArgumentException("unexpected port: " + i);
|
|
}
|
|
|
|
public Builder query(String str) {
|
|
this.encodedQueryNamesAndValues = str != null ? HttpUrl.queryStringToNamesAndValues(HttpUrl.canonicalize(str, HttpUrl.QUERY_ENCODE_SET, false, false, true, true)) : null;
|
|
return this;
|
|
}
|
|
|
|
Builder reencodeForUri() {
|
|
int size = this.encodedPathSegments.size();
|
|
for (int i = 0; i < size; i++) {
|
|
this.encodedPathSegments.set(i, HttpUrl.canonicalize(this.encodedPathSegments.get(i), HttpUrl.PATH_SEGMENT_ENCODE_SET_URI, true, true, false, true));
|
|
}
|
|
List<String> list = this.encodedQueryNamesAndValues;
|
|
if (list != null) {
|
|
int size2 = list.size();
|
|
for (int i2 = 0; i2 < size2; i2++) {
|
|
String str = this.encodedQueryNamesAndValues.get(i2);
|
|
if (str != null) {
|
|
this.encodedQueryNamesAndValues.set(i2, HttpUrl.canonicalize(str, HttpUrl.QUERY_COMPONENT_ENCODE_SET_URI, true, true, true, true));
|
|
}
|
|
}
|
|
}
|
|
String str2 = this.encodedFragment;
|
|
if (str2 != null) {
|
|
this.encodedFragment = HttpUrl.canonicalize(str2, HttpUrl.FRAGMENT_ENCODE_SET_URI, true, true, false, false);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public Builder removeAllEncodedQueryParameters(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("encodedName == null");
|
|
}
|
|
if (this.encodedQueryNamesAndValues == null) {
|
|
return this;
|
|
}
|
|
removeAllCanonicalQueryParameters(HttpUrl.canonicalize(str, HttpUrl.QUERY_COMPONENT_REENCODE_SET, true, false, true, true));
|
|
return this;
|
|
}
|
|
|
|
public Builder removeAllQueryParameters(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("name == null");
|
|
}
|
|
if (this.encodedQueryNamesAndValues == null) {
|
|
return this;
|
|
}
|
|
removeAllCanonicalQueryParameters(HttpUrl.canonicalize(str, HttpUrl.QUERY_COMPONENT_ENCODE_SET, false, false, true, true));
|
|
return this;
|
|
}
|
|
|
|
public Builder removePathSegment(int i) {
|
|
this.encodedPathSegments.remove(i);
|
|
if (this.encodedPathSegments.isEmpty()) {
|
|
this.encodedPathSegments.add("");
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public Builder scheme(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("scheme == null");
|
|
}
|
|
if (str.equalsIgnoreCase("http")) {
|
|
this.scheme = "http";
|
|
} else {
|
|
if (!str.equalsIgnoreCase("https")) {
|
|
throw new IllegalArgumentException("unexpected scheme: " + str);
|
|
}
|
|
this.scheme = "https";
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public Builder setEncodedPathSegment(int i, String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("encodedPathSegment == null");
|
|
}
|
|
String canonicalize = HttpUrl.canonicalize(str, 0, str.length(), HttpUrl.PATH_SEGMENT_ENCODE_SET, true, false, false, true, null);
|
|
this.encodedPathSegments.set(i, canonicalize);
|
|
if (!isDot(canonicalize) && !isDotDot(canonicalize)) {
|
|
return this;
|
|
}
|
|
throw new IllegalArgumentException("unexpected path segment: " + str);
|
|
}
|
|
|
|
public Builder setEncodedQueryParameter(String str, String str2) {
|
|
removeAllEncodedQueryParameters(str);
|
|
addEncodedQueryParameter(str, str2);
|
|
return this;
|
|
}
|
|
|
|
public Builder setPathSegment(int i, String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("pathSegment == null");
|
|
}
|
|
String canonicalize = HttpUrl.canonicalize(str, 0, str.length(), HttpUrl.PATH_SEGMENT_ENCODE_SET, false, false, false, true, null);
|
|
if (!isDot(canonicalize) && !isDotDot(canonicalize)) {
|
|
this.encodedPathSegments.set(i, canonicalize);
|
|
return this;
|
|
}
|
|
throw new IllegalArgumentException("unexpected path segment: " + str);
|
|
}
|
|
|
|
public Builder setQueryParameter(String str, String str2) {
|
|
removeAllQueryParameters(str);
|
|
addQueryParameter(str, str2);
|
|
return this;
|
|
}
|
|
|
|
public String toString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append(this.scheme);
|
|
sb.append("://");
|
|
if (!this.encodedUsername.isEmpty() || !this.encodedPassword.isEmpty()) {
|
|
sb.append(this.encodedUsername);
|
|
if (!this.encodedPassword.isEmpty()) {
|
|
sb.append(':');
|
|
sb.append(this.encodedPassword);
|
|
}
|
|
sb.append('@');
|
|
}
|
|
if (this.host.indexOf(58) != -1) {
|
|
sb.append('[');
|
|
sb.append(this.host);
|
|
sb.append(']');
|
|
} else {
|
|
sb.append(this.host);
|
|
}
|
|
int effectivePort = effectivePort();
|
|
if (effectivePort != HttpUrl.defaultPort(this.scheme)) {
|
|
sb.append(':');
|
|
sb.append(effectivePort);
|
|
}
|
|
HttpUrl.pathSegmentsToString(sb, this.encodedPathSegments);
|
|
if (this.encodedQueryNamesAndValues != null) {
|
|
sb.append('?');
|
|
HttpUrl.namesAndValuesToQueryString(sb, this.encodedQueryNamesAndValues);
|
|
}
|
|
if (this.encodedFragment != null) {
|
|
sb.append('#');
|
|
sb.append(this.encodedFragment);
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
public Builder username(String str) {
|
|
if (str == null) {
|
|
throw new NullPointerException("username == null");
|
|
}
|
|
this.encodedUsername = HttpUrl.canonicalize(str, " \"':;<=>@[]^`{}|/\\?#", false, false, false, true);
|
|
return this;
|
|
}
|
|
|
|
private Builder addPathSegments(String str, boolean z) {
|
|
int i = 0;
|
|
do {
|
|
int delimiterOffset = Util.delimiterOffset(str, i, str.length(), "/\\");
|
|
push(str, i, delimiterOffset, delimiterOffset < str.length(), z);
|
|
i = delimiterOffset + 1;
|
|
} while (i <= str.length());
|
|
return this;
|
|
}
|
|
}
|
|
|
|
public static HttpUrl get(URL url) {
|
|
return parse(url.toString());
|
|
}
|
|
|
|
private List<String> percentDecode(List<String> list, boolean z) {
|
|
int size = list.size();
|
|
ArrayList arrayList = new ArrayList(size);
|
|
for (int i = 0; i < size; i++) {
|
|
String str = list.get(i);
|
|
arrayList.add(str != null ? percentDecode(str, z) : null);
|
|
}
|
|
return Collections.unmodifiableList(arrayList);
|
|
}
|
|
|
|
public static HttpUrl get(URI uri) {
|
|
return parse(uri.toString());
|
|
}
|
|
|
|
static String percentDecode(String str, int i, int i2, boolean z) {
|
|
for (int i3 = i; i3 < i2; i3++) {
|
|
char charAt = str.charAt(i3);
|
|
if (charAt == '%' || (charAt == '+' && z)) {
|
|
Buffer buffer = new Buffer();
|
|
buffer.writeUtf8(str, i, i3);
|
|
percentDecode(buffer, str, i3, i2, z);
|
|
return buffer.readUtf8();
|
|
}
|
|
}
|
|
return str.substring(i, i2);
|
|
}
|
|
|
|
static void canonicalize(Buffer buffer, String str, int i, int i2, String str2, boolean z, boolean z2, boolean z3, boolean z4, Charset charset) {
|
|
Buffer buffer2 = null;
|
|
while (i < i2) {
|
|
int codePointAt = str.codePointAt(i);
|
|
if (!z || (codePointAt != 9 && codePointAt != 10 && codePointAt != 12 && codePointAt != 13)) {
|
|
if (codePointAt == 43 && z3) {
|
|
buffer.writeUtf8(z ? "+" : "%2B");
|
|
} else if (codePointAt >= 32 && codePointAt != 127 && ((codePointAt < 128 || !z4) && str2.indexOf(codePointAt) == -1 && (codePointAt != 37 || (z && (!z2 || percentEncoded(str, i, i2)))))) {
|
|
buffer.writeUtf8CodePoint(codePointAt);
|
|
} else {
|
|
if (buffer2 == null) {
|
|
buffer2 = new Buffer();
|
|
}
|
|
if (charset != null && !charset.equals(Util.UTF_8)) {
|
|
buffer2.writeString(str, i, Character.charCount(codePointAt) + i, charset);
|
|
} else {
|
|
buffer2.writeUtf8CodePoint(codePointAt);
|
|
}
|
|
while (!buffer2.exhausted()) {
|
|
int readByte = buffer2.readByte() & 255;
|
|
buffer.writeByte(37);
|
|
buffer.writeByte((int) HEX_DIGITS[(readByte >> 4) & 15]);
|
|
buffer.writeByte((int) HEX_DIGITS[readByte & 15]);
|
|
}
|
|
}
|
|
}
|
|
i += Character.charCount(codePointAt);
|
|
}
|
|
}
|
|
|
|
public Builder newBuilder(String str) {
|
|
try {
|
|
return new Builder().parse(this, str);
|
|
} catch (IllegalArgumentException unused) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static void percentDecode(Buffer buffer, String str, int i, int i2, boolean z) {
|
|
int i3;
|
|
while (i < i2) {
|
|
int codePointAt = str.codePointAt(i);
|
|
if (codePointAt == 37 && (i3 = i + 2) < i2) {
|
|
int decodeHexDigit = Util.decodeHexDigit(str.charAt(i + 1));
|
|
int decodeHexDigit2 = Util.decodeHexDigit(str.charAt(i3));
|
|
if (decodeHexDigit != -1 && decodeHexDigit2 != -1) {
|
|
buffer.writeByte((decodeHexDigit << 4) + decodeHexDigit2);
|
|
i = i3;
|
|
}
|
|
buffer.writeUtf8CodePoint(codePointAt);
|
|
} else {
|
|
if (codePointAt == 43 && z) {
|
|
buffer.writeByte(32);
|
|
}
|
|
buffer.writeUtf8CodePoint(codePointAt);
|
|
}
|
|
i += Character.charCount(codePointAt);
|
|
}
|
|
}
|
|
|
|
static String canonicalize(String str, String str2, boolean z, boolean z2, boolean z3, boolean z4, Charset charset) {
|
|
return canonicalize(str, 0, str.length(), str2, z, z2, z3, z4, charset);
|
|
}
|
|
|
|
static String canonicalize(String str, String str2, boolean z, boolean z2, boolean z3, boolean z4) {
|
|
return canonicalize(str, 0, str.length(), str2, z, z2, z3, z4, null);
|
|
}
|
|
}
|