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

org.sonarsource.scanner.lib.internal.http.HttpConfig Maven / Gradle / Ivy

The newest version!
/*
 * SonarScanner Java Library
 * Copyright (C) 2011-2024 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonarsource.scanner.lib.internal.http;

import java.net.InetSocketAddress;
import java.net.Proxy;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.format.DateTimeParseException;
import java.util.Map;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonarsource.scanner.lib.ScannerProperties;
import org.sonarsource.scanner.lib.internal.InternalProperties;
import org.sonarsource.scanner.lib.internal.http.ssl.CertificateStore;
import org.sonarsource.scanner.lib.internal.http.ssl.SslConfig;

import static java.lang.Integer.parseInt;
import static java.lang.String.format;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_CONNECT_TIMEOUT;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_KEYSTORE_PASSWORD;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_KEYSTORE_PATH;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_PROXY_HOST;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_PROXY_PASSWORD;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_PROXY_PORT;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_PROXY_USER;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_RESPONSE_TIMEOUT;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_SOCKET_TIMEOUT;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_TRUSTSTORE_PASSWORD;
import static org.sonarsource.scanner.lib.ScannerProperties.SONAR_SCANNER_TRUSTSTORE_PATH;

public class HttpConfig {

  private static final Logger LOG = LoggerFactory.getLogger(HttpConfig.class);

  static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(5);
  static final Duration DEFAULT_RESPONSE_TIMEOUT = Duration.ZERO;
  public static final String READ_TIMEOUT_SEC_PROPERTY = "sonar.ws.timeout";
  static final Duration DEFAULT_READ_TIMEOUT_SEC = Duration.ofSeconds(60);
  static final int DEFAULT_PROXY_PORT = 80;

  private final String webApiBaseUrl;
  private final String restApiBaseUrl;
  @Nullable
  private final String token;
  @Nullable
  private final String login;
  @Nullable
  private final String password;

  private final SslConfig sslConfig;
  private final Duration socketTimeout;
  private final Duration connectTimeout;
  private final Duration responseTimeout;
  @Nullable
  private final Proxy proxy;
  private final String proxyUser;
  private final String proxyPassword;
  private final String userAgent;

  public HttpConfig(Map bootstrapProperties, Path sonarUserHome) {
    this.webApiBaseUrl = StringUtils.removeEnd(bootstrapProperties.get(ScannerProperties.HOST_URL), "/");
    this.restApiBaseUrl = StringUtils.removeEnd(bootstrapProperties.get(ScannerProperties.API_BASE_URL), "/");
    this.token = bootstrapProperties.get(ScannerProperties.SONAR_TOKEN);
    this.login = bootstrapProperties.get(ScannerProperties.SONAR_LOGIN);
    this.password = bootstrapProperties.get(ScannerProperties.SONAR_PASSWORD);
    this.userAgent = format("%s/%s", bootstrapProperties.get(InternalProperties.SCANNER_APP), bootstrapProperties.get(InternalProperties.SCANNER_APP_VERSION));
    this.socketTimeout = loadDuration(bootstrapProperties, SONAR_SCANNER_SOCKET_TIMEOUT, READ_TIMEOUT_SEC_PROPERTY, DEFAULT_READ_TIMEOUT_SEC);
    this.connectTimeout = loadDuration(bootstrapProperties, SONAR_SCANNER_CONNECT_TIMEOUT, null, DEFAULT_CONNECT_TIMEOUT);
    this.responseTimeout = loadDuration(bootstrapProperties, SONAR_SCANNER_RESPONSE_TIMEOUT, null, DEFAULT_RESPONSE_TIMEOUT);
    this.sslConfig = loadSslConfig(bootstrapProperties, sonarUserHome);
    this.proxy = loadProxy(bootstrapProperties);
    this.proxyUser = loadProxyUser(bootstrapProperties);
    this.proxyPassword = loadProxyPassword(bootstrapProperties);
  }

  private static String loadProxyPassword(Map bootstrapProperties) {
    var scannerProxyPwd = bootstrapProperties.get(SONAR_SCANNER_PROXY_PASSWORD);
    return scannerProxyPwd != null ? scannerProxyPwd : System.getProperty("http.proxyPassword", "");
  }

  private static String loadProxyUser(Map bootstrapProperties) {
    var scannerProxyUser = bootstrapProperties.get(SONAR_SCANNER_PROXY_USER);
    return scannerProxyUser != null ? scannerProxyUser : System.getProperty("http.proxyUser", "");
  }

  private static Duration loadDuration(Map bootstrapProperties, String propKey, @Nullable String deprecatedPropKey, Duration defaultValue) {
    if (bootstrapProperties.containsKey(propKey)) {
      return parseDurationProperty(bootstrapProperties.get(propKey), propKey);
    } else if (deprecatedPropKey != null && bootstrapProperties.containsKey(deprecatedPropKey)) {
      LOG.warn("Property {} is deprecated and will be removed in a future version. Please use {} instead.", deprecatedPropKey, propKey);
      return parseDurationProperty(bootstrapProperties.get(deprecatedPropKey), deprecatedPropKey);
    } else {
      return defaultValue;
    }
  }

  @Nullable
  private static Proxy loadProxy(Map bootstrapProperties) {
    // OkHttp detects 'http.proxyHost' java property already, so just focus on sonar properties
    String proxyHost = defaultIfBlank(bootstrapProperties.get(SONAR_SCANNER_PROXY_HOST), null);
    if (proxyHost != null) {
      int proxyPort;
      if (bootstrapProperties.containsKey(SONAR_SCANNER_PROXY_PORT)) {
        proxyPort = parseIntProperty(bootstrapProperties.get(SONAR_SCANNER_PROXY_PORT), SONAR_SCANNER_PROXY_PORT);
      } else {
        proxyPort = DEFAULT_PROXY_PORT;
      }
      return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
    } else {
      return null;
    }
  }

  /**
   * For testing, we can accept timeouts that are smaller than a second, expressed using ISO-8601 format for durations.
   * If we can't parse as ISO-8601, then fallback to the official format that is simply the number of seconds
   */
  private static Duration parseDurationProperty(String propValue, String propKey) {
    try {
      return Duration.parse(propValue);
    } catch (DateTimeParseException e) {
      return Duration.ofSeconds(parseIntProperty(propValue, propKey));
    }
  }

  private static int parseIntProperty(String propValue, String propKey) {
    try {
      return parseInt(propValue);
    } catch (NumberFormatException e) {
      throw new IllegalArgumentException(propKey + " is not a valid integer: " + propValue, e);
    }
  }

  private static SslConfig loadSslConfig(Map bootstrapProperties, Path sonarUserHome) {
    var keyStore = loadKeyStoreConfig(bootstrapProperties, sonarUserHome);
    var trustStore = loadTrustStoreConfig(bootstrapProperties, sonarUserHome);
    return new SslConfig(keyStore, trustStore);
  }

  @Nullable
  private static CertificateStore loadTrustStoreConfig(Map bootstrapProperties, Path sonarUserHome) {
    var trustStorePath = parseFileProperty(bootstrapProperties, SONAR_SCANNER_TRUSTSTORE_PATH, "truststore", sonarUserHome.resolve("ssl/truststore.p12"));
    if (trustStorePath != null) {
      LOG.debug("Using truststore: {}", trustStorePath);
      var trustStorePassword = defaultIfBlank(bootstrapProperties.get(SONAR_SCANNER_TRUSTSTORE_PASSWORD), CertificateStore.DEFAULT_PASSWORD);
      return new CertificateStore(trustStorePath, trustStorePassword);
    }
    return null;
  }

  @Nullable
  private static CertificateStore loadKeyStoreConfig(Map bootstrapProperties, Path sonarUserHome) {
    var keyStorePath = parseFileProperty(bootstrapProperties, SONAR_SCANNER_KEYSTORE_PATH, "keystore", sonarUserHome.resolve("ssl/keystore.p12"));
    if (keyStorePath != null) {
      LOG.debug("Using keystore: {}", keyStorePath);
      var keyStorePassword = defaultIfBlank(bootstrapProperties.get(SONAR_SCANNER_KEYSTORE_PASSWORD), CertificateStore.DEFAULT_PASSWORD);
      return new CertificateStore(keyStorePath, keyStorePassword);
    }
    return null;
  }

  @Nullable
  private static Path parseFileProperty(Map bootstrapProperties, String propKey, String labelForLogs, Path defaultPath) {
    if (bootstrapProperties.containsKey(propKey)) {
      var keyStorePath = Paths.get(bootstrapProperties.get(propKey));
      if (!Files.exists(keyStorePath)) {
        throw new IllegalArgumentException("The " + labelForLogs + " file does not exist: " + keyStorePath);
      }
      return keyStorePath;
    } else if (Files.isRegularFile(defaultPath)) {
      return defaultPath;
    }
    return null;
  }

  public String getWebApiBaseUrl() {
    return webApiBaseUrl;
  }

  public String getRestApiBaseUrl() {
    return restApiBaseUrl;
  }

  @Nullable
  public String getToken() {
    return token;
  }

  @Nullable
  public String getLogin() {
    return login;
  }

  @Nullable
  public String getPassword() {
    return password;
  }

  public String getUserAgent() {
    return userAgent;
  }

  public SslConfig getSslConfig() {
    return sslConfig;
  }

  public Duration getConnectTimeout() {
    return connectTimeout;
  }

  public Duration getResponseTimeout() {
    return responseTimeout;
  }

  public Duration getSocketTimeout() {
    return socketTimeout;
  }

  @Nullable
  public Proxy getProxy() {
    return proxy;
  }

  public String getProxyUser() {
    return proxyUser;
  }

  public String getProxyPassword() {
    return proxyPassword;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy