// 
// Decompiled by Procyon v0.6.0
// 

package io.sentry.transport;

import org.jetbrains.annotations.TestOnly;
import java.io.InputStream;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
import io.sentry.SentryEnvelope;
import javax.net.ssl.SSLSocketFactory;
import java.util.Iterator;
import javax.net.ssl.HttpsURLConnection;
import java.util.Map;
import java.io.IOException;
import java.net.HttpURLConnection;
import io.sentry.SentryLevel;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.net.Authenticator;
import io.sentry.SentryOptions;
import org.jetbrains.annotations.NotNull;
import io.sentry.RequestDetails;
import org.jetbrains.annotations.Nullable;
import java.net.Proxy;
import java.nio.charset.Charset;

final class HttpConnection
{
    private static final Charset UTF_8;
    @Nullable
    private final Proxy proxy;
    @NotNull
    private final RequestDetails requestDetails;
    @NotNull
    private final SentryOptions options;
    @NotNull
    private final RateLimiter rateLimiter;
    
    public HttpConnection(@NotNull final SentryOptions options, @NotNull final RequestDetails requestDetails, @NotNull final RateLimiter rateLimiter) {
        this(options, requestDetails, AuthenticatorWrapper.getInstance(), rateLimiter);
    }
    
    HttpConnection(@NotNull final SentryOptions options, @NotNull final RequestDetails requestDetails, @NotNull final AuthenticatorWrapper authenticatorWrapper, @NotNull final RateLimiter rateLimiter) {
        this.requestDetails = requestDetails;
        this.options = options;
        this.rateLimiter = rateLimiter;
        this.proxy = this.resolveProxy(options.getProxy());
        if (this.proxy != null && options.getProxy() != null) {
            final String proxyUser = options.getProxy().getUser();
            final String proxyPassword = options.getProxy().getPass();
            if (proxyUser != null && proxyPassword != null) {
                authenticatorWrapper.setDefault(new ProxyAuthenticator(proxyUser, proxyPassword));
            }
        }
    }
    
    @Nullable
    private Proxy resolveProxy(@Nullable final SentryOptions.Proxy optionsProxy) {
        Proxy proxy = null;
        if (optionsProxy != null) {
            final String port = optionsProxy.getPort();
            final String host = optionsProxy.getHost();
            if (port != null && host != null) {
                try {
                    Proxy.Type type;
                    if (optionsProxy.getType() != null) {
                        type = optionsProxy.getType();
                    }
                    else {
                        type = Proxy.Type.HTTP;
                    }
                    final InetSocketAddress proxyAddr = new InetSocketAddress(host, Integer.parseInt(port));
                    proxy = new Proxy(type, proxyAddr);
                }
                catch (final NumberFormatException e) {
                    this.options.getLogger().log(SentryLevel.ERROR, e, "Failed to parse Sentry Proxy port: " + optionsProxy.getPort() + ". Proxy is ignored", new Object[0]);
                }
            }
        }
        return proxy;
    }
    
    @NotNull
    HttpURLConnection open() throws IOException {
        return (HttpURLConnection)((this.proxy == null) ? this.requestDetails.getUrl().openConnection() : this.requestDetails.getUrl().openConnection(this.proxy));
    }
    
    @NotNull
    private HttpURLConnection createConnection() throws IOException {
        final HttpURLConnection connection = this.open();
        for (final Map.Entry<String, String> header : this.requestDetails.getHeaders().entrySet()) {
            connection.setRequestProperty(header.getKey(), header.getValue());
        }
        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
        connection.setRequestProperty("Content-Encoding", "gzip");
        connection.setRequestProperty("Content-Type", "application/x-sentry-envelope");
        connection.setRequestProperty("Accept", "application/json");
        connection.setRequestProperty("Connection", "close");
        connection.setConnectTimeout(this.options.getConnectionTimeoutMillis());
        connection.setReadTimeout(this.options.getReadTimeoutMillis());
        final SSLSocketFactory sslSocketFactory = this.options.getSslSocketFactory();
        if (connection instanceof HttpsURLConnection && sslSocketFactory != null) {
            ((HttpsURLConnection)connection).setSSLSocketFactory(sslSocketFactory);
        }
        connection.connect();
        return connection;
    }
    
    @NotNull
    public TransportResult send(@NotNull final SentryEnvelope envelope) throws IOException {
        this.options.getSocketTagger().tagSockets();
        final HttpURLConnection connection = this.createConnection();
        TransportResult result = null;
        try (final OutputStream outputStream = connection.getOutputStream();
             final GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
            this.options.getSerializer().serialize(envelope, gzip);
        }
        catch (final Throwable e) {
            this.options.getLogger().log(SentryLevel.ERROR, e, "An exception occurred while submitting the envelope to the Sentry server.", new Object[0]);
        }
        finally {
            result = this.readAndLog(connection);
            this.options.getSocketTagger().untagSockets();
        }
        return result;
    }
    
    @NotNull
    private TransportResult readAndLog(@NotNull final HttpURLConnection connection) {
        try {
            final int responseCode = connection.getResponseCode();
            this.updateRetryAfterLimits(connection, responseCode);
            if (!this.isSuccessfulResponseCode(responseCode)) {
                this.options.getLogger().log(SentryLevel.ERROR, "Request failed, API returned %s", responseCode);
                if (this.options.isDebug()) {
                    final String errorMessage = this.getErrorMessageFromStream(connection);
                    this.options.getLogger().log(SentryLevel.ERROR, "%s", errorMessage);
                }
                return TransportResult.error(responseCode);
            }
            this.options.getLogger().log(SentryLevel.DEBUG, "Envelope sent successfully.", new Object[0]);
            return TransportResult.success();
        }
        catch (final IOException e) {
            this.options.getLogger().log(SentryLevel.ERROR, e, "Error reading and logging the response stream", new Object[0]);
        }
        finally {
            this.closeAndDisconnect(connection);
        }
        return TransportResult.error();
    }
    
    public void updateRetryAfterLimits(@NotNull final HttpURLConnection connection, final int responseCode) {
        final String retryAfterHeader = connection.getHeaderField("Retry-After");
        final String sentryRateLimitHeader = connection.getHeaderField("X-Sentry-Rate-Limits");
        this.rateLimiter.updateRetryAfterLimits(sentryRateLimitHeader, retryAfterHeader, responseCode);
    }
    
    private void closeAndDisconnect(@NotNull final HttpURLConnection connection) {
        try {
            connection.getInputStream().close();
        }
        catch (final IOException ex) {}
        finally {
            connection.disconnect();
        }
    }
    
    @NotNull
    private String getErrorMessageFromStream(@NotNull final HttpURLConnection connection) {
        try (final InputStream errorStream = connection.getErrorStream();
             final BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream, HttpConnection.UTF_8))) {
            final StringBuilder sb = new StringBuilder();
            boolean first = true;
            String line;
            while ((line = reader.readLine()) != null) {
                if (!first) {
                    sb.append("\n");
                }
                sb.append(line);
                first = false;
            }
            return sb.toString();
        }
        catch (final IOException e) {
            return "Failed to obtain error message while analyzing send failure.";
        }
    }
    
    private boolean isSuccessfulResponseCode(final int responseCode) {
        return responseCode == 200;
    }
    
    @TestOnly
    @Nullable
    Proxy getProxy() {
        return this.proxy;
    }
    
    static {
        UTF_8 = Charset.forName("UTF-8");
    }
}
