EhViewer v1.9.7.7版本的 MD5 值为:828893cd54837a794dab17a02809e194

以下内容为反编译后的 HttpConnection.java 源代码,内容仅作参考


package org.jsoup.helper;

import androidx.webkit.ProxyConfig;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpURLConnection;
import java.net.IDN;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import javax.annotation.Nullable;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
import kotlin.UByte;
import kotlin.jvm.internal.ByteCompanionObject;
import kotlin.text.Typography;
import org.jsoup.Connection;
import org.jsoup.HttpStatusException;
import org.jsoup.UncheckedIOException;
import org.jsoup.UnsupportedMimeTypeException;
import org.jsoup.internal.ConstrainableInputStream;
import org.jsoup.internal.Normalizer;
import org.jsoup.internal.StringUtil;
import org.jsoup.nodes.Document;
import org.jsoup.parser.Parser;
import org.jsoup.parser.TokenQueue;
public class HttpConnection implements Connection {
    public static final String CONTENT_ENCODING = "Content-Encoding";
    public static final String CONTENT_TYPE = "Content-Type";
    public static final String DEFAULT_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36";
    private static final String DefaultUploadType = "application/octet-stream";
    public static final String FORM_URL_ENCODED = "application/x-www-form-urlencoded";
    private static final int HTTP_TEMP_REDIR = 307;
    public static final String MULTIPART_FORM_DATA = "multipart/form-data";
    private static final String USER_AGENT = "User-Agent";
    private Request req;
    @Nullable
    private Connection.Response res;
    private static final Charset UTF_8 = Charset.forName("UTF-8");
    private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");

    public static Connection connect(String str) {
        HttpConnection httpConnection = new HttpConnection();
        httpConnection.url(str);
        return httpConnection;
    }

    public static Connection connect(URL url) {
        HttpConnection httpConnection = new HttpConnection();
        httpConnection.url(url);
        return httpConnection;
    }

    public HttpConnection() {
        this.req = new Request();
    }

    HttpConnection(Request request) {
        this.req = new Request(request);
    }

    private static String encodeUrl(String str) {
        try {
            return encodeUrl(new URL(str)).toExternalForm();
        } catch (Exception unused) {
            return str;
        }
    }

    static URL encodeUrl(URL url) {
        URL punyUrl = punyUrl(url);
        try {
            return new URL(new URI(punyUrl.toExternalForm().replace(" ", "%20")).toASCIIString());
        } catch (MalformedURLException | URISyntaxException unused) {
            return punyUrl;
        }
    }

    public static URL punyUrl(URL url) {
        if (StringUtil.isAscii(url.getHost())) {
            return url;
        }
        try {
            return new URL(url.getProtocol(), IDN.toASCII(url.getHost()), url.getPort(), url.getFile());
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException(e);
        }
    }

    public static String encodeMimeName(String str) {
        return str.replace("\"", "%22");
    }

    @Override
    public Connection newRequest() {
        return new HttpConnection(this.req);
    }

    private HttpConnection(Request request, Response response) {
        this.req = request;
        this.res = response;
    }

    @Override
    public Connection url(URL url) {
        this.req.url(url);
        return this;
    }

    @Override
    public Connection url(String str) {
        Validate.notEmpty(str, "Must supply a valid URL");
        try {
            this.req.url(new URL(encodeUrl(str)));
            return this;
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("Malformed URL: " + str, e);
        }
    }

    @Override
    public Connection proxy(@Nullable Proxy proxy) {
        this.req.proxy(proxy);
        return this;
    }

    @Override
    public Connection proxy(String str, int i) {
        this.req.proxy(str, i);
        return this;
    }

    @Override
    public Connection userAgent(String str) {
        Validate.notNull(str, "User agent must not be null");
        this.req.header(USER_AGENT, str);
        return this;
    }

    @Override
    public Connection timeout(int i) {
        this.req.timeout(i);
        return this;
    }

    @Override
    public Connection maxBodySize(int i) {
        this.req.maxBodySize(i);
        return this;
    }

    @Override
    public Connection followRedirects(boolean z) {
        this.req.followRedirects(z);
        return this;
    }

    @Override
    public Connection referrer(String str) {
        Validate.notNull(str, "Referrer must not be null");
        this.req.header("Referer", str);
        return this;
    }

    @Override
    public Connection method(Connection.Method method) {
        this.req.method(method);
        return this;
    }

    @Override
    public Connection ignoreHttpErrors(boolean z) {
        this.req.ignoreHttpErrors(z);
        return this;
    }

    @Override
    public Connection ignoreContentType(boolean z) {
        this.req.ignoreContentType(z);
        return this;
    }

    @Override
    public Connection data(String str, String str2) {
        this.req.data((Connection.KeyVal) KeyVal.create(str, str2));
        return this;
    }

    @Override
    public Connection sslSocketFactory(SSLSocketFactory sSLSocketFactory) {
        this.req.sslSocketFactory(sSLSocketFactory);
        return this;
    }

    @Override
    public Connection data(String str, String str2, InputStream inputStream) {
        this.req.data((Connection.KeyVal) KeyVal.create(str, str2, inputStream));
        return this;
    }

    @Override
    public Connection data(String str, String str2, InputStream inputStream, String str3) {
        this.req.data(KeyVal.create(str, str2, inputStream).contentType(str3));
        return this;
    }

    @Override
    public Connection data(Map<String, String> map) {
        Validate.notNull(map, "Data map must not be null");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            this.req.data((Connection.KeyVal) KeyVal.create(entry.getKey(), entry.getValue()));
        }
        return this;
    }

    @Override
    public Connection data(String... strArr) {
        Validate.notNull(strArr, "Data key value pairs must not be null");
        Validate.isTrue(strArr.length % 2 == 0, "Must supply an even number of key value pairs");
        for (int i = 0; i < strArr.length; i += 2) {
            String str = strArr[i];
            String str2 = strArr[i + 1];
            Validate.notEmpty(str, "Data key must not be empty");
            Validate.notNull(str2, "Data value must not be null");
            this.req.data((Connection.KeyVal) KeyVal.create(str, str2));
        }
        return this;
    }

    @Override
    public Connection data(Collection<Connection.KeyVal> collection) {
        Validate.notNull(collection, "Data collection must not be null");
        for (Connection.KeyVal keyVal : collection) {
            this.req.data(keyVal);
        }
        return this;
    }

    @Override
    public Connection.KeyVal data(String str) {
        Validate.notEmpty(str, "Data key must not be empty");
        for (Connection.KeyVal keyVal : request().data()) {
            if (keyVal.key().equals(str)) {
                return keyVal;
            }
        }
        return null;
    }

    @Override
    public Connection requestBody(String str) {
        this.req.requestBody(str);
        return this;
    }

    @Override
    public Connection header(String str, String str2) {
        this.req.header(str, str2);
        return this;
    }

    @Override
    public Connection headers(Map<String, String> map) {
        Validate.notNull(map, "Header map must not be null");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            this.req.header(entry.getKey(), entry.getValue());
        }
        return this;
    }

    @Override
    public Connection cookie(String str, String str2) {
        this.req.cookie(str, str2);
        return this;
    }

    @Override
    public Connection cookies(Map<String, String> map) {
        Validate.notNull(map, "Cookie map must not be null");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            this.req.cookie(entry.getKey(), entry.getValue());
        }
        return this;
    }

    @Override
    public Connection cookieStore(CookieStore cookieStore) {
        this.req.cookieManager = new CookieManager(cookieStore, null);
        return this;
    }

    @Override
    public CookieStore cookieStore() {
        return this.req.cookieManager.getCookieStore();
    }

    @Override
    public Connection parser(Parser parser) {
        this.req.parser(parser);
        return this;
    }

    @Override
    public Document get() throws IOException {
        this.req.method(Connection.Method.GET);
        execute();
        Validate.notNull(this.res);
        return this.res.parse();
    }

    @Override
    public Document post() throws IOException {
        this.req.method(Connection.Method.POST);
        execute();
        Validate.notNull(this.res);
        return this.res.parse();
    }

    @Override
    public Connection.Response execute() throws IOException {
        Response execute = Response.execute(this.req);
        this.res = execute;
        return execute;
    }

    @Override
    public Connection.Request request() {
        return this.req;
    }

    @Override
    public Connection request(Connection.Request request) {
        this.req = (Request) request;
        return this;
    }

    @Override
    public Connection.Response response() {
        Connection.Response response = this.res;
        if (response != null) {
            return response;
        }
        throw new IllegalArgumentException("You must execute the request before getting a response.");
    }

    @Override
    public Connection response(Connection.Response response) {
        this.res = response;
        return this;
    }

    @Override
    public Connection postDataCharset(String str) {
        this.req.postDataCharset(str);
        return this;
    }

    public static abstract class Base<T extends Connection.Base<T>> implements Connection.Base<T> {
        private static final URL UnsetUrl;
        Map<String, String> cookies;
        Map<String, List<String>> headers;
        Connection.Method method;
        URL url;

        static {
            try {
                UnsetUrl = new URL("http://undefined/");
            } catch (MalformedURLException e) {
                throw new IllegalStateException(e);
            }
        }

        private Base() {
            this.url = UnsetUrl;
            this.method = Connection.Method.GET;
            this.headers = new LinkedHashMap();
            this.cookies = new LinkedHashMap();
        }

        private Base(Base<T> base) {
            this.url = UnsetUrl;
            this.method = Connection.Method.GET;
            this.url = base.url;
            this.method = base.method;
            this.headers = new LinkedHashMap();
            for (Map.Entry<String, List<String>> entry : base.headers.entrySet()) {
                this.headers.put(entry.getKey(), new ArrayList(entry.getValue()));
            }
            LinkedHashMap linkedHashMap = new LinkedHashMap();
            this.cookies = linkedHashMap;
            linkedHashMap.putAll(base.cookies);
        }

        @Override
        public URL url() {
            URL url = this.url;
            if (url != UnsetUrl) {
                return url;
            }
            throw new IllegalArgumentException("URL not set. Make sure to call #url(...) before executing the request.");
        }

        @Override
        public T url(URL url) {
            Validate.notNull(url, "URL must not be null");
            this.url = HttpConnection.punyUrl(url);
            return this;
        }

        @Override
        public Connection.Method method() {
            return this.method;
        }

        @Override
        public T method(Connection.Method method) {
            Validate.notNull(method, "Method must not be null");
            this.method = method;
            return this;
        }

        @Override
        public String header(String str) {
            Validate.notNull(str, "Header name must not be null");
            List<String> headersCaseInsensitive = getHeadersCaseInsensitive(str);
            if (headersCaseInsensitive.size() > 0) {
                return StringUtil.join(headersCaseInsensitive, ", ");
            }
            return null;
        }

        @Override
        public T addHeader(String str, String str2) {
            Validate.notEmpty(str);
            if (str2 == null) {
                str2 = "";
            }
            List<String> headers = headers(str);
            if (headers.isEmpty()) {
                headers = new ArrayList<>();
                this.headers.put(str, headers);
            }
            headers.add(fixHeaderEncoding(str2));
            return this;
        }

        @Override
        public List<String> headers(String str) {
            Validate.notEmpty(str);
            return getHeadersCaseInsensitive(str);
        }

        private static String fixHeaderEncoding(String str) {
            byte[] bytes = str.getBytes(HttpConnection.ISO_8859_1);
            return !looksLikeUtf8(bytes) ? str : new String(bytes, HttpConnection.UTF_8);
        }

        private static boolean looksLikeUtf8(byte[] bArr) {
            int i;
            int i2 = 3;
            if (bArr.length < 3 || (bArr[0] & UByte.MAX_VALUE) != 239 || (bArr[1] & UByte.MAX_VALUE) != 187 || (bArr[2] & UByte.MAX_VALUE) != 191) {
                i2 = 0;
            }
            int length = bArr.length;
            while (i2 < length) {
                byte b = bArr[i2];
                if ((b & ByteCompanionObject.MIN_VALUE) != 0) {
                    if ((b & 224) == 192) {
                        i = i2 + 1;
                    } else if ((b & 240) == 224) {
                        i = i2 + 2;
                    } else if ((b & 248) != 240) {
                        return false;
                    } else {
                        i = i2 + 3;
                    }
                    if (i >= bArr.length) {
                        return false;
                    }
                    while (i2 < i) {
                        i2++;
                        if ((bArr[i2] & 192) != 128) {
                            return false;
                        }
                    }
                    continue;
                }
                i2++;
            }
            return true;
        }

        @Override
        public T header(String str, String str2) {
            Validate.notEmpty(str, "Header name must not be empty");
            removeHeader(str);
            addHeader(str, str2);
            return this;
        }

        @Override
        public boolean hasHeader(String str) {
            Validate.notEmpty(str, "Header name must not be empty");
            return !getHeadersCaseInsensitive(str).isEmpty();
        }

        @Override
        public boolean hasHeaderWithValue(String str, String str2) {
            Validate.notEmpty(str);
            Validate.notEmpty(str2);
            for (String str3 : headers(str)) {
                if (str2.equalsIgnoreCase(str3)) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public T removeHeader(String str) {
            Validate.notEmpty(str, "Header name must not be empty");
            Map.Entry<String, List<String>> scanHeaders = scanHeaders(str);
            if (scanHeaders != null) {
                this.headers.remove(scanHeaders.getKey());
            }
            return this;
        }

        @Override
        public Map<String, String> headers() {
            LinkedHashMap linkedHashMap = new LinkedHashMap(this.headers.size());
            for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
                String key = entry.getKey();
                List<String> value = entry.getValue();
                if (value.size() > 0) {
                    linkedHashMap.put(key, value.get(0));
                }
            }
            return linkedHashMap;
        }

        @Override
        public Map<String, List<String>> multiHeaders() {
            return this.headers;
        }

        private List<String> getHeadersCaseInsensitive(String str) {
            Validate.notNull(str);
            for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
                if (str.equalsIgnoreCase(entry.getKey())) {
                    return entry.getValue();
                }
            }
            return Collections.emptyList();
        }

        @Nullable
        private Map.Entry<String, List<String>> scanHeaders(String str) {
            String lowerCase = Normalizer.lowerCase(str);
            for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
                if (Normalizer.lowerCase(entry.getKey()).equals(lowerCase)) {
                    return entry;
                }
            }
            return null;
        }

        @Override
        public String cookie(String str) {
            Validate.notEmpty(str, "Cookie name must not be empty");
            return this.cookies.get(str);
        }

        @Override
        public T cookie(String str, String str2) {
            Validate.notEmpty(str, "Cookie name must not be empty");
            Validate.notNull(str2, "Cookie value must not be null");
            this.cookies.put(str, str2);
            return this;
        }

        @Override
        public boolean hasCookie(String str) {
            Validate.notEmpty(str, "Cookie name must not be empty");
            return this.cookies.containsKey(str);
        }

        @Override
        public T removeCookie(String str) {
            Validate.notEmpty(str, "Cookie name must not be empty");
            this.cookies.remove(str);
            return this;
        }

        @Override
        public Map<String, String> cookies() {
            return this.cookies;
        }
    }

    public static class Request extends Base<Connection.Request> implements Connection.Request {
        @Nullable
        private String body;
        private CookieManager cookieManager;
        private final Collection<Connection.KeyVal> data;
        private volatile boolean executing;
        private boolean followRedirects;
        private boolean ignoreContentType;
        private boolean ignoreHttpErrors;
        private int maxBodySizeBytes;
        private Parser parser;
        private boolean parserDefined;
        private String postDataCharset;
        @Nullable
        private Proxy proxy;
        @Nullable
        private SSLSocketFactory sslSocketFactory;
        private int timeoutMilliseconds;

        @Override
        public Connection.Request addHeader(String str, String str2) {
            return super.addHeader(str, str2);
        }

        @Override
        public String cookie(String str) {
            return super.cookie(str);
        }

        @Override
        public Connection.Request cookie(String str, String str2) {
            return super.cookie(str, str2);
        }

        @Override
        public Map cookies() {
            return super.cookies();
        }

        @Override
        public boolean hasCookie(String str) {
            return super.hasCookie(str);
        }

        @Override
        public boolean hasHeader(String str) {
            return super.hasHeader(str);
        }

        @Override
        public boolean hasHeaderWithValue(String str, String str2) {
            return super.hasHeaderWithValue(str, str2);
        }

        @Override
        public String header(String str) {
            return super.header(str);
        }

        @Override
        public Connection.Request header(String str, String str2) {
            return super.header(str, str2);
        }

        @Override
        public List headers(String str) {
            return super.headers(str);
        }

        @Override
        public Map headers() {
            return super.headers();
        }

        @Override
        public Connection.Request method(Connection.Method method) {
            return super.method(method);
        }

        @Override
        public Connection.Method method() {
            return super.method();
        }

        @Override
        public Map multiHeaders() {
            return super.multiHeaders();
        }

        @Override
        public Connection.Request removeCookie(String str) {
            return super.removeCookie(str);
        }

        @Override
        public Connection.Request removeHeader(String str) {
            return super.removeHeader(str);
        }

        @Override
        public URL url() {
            return super.url();
        }

        @Override
        public Connection.Request url(URL url) {
            return super.url(url);
        }

        static {
            System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
        }

        Request() {
            super();
            this.body = null;
            this.ignoreHttpErrors = false;
            this.ignoreContentType = false;
            this.parserDefined = false;
            this.postDataCharset = DataUtil.defaultCharsetName;
            this.executing = false;
            this.timeoutMilliseconds = 30000;
            this.maxBodySizeBytes = 2097152;
            this.followRedirects = true;
            this.data = new ArrayList();
            this.method = Connection.Method.GET;
            addHeader("Accept-Encoding", "gzip");
            addHeader(HttpConnection.USER_AGENT, HttpConnection.DEFAULT_UA);
            this.parser = Parser.htmlParser();
            this.cookieManager = new CookieManager();
        }

        Request(Request request) {
            super(request);
            this.body = null;
            this.ignoreHttpErrors = false;
            this.ignoreContentType = false;
            this.parserDefined = false;
            this.postDataCharset = DataUtil.defaultCharsetName;
            this.executing = false;
            this.proxy = request.proxy;
            this.postDataCharset = request.postDataCharset;
            this.timeoutMilliseconds = request.timeoutMilliseconds;
            this.maxBodySizeBytes = request.maxBodySizeBytes;
            this.followRedirects = request.followRedirects;
            ArrayList arrayList = new ArrayList();
            this.data = arrayList;
            arrayList.addAll(request.data());
            this.body = request.body;
            this.ignoreHttpErrors = request.ignoreHttpErrors;
            this.ignoreContentType = request.ignoreContentType;
            this.parser = request.parser.newInstance();
            this.parserDefined = request.parserDefined;
            this.sslSocketFactory = request.sslSocketFactory;
            this.cookieManager = request.cookieManager;
            this.executing = false;
        }

        @Override
        public Proxy proxy() {
            return this.proxy;
        }

        @Override
        public Request proxy(@Nullable Proxy proxy) {
            this.proxy = proxy;
            return this;
        }

        @Override
        public Request proxy(String str, int i) {
            this.proxy = new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(str, i));
            return this;
        }

        @Override
        public int timeout() {
            return this.timeoutMilliseconds;
        }

        @Override
        public Request timeout(int i) {
            Validate.isTrue(i >= 0, "Timeout milliseconds must be 0 (infinite) or greater");
            this.timeoutMilliseconds = i;
            return this;
        }

        @Override
        public int maxBodySize() {
            return this.maxBodySizeBytes;
        }

        @Override
        public Connection.Request maxBodySize(int i) {
            Validate.isTrue(i >= 0, "maxSize must be 0 (unlimited) or larger");
            this.maxBodySizeBytes = i;
            return this;
        }

        @Override
        public boolean followRedirects() {
            return this.followRedirects;
        }

        @Override
        public Connection.Request followRedirects(boolean z) {
            this.followRedirects = z;
            return this;
        }

        @Override
        public boolean ignoreHttpErrors() {
            return this.ignoreHttpErrors;
        }

        @Override
        public SSLSocketFactory sslSocketFactory() {
            return this.sslSocketFactory;
        }

        @Override
        public void sslSocketFactory(SSLSocketFactory sSLSocketFactory) {
            this.sslSocketFactory = sSLSocketFactory;
        }

        @Override
        public Connection.Request ignoreHttpErrors(boolean z) {
            this.ignoreHttpErrors = z;
            return this;
        }

        @Override
        public boolean ignoreContentType() {
            return this.ignoreContentType;
        }

        @Override
        public Connection.Request ignoreContentType(boolean z) {
            this.ignoreContentType = z;
            return this;
        }

        @Override
        public Request data(Connection.KeyVal keyVal) {
            Validate.notNull(keyVal, "Key val must not be null");
            this.data.add(keyVal);
            return this;
        }

        @Override
        public Collection<Connection.KeyVal> data() {
            return this.data;
        }

        @Override
        public Connection.Request requestBody(@Nullable String str) {
            this.body = str;
            return this;
        }

        @Override
        public String requestBody() {
            return this.body;
        }

        @Override
        public Request parser(Parser parser) {
            this.parser = parser;
            this.parserDefined = true;
            return this;
        }

        @Override
        public Parser parser() {
            return this.parser;
        }

        @Override
        public Connection.Request postDataCharset(String str) {
            Validate.notNull(str, "Charset must not be null");
            if (!Charset.isSupported(str)) {
                throw new IllegalCharsetNameException(str);
            }
            this.postDataCharset = str;
            return this;
        }

        @Override
        public String postDataCharset() {
            return this.postDataCharset;
        }

        public CookieManager cookieManager() {
            return this.cookieManager;
        }
    }

    public static class Response extends Base<Connection.Response> implements Connection.Response {
        private static final String LOCATION = "Location";
        private static final int MAX_REDIRECTS = 20;
        private static final Pattern xmlContentTypeRxp = Pattern.compile("(application|text)/\\w*\\+?xml.*");
        @Nullable
        private InputStream bodyStream;
        @Nullable
        private ByteBuffer byteData;
        @Nullable
        private String charset;
        @Nullable
        private HttpURLConnection conn;
        @Nullable
        private final String contentType;
        private boolean executed;
        private boolean inputStreamRead;
        private int numRedirects;
        private final Request req;
        private final int statusCode;
        private final String statusMessage;

        @Override
        public Connection.Response addHeader(String str, String str2) {
            return super.addHeader(str, str2);
        }

        @Override
        public String cookie(String str) {
            return super.cookie(str);
        }

        @Override
        public Connection.Response cookie(String str, String str2) {
            return super.cookie(str, str2);
        }

        @Override
        public Map cookies() {
            return super.cookies();
        }

        @Override
        public boolean hasCookie(String str) {
            return super.hasCookie(str);
        }

        @Override
        public boolean hasHeader(String str) {
            return super.hasHeader(str);
        }

        @Override
        public boolean hasHeaderWithValue(String str, String str2) {
            return super.hasHeaderWithValue(str, str2);
        }

        @Override
        public String header(String str) {
            return super.header(str);
        }

        @Override
        public Connection.Response header(String str, String str2) {
            return super.header(str, str2);
        }

        @Override
        public List headers(String str) {
            return super.headers(str);
        }

        @Override
        public Map headers() {
            return super.headers();
        }

        @Override
        public Connection.Response method(Connection.Method method) {
            return super.method(method);
        }

        @Override
        public Connection.Method method() {
            return super.method();
        }

        @Override
        public Map multiHeaders() {
            return super.multiHeaders();
        }

        @Override
        public Connection.Response removeCookie(String str) {
            return super.removeCookie(str);
        }

        @Override
        public Connection.Response removeHeader(String str) {
            return super.removeHeader(str);
        }

        @Override
        public URL url() {
            return super.url();
        }

        @Override
        public Connection.Response url(URL url) {
            return super.url(url);
        }

        Response() {
            super();
            this.executed = false;
            this.inputStreamRead = false;
            this.numRedirects = 0;
            this.statusCode = 400;
            this.statusMessage = "Request not made";
            this.req = new Request();
            this.contentType = null;
        }

        static Response execute(Request request) throws IOException {
            return execute(request, null);
        }

        static Response execute(Request request, @Nullable Response response) throws IOException {
            String outputContentType;
            HttpURLConnection createConnection;
            int responseCode;
            Response response2;
            synchronized (request) {
                Validate.isFalse(request.executing, "Multiple threads were detected trying to execute the same request concurrently. Make sure to use Connection#newRequest() and do not share an executing request between threads.");
                request.executing = true;
            }
            Validate.notNull(request, "Request must not be null");
            URL url = request.url();
            Validate.notNull(url, "URL must be specified to connect");
            String protocol = url.getProtocol();
            if (!protocol.equals(ProxyConfig.MATCH_HTTP) && !protocol.equals(ProxyConfig.MATCH_HTTPS)) {
                throw new MalformedURLException("Only http & https protocols supported");
            }
            boolean hasBody = request.method().hasBody();
            boolean z = request.requestBody() != null;
            if (!hasBody) {
                Validate.isFalse(z, "Cannot set a request body for HTTP method " + request.method());
            }
            Response response3 = null;
            try {
                try {
                    if (request.data().size() > 0 && (!hasBody || z)) {
                        serialiseRequestUrl(request);
                    } else if (hasBody) {
                        outputContentType = setOutputContentType(request);
                        long nanoTime = System.nanoTime();
                        createConnection = createConnection(request);
                        createConnection.connect();
                        if (createConnection.getDoOutput()) {
                            OutputStream outputStream = createConnection.getOutputStream();
                            try {
                                try {
                                    writePost(request, outputStream, outputContentType);
                                } catch (IOException e) {
                                    createConnection.disconnect();
                                    throw e;
                                }
                            } finally {
                                outputStream.close();
                            }
                        }
                        responseCode = createConnection.getResponseCode();
                        response2 = new Response(createConnection, request, response);
                        if (!response2.hasHeader(LOCATION) && request.followRedirects()) {
                            if (responseCode != 307) {
                                request.method(Connection.Method.GET);
                                request.data().clear();
                                request.requestBody(null);
                                request.removeHeader("Content-Type");
                            }
                            String header = response2.header(LOCATION);
                            Validate.notNull(header);
                            if (header.startsWith("http:/") && header.charAt(6) != '/') {
                                header = header.substring(6);
                            }
                            request.url(HttpConnection.encodeUrl(StringUtil.resolve(request.url(), header)));
                            request.executing = false;
                            return execute(request, response2);
                        } else if ((responseCode >= 200 || responseCode >= 400) && !request.ignoreHttpErrors()) {
                            throw new HttpStatusException("HTTP error fetching URL", responseCode, request.url().toString());
                        } else {
                            String contentType = response2.contentType();
                            if (contentType != null && !request.ignoreContentType() && !contentType.startsWith("text/") && !xmlContentTypeRxp.matcher(contentType).matches()) {
                                throw new UnsupportedMimeTypeException("Unhandled content type. Must be text/*, application/xml, or application/*+xml", contentType, request.url().toString());
                            }
                            response2.charset = DataUtil.getCharsetFromContentType(response2.contentType);
                            if (createConnection.getContentLength() != 0 && request.method() != Connection.Method.HEAD) {
                                InputStream errorStream = createConnection.getErrorStream() != null ? createConnection.getErrorStream() : createConnection.getInputStream();
                                response2.bodyStream = errorStream;
                                Validate.notNull(errorStream);
                                if (response2.hasHeaderWithValue(HttpConnection.CONTENT_ENCODING, "gzip")) {
                                    response2.bodyStream = new GZIPInputStream(response2.bodyStream);
                                } else if (response2.hasHeaderWithValue(HttpConnection.CONTENT_ENCODING, "deflate")) {
                                    response2.bodyStream = new InflaterInputStream(response2.bodyStream, new Inflater(true));
                                }
                                response2.bodyStream = ConstrainableInputStream.wrap(response2.bodyStream, 32768, request.maxBodySize()).timeout(nanoTime, request.timeout());
                            } else {
                                response2.byteData = DataUtil.emptyByteBuffer();
                            }
                            request.executing = false;
                            response2.executed = true;
                            return response2;
                        }
                    }
                    if (!response2.hasHeader(LOCATION)) {
                    }
                    if (responseCode >= 200) {
                    }
                    throw new HttpStatusException("HTTP error fetching URL", responseCode, request.url().toString());
                } catch (IOException e2) {
                    e = e2;
                    response3 = response2;
                    if (response3 != null) {
                        response3.safeClose();
                    }
                    throw e;
                }
                createConnection.connect();
                if (createConnection.getDoOutput()) {
                }
                responseCode = createConnection.getResponseCode();
                response2 = new Response(createConnection, request, response);
            } finally {
                request.executing = false;
            }
            outputContentType = null;
            long nanoTime2 = System.nanoTime();
            createConnection = createConnection(request);
        }

        @Override
        public int statusCode() {
            return this.statusCode;
        }

        @Override
        public String statusMessage() {
            return this.statusMessage;
        }

        @Override
        public String charset() {
            return this.charset;
        }

        @Override
        public Response charset(String str) {
            this.charset = str;
            return this;
        }

        @Override
        public String contentType() {
            return this.contentType;
        }

        @Override
        public Document parse() throws IOException {
            Validate.isTrue(this.executed, "Request must be executed (with .execute(), .get(), or .post() before parsing response");
            if (this.byteData != null) {
                this.bodyStream = new ByteArrayInputStream(this.byteData.array());
                this.inputStreamRead = false;
            }
            Validate.isFalse(this.inputStreamRead, "Input stream already read and parsed, cannot re-read.");
            Document parseInputStream = DataUtil.parseInputStream(this.bodyStream, this.charset, this.url.toExternalForm(), this.req.parser());
            parseInputStream.connection(new HttpConnection(this.req, this));
            this.charset = parseInputStream.outputSettings().charset().name();
            this.inputStreamRead = true;
            safeClose();
            return parseInputStream;
        }

        private void prepareByteData() {
            Validate.isTrue(this.executed, "Request must be executed (with .execute(), .get(), or .post() before getting response body");
            if (this.bodyStream == null || this.byteData != null) {
                return;
            }
            Validate.isFalse(this.inputStreamRead, "Request has already been read (with .parse())");
            try {
                try {
                    this.byteData = DataUtil.readToByteBuffer(this.bodyStream, this.req.maxBodySize());
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            } finally {
                this.inputStreamRead = true;
                safeClose();
            }
        }

        @Override
        public String body() {
            prepareByteData();
            Validate.notNull(this.byteData);
            String str = this.charset;
            String charBuffer = (str == null ? DataUtil.UTF_8 : Charset.forName(str)).decode(this.byteData).toString();
            this.byteData.rewind();
            return charBuffer;
        }

        @Override
        public byte[] bodyAsBytes() {
            prepareByteData();
            Validate.notNull(this.byteData);
            return this.byteData.array();
        }

        @Override
        public Connection.Response bufferUp() {
            prepareByteData();
            return this;
        }

        @Override
        public BufferedInputStream bodyStream() {
            Validate.isTrue(this.executed, "Request must be executed (with .execute(), .get(), or .post() before getting response body");
            Validate.isFalse(this.inputStreamRead, "Request has already been read");
            this.inputStreamRead = true;
            return ConstrainableInputStream.wrap(this.bodyStream, 32768, this.req.maxBodySize());
        }

        private static HttpURLConnection createConnection(Request request) throws IOException {
            URLConnection openConnection;
            Proxy proxy = request.proxy();
            if (proxy == null) {
                openConnection = request.url().openConnection();
            } else {
                openConnection = request.url().openConnection(proxy);
            }
            HttpURLConnection httpURLConnection = (HttpURLConnection) openConnection;
            httpURLConnection.setRequestMethod(request.method().name());
            httpURLConnection.setInstanceFollowRedirects(false);
            httpURLConnection.setConnectTimeout(request.timeout());
            httpURLConnection.setReadTimeout(request.timeout() / 2);
            if (request.sslSocketFactory() != null && (httpURLConnection instanceof HttpsURLConnection)) {
                ((HttpsURLConnection) httpURLConnection).setSSLSocketFactory(request.sslSocketFactory());
            }
            if (request.method().hasBody()) {
                httpURLConnection.setDoOutput(true);
            }
            CookieUtil.applyCookiesToRequest(request, httpURLConnection);
            for (Map.Entry entry : request.multiHeaders().entrySet()) {
                for (String str : (List) entry.getValue()) {
                    httpURLConnection.addRequestProperty((String) entry.getKey(), str);
                }
            }
            return httpURLConnection;
        }

        private void safeClose() {
            InputStream inputStream = this.bodyStream;
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException unused) {
                } catch (Throwable th) {
                    this.bodyStream = null;
                    throw th;
                }
                this.bodyStream = null;
            }
            HttpURLConnection httpURLConnection = this.conn;
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
                this.conn = null;
            }
        }

        private Response(HttpURLConnection httpURLConnection, Request request, @Nullable Response response) throws IOException {
            super();
            this.executed = false;
            this.inputStreamRead = false;
            this.numRedirects = 0;
            this.conn = httpURLConnection;
            this.req = request;
            this.method = Connection.Method.valueOf(httpURLConnection.getRequestMethod());
            this.url = httpURLConnection.getURL();
            this.statusCode = httpURLConnection.getResponseCode();
            this.statusMessage = httpURLConnection.getResponseMessage();
            this.contentType = httpURLConnection.getContentType();
            LinkedHashMap<String, List<String>> createHeaderMap = createHeaderMap(httpURLConnection);
            processResponseHeaders(createHeaderMap);
            CookieUtil.storeCookies(request, this.url, createHeaderMap);
            if (response != null) {
                for (Map.Entry entry : response.cookies().entrySet()) {
                    if (!hasCookie((String) entry.getKey())) {
                        cookie((String) entry.getKey(), (String) entry.getValue());
                    }
                }
                response.safeClose();
                int i = response.numRedirects + 1;
                this.numRedirects = i;
                if (i >= 20) {
                    throw new IOException(String.format("Too many redirects occurred trying to load URL %s", response.url()));
                }
            }
        }

        private static LinkedHashMap<String, List<String>> createHeaderMap(HttpURLConnection httpURLConnection) {
            LinkedHashMap<String, List<String>> linkedHashMap = new LinkedHashMap<>();
            int i = 0;
            while (true) {
                String headerFieldKey = httpURLConnection.getHeaderFieldKey(i);
                String headerField = httpURLConnection.getHeaderField(i);
                if (headerFieldKey == null && headerField == null) {
                    return linkedHashMap;
                }
                i++;
                if (headerFieldKey != null && headerField != null) {
                    if (linkedHashMap.containsKey(headerFieldKey)) {
                        linkedHashMap.get(headerFieldKey).add(headerField);
                    } else {
                        ArrayList arrayList = new ArrayList();
                        arrayList.add(headerField);
                        linkedHashMap.put(headerFieldKey, arrayList);
                    }
                }
            }
        }

        void processResponseHeaders(Map<String, List<String>> map) {
            for (Map.Entry<String, List<String>> entry : map.entrySet()) {
                String key = entry.getKey();
                if (key != null) {
                    List<String> value = entry.getValue();
                    if (key.equalsIgnoreCase("Set-Cookie")) {
                        for (String str : value) {
                            if (str != null) {
                                TokenQueue tokenQueue = new TokenQueue(str);
                                String trim = tokenQueue.chompTo("=").trim();
                                String trim2 = tokenQueue.consumeTo(";").trim();
                                if (trim.length() > 0 && !this.cookies.containsKey(trim)) {
                                    cookie(trim, trim2);
                                }
                            }
                        }
                    }
                    for (String str2 : value) {
                        addHeader(key, str2);
                    }
                }
            }
        }

        @Nullable
        private static String setOutputContentType(Connection.Request request) {
            String header = request.header("Content-Type");
            if (header == null) {
                if (HttpConnection.needsMultipart(request)) {
                    String mimeBoundary = DataUtil.mimeBoundary();
                    request.header("Content-Type", "multipart/form-data; boundary=" + mimeBoundary);
                    return mimeBoundary;
                }
                request.header("Content-Type", "application/x-www-form-urlencoded; charset=" + request.postDataCharset());
            } else if (header.contains("multipart/form-data") && !header.contains("boundary")) {
                String mimeBoundary2 = DataUtil.mimeBoundary();
                request.header("Content-Type", "multipart/form-data; boundary=" + mimeBoundary2);
                return mimeBoundary2;
            }
            return null;
        }

        private static void writePost(Connection.Request request, OutputStream outputStream, @Nullable String str) throws IOException {
            Collection<Connection.KeyVal> data = request.data();
            BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, request.postDataCharset()));
            if (str != null) {
                for (Connection.KeyVal keyVal : data) {
                    bufferedWriter.write("--");
                    bufferedWriter.write(str);
                    bufferedWriter.write("\r\n");
                    bufferedWriter.write("Content-Disposition: form-data; name=\"");
                    bufferedWriter.write(HttpConnection.encodeMimeName(keyVal.key()));
                    bufferedWriter.write("\"");
                    InputStream inputStream = keyVal.inputStream();
                    if (inputStream != null) {
                        bufferedWriter.write("; filename=\"");
                        bufferedWriter.write(HttpConnection.encodeMimeName(keyVal.value()));
                        bufferedWriter.write("\"\r\nContent-Type: ");
                        String contentType = keyVal.contentType();
                        if (contentType == null) {
                            contentType = HttpConnection.DefaultUploadType;
                        }
                        bufferedWriter.write(contentType);
                        bufferedWriter.write("\r\n\r\n");
                        bufferedWriter.flush();
                        DataUtil.crossStreams(inputStream, outputStream);
                        outputStream.flush();
                    } else {
                        bufferedWriter.write("\r\n\r\n");
                        bufferedWriter.write(keyVal.value());
                    }
                    bufferedWriter.write("\r\n");
                }
                bufferedWriter.write("--");
                bufferedWriter.write(str);
                bufferedWriter.write("--");
            } else {
                String requestBody = request.requestBody();
                if (requestBody != null) {
                    bufferedWriter.write(requestBody);
                } else {
                    boolean z = true;
                    for (Connection.KeyVal keyVal2 : data) {
                        if (z) {
                            z = false;
                        } else {
                            bufferedWriter.append(Typography.amp);
                        }
                        bufferedWriter.write(URLEncoder.encode(keyVal2.key(), request.postDataCharset()));
                        bufferedWriter.write(61);
                        bufferedWriter.write(URLEncoder.encode(keyVal2.value(), request.postDataCharset()));
                    }
                }
            }
            bufferedWriter.close();
        }

        private static void serialiseRequestUrl(Connection.Request request) throws IOException {
            boolean z;
            URL url = request.url();
            StringBuilder borrowBuilder = StringUtil.borrowBuilder();
            borrowBuilder.append(url.getProtocol());
            borrowBuilder.append("://");
            borrowBuilder.append(url.getAuthority());
            borrowBuilder.append(url.getPath());
            borrowBuilder.append("?");
            if (url.getQuery() != null) {
                borrowBuilder.append(url.getQuery());
                z = false;
            } else {
                z = true;
            }
            for (Connection.KeyVal keyVal : request.data()) {
                Validate.isFalse(keyVal.hasInputStream(), "InputStream data not supported in URL query string.");
                if (z) {
                    z = false;
                } else {
                    borrowBuilder.append(Typography.amp);
                }
                borrowBuilder.append(URLEncoder.encode(keyVal.key(), DataUtil.defaultCharsetName));
                borrowBuilder.append('=');
                borrowBuilder.append(URLEncoder.encode(keyVal.value(), DataUtil.defaultCharsetName));
            }
            request.url(new URL(StringUtil.releaseBuilder(borrowBuilder)));
            request.data().clear();
        }
    }

    public static boolean needsMultipart(Connection.Request request) {
        for (Connection.KeyVal keyVal : request.data()) {
            if (keyVal.hasInputStream()) {
                return true;
            }
        }
        return false;
    }

    public static class KeyVal implements Connection.KeyVal {
        @Nullable
        private String contentType;
        private String key;
        @Nullable
        private InputStream stream;
        private String value;

        public static KeyVal create(String str, String str2) {
            return new KeyVal(str, str2);
        }

        public static KeyVal create(String str, String str2, InputStream inputStream) {
            return new KeyVal(str, str2).inputStream(inputStream);
        }

        private KeyVal(String str, String str2) {
            Validate.notEmpty(str, "Data key must not be empty");
            Validate.notNull(str2, "Data value must not be null");
            this.key = str;
            this.value = str2;
        }

        @Override
        public KeyVal key(String str) {
            Validate.notEmpty(str, "Data key must not be empty");
            this.key = str;
            return this;
        }

        @Override
        public String key() {
            return this.key;
        }

        @Override
        public KeyVal value(String str) {
            Validate.notNull(str, "Data value must not be null");
            this.value = str;
            return this;
        }

        @Override
        public String value() {
            return this.value;
        }

        @Override
        public KeyVal inputStream(InputStream inputStream) {
            Validate.notNull(this.value, "Data input stream must not be null");
            this.stream = inputStream;
            return this;
        }

        @Override
        public InputStream inputStream() {
            return this.stream;
        }

        @Override
        public boolean hasInputStream() {
            return this.stream != null;
        }

        @Override
        public Connection.KeyVal contentType(String str) {
            Validate.notEmpty(str);
            this.contentType = str;
            return this;
        }

        @Override
        public String contentType() {
            return this.contentType;
        }

        public String toString() {
            return this.key + "=" + this.value;
        }
    }
}