import org.sonar.server.user.DeprecatedUserFinder;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexer;
+import org.sonar.server.util.OkHttpClientProvider;
import org.sonar.server.view.index.ViewIndex;
import org.sonar.server.view.index.ViewIndexer;
import org.sonarqube.ws.Rules;
// issues
IssueIndex.class,
+
+ new OkHttpClientProvider()
};
}
);
assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
- + 24 // level 1
+ + 25 // level 1
+ 46 // content of DaoModule
+ 2 // content of EsSearchModule
+ 62 // content of CorePropertyDefinitions
import org.sonar.server.search.EsSearchModule;
import org.sonar.server.setting.ThreadLocalSettings;
import org.sonar.server.user.ThreadLocalUserSession;
+import org.sonar.server.util.OkHttpClientProvider;
public class PlatformLevel1 extends PlatformLevel {
private final Platform platform;
// issues
IssueIndex.class,
+ new OkHttpClientProvider(),
// Classes kept for backward compatibility of plugins/libs (like sonar-license) that are directly calling classes from the core
org.sonar.core.properties.PropertiesDao.class);
addAll(CorePropertyDefinitions.all());
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.sonar.server.util;
+
+import okhttp3.OkHttpClient;
+import org.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.config.Settings;
+import org.sonar.api.server.ServerSide;
+import org.sonar.process.ProcessProperties;
+import org.sonarqube.ws.client.OkHttpClientBuilder;
+
+import static java.lang.String.format;
+
+/**
+ * Provide a unique instance of {@link OkHttpClient} which configuration:
+ * <ul>
+ * <li>supports HTTPS</li>
+ * <li>supports proxy, including authentication, as defined by the properties like "http.proxyHost" in
+ * conf/sonar.properties</li>
+ * <li>has connect and read timeouts of 10 seconds each</li>
+ * <li>sends automatically the HTTP header "User-Agent" with value "SonarQube/{version}", for instance "SonarQube/6.2"</li>
+ * </ul>
+ */
+@ServerSide
+@ComputeEngineSide
+public class OkHttpClientProvider extends ProviderAdapter {
+
+ private static final int DEFAULT_CONNECT_TIMEOUT_IN_MS = 10_000;
+ private static final int DEFAULT_READ_TIMEOUT_IN_MS = 10_000;
+
+ private okhttp3.OkHttpClient okHttpClient;
+
+ /**
+ * @return a {@link OkHttpClient} singleton
+ */
+ public OkHttpClient provide(Settings settings, SonarRuntime runtime) {
+ if (okHttpClient == null) {
+ OkHttpClientBuilder builder = new OkHttpClientBuilder();
+ builder.setConnectTimeoutMs(DEFAULT_CONNECT_TIMEOUT_IN_MS);
+ builder.setReadTimeoutMs(DEFAULT_READ_TIMEOUT_IN_MS);
+ // no need to define proxy URL as system-wide proxy is used and properly
+ // configured by bootstrap process.
+ builder.setProxyLogin(settings.getString(ProcessProperties.HTTP_PROXY_USER));
+ builder.setProxyPassword(settings.getString(ProcessProperties.HTTP_PROXY_PASSWORD));
+ builder.setUserAgent(format("SonarQube/%s", runtime.getApiVersion().toString()));
+ okHttpClient = builder.build();
+ }
+ return okHttpClient;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.sonar.server.util;
+
+import java.io.IOException;
+import java.util.Base64;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.SonarQubeSide;
+import org.sonar.api.SonarRuntime;
+import org.sonar.api.config.MapSettings;
+import org.sonar.api.config.Settings;
+import org.sonar.api.internal.SonarRuntimeImpl;
+import org.sonar.api.utils.Version;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OkHttpClientProviderTest {
+
+ private Settings settings = new MapSettings();
+ private SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.parse("6.2"), SonarQubeSide.SERVER);
+ private final OkHttpClientProvider underTest = new OkHttpClientProvider();
+
+ @Rule
+ public MockWebServer server = new MockWebServer();
+
+ @Test
+ public void get_returns_a_OkHttpClient_with_default_configuration() throws Exception {
+ OkHttpClient client = underTest.provide(settings, runtime);
+
+ assertThat(client.connectTimeoutMillis()).isEqualTo(10_000);
+ assertThat(client.readTimeoutMillis()).isEqualTo(10_000);
+ assertThat(client.proxy()).isNull();
+
+ RecordedRequest recordedRequest = call(client);
+ assertThat(recordedRequest.getHeader("User-Agent")).isEqualTo("SonarQube/6.2");
+ assertThat(recordedRequest.getHeader("Proxy-Authorization")).isNull();
+ }
+
+ @Test
+ public void get_returns_a_OkHttpClient_with_proxy_authentication() throws Exception {
+ settings.setProperty("http.proxyUser", "the-login");
+ settings.setProperty("http.proxyPassword", "the-password");
+
+ OkHttpClient client = underTest.provide(settings, runtime);
+ RecordedRequest recordedRequest = call(client);
+
+ assertThat(recordedRequest.getHeader("Proxy-Authorization")).isEqualTo("Basic " + Base64.getEncoder().encodeToString("the-login:the-password".getBytes()));
+ }
+
+ @Test
+ public void get_returns_a_singleton() {
+ OkHttpClient client1 = underTest.provide(settings, runtime);
+ OkHttpClient client2 = underTest.provide(settings, runtime);
+ assertThat(client2).isNotNull().isSameAs(client1);
+ }
+
+ private RecordedRequest call(OkHttpClient client) throws IOException, InterruptedException {
+ server.enqueue(new MockResponse().setBody("pong"));
+ client.newCall(new Request.Builder().url(server.url("/ping")).build()).execute();
+
+ return server.takeRequest();
+ }
+}
import java.util.Map;
import org.junit.Test;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
-import org.sonar.scanner.bootstrap.BatchWsClient;
-import org.sonar.scanner.bootstrap.BatchWsClientProvider;
-import org.sonar.scanner.bootstrap.GlobalProperties;
import org.sonarqube.ws.client.HttpConnector;
import static org.assertj.core.api.Assertions.assertThat;
public class BatchWsClientProviderTest {
- BatchWsClientProvider underTest = new BatchWsClientProvider();
- EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3");
+ private BatchWsClientProvider underTest = new BatchWsClientProvider();
+ private EnvironmentInformation env = new EnvironmentInformation("Maven Plugin", "2.3");
@Test
public void provide_client_with_default_settings() {
- GlobalProperties settings = new GlobalProperties(new HashMap<String, String>());
+ GlobalProperties settings = new GlobalProperties(new HashMap<>());
BatchWsClient client = underTest.provide(settings, env);
assertThat(httpConnector.okHttpClient().proxy()).isNull();
assertThat(httpConnector.okHttpClient().connectTimeoutMillis()).isEqualTo(5_000);
assertThat(httpConnector.okHttpClient().readTimeoutMillis()).isEqualTo(60_000);
- assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3");
}
@Test
HttpConnector httpConnector = (HttpConnector) client.wsConnector();
assertThat(httpConnector.baseUrl()).isEqualTo("https://here/sonarqube/");
assertThat(httpConnector.okHttpClient().proxy()).isNull();
- assertThat(httpConnector.userAgent()).isEqualTo("Maven Plugin/2.3");
}
@Test
public void build_singleton() {
- GlobalProperties settings = new GlobalProperties(new HashMap<String, String>());
+ GlobalProperties settings = new GlobalProperties(new HashMap<>());
BatchWsClient first = underTest.provide(settings, env);
BatchWsClient second = underTest.provide(settings, env);
assertThat(first).isSameAs(second);
*/
package org.sonarqube.ws.client;
-import java.io.FileInputStream;
import java.io.IOException;
import java.net.Proxy;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.util.Arrays;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
import okhttp3.Call;
-import okhttp3.ConnectionSpec;
import okhttp3.Credentials;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Strings.nullToEmpty;
import static java.lang.String.format;
-import static java.util.Arrays.asList;
/**
* Connect to any SonarQube server available through HTTP or HTTPS.
public static final int DEFAULT_CONNECT_TIMEOUT_MILLISECONDS = 30_000;
public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60_000;
- private static final String NONE = "NONE";
- private static final String P11KEYSTORE = "PKCS11";
/**
* Base URL with trailing slash, for instance "https://localhost/sonarqube/".
* It is required for further usage of {@link HttpUrl#resolve(String)}.
*/
private final HttpUrl baseUrl;
- private final String userAgent;
private final String credentials;
- private final String proxyCredentials;
private final OkHttpClient okHttpClient;
private HttpConnector(Builder builder) {
this.baseUrl = HttpUrl.parse(builder.url.endsWith("/") ? builder.url : format("%s/", builder.url));
checkArgument(this.baseUrl != null, "Malformed URL: '%s'", builder.url);
- this.userAgent = builder.userAgent;
+
+ OkHttpClientBuilder okHttpClientBuilder = new OkHttpClientBuilder();
+ okHttpClientBuilder.setUserAgent(builder.userAgent);
if (isNullOrEmpty(builder.login)) {
// no login nor access token
// the Basic credentials consider an empty password.
this.credentials = Credentials.basic(builder.login, nullToEmpty(builder.password));
}
- // proxy credentials can be used on system-wide proxies, so even if builder.proxy is null
- if (isNullOrEmpty(builder.proxyLogin)) {
- this.proxyCredentials = null;
- } else {
- this.proxyCredentials = Credentials.basic(builder.proxyLogin, nullToEmpty(builder.proxyPassword));
- }
- this.okHttpClient = buildClient(builder);
- }
-
- private static OkHttpClient buildClient(Builder builder) {
- OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
- if (builder.proxy != null) {
- okHttpClientBuilder.proxy(builder.proxy);
- }
-
- okHttpClientBuilder.connectTimeout(builder.connectTimeoutMs, TimeUnit.MILLISECONDS);
- okHttpClientBuilder.readTimeout(builder.readTimeoutMs, TimeUnit.MILLISECONDS);
-
- ConnectionSpec tls = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
- .allEnabledTlsVersions()
- .allEnabledCipherSuites()
- .supportsTlsExtensions(true)
- .build();
- okHttpClientBuilder.connectionSpecs(asList(tls, ConnectionSpec.CLEARTEXT));
- X509TrustManager systemDefaultTrustManager = systemDefaultTrustManager();
- okHttpClientBuilder.sslSocketFactory(systemDefaultSslSocketFactory(systemDefaultTrustManager), systemDefaultTrustManager);
-
- return okHttpClientBuilder.build();
- }
-
- private static X509TrustManager systemDefaultTrustManager() {
- try {
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- trustManagerFactory.init((KeyStore) null);
- TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
- if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
- throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
- }
- return (X509TrustManager) trustManagers[0];
- } catch (GeneralSecurityException e) {
- // The system has no TLS. Just give up.
- throw new AssertionError(e);
- }
- }
-
- private static SSLSocketFactory systemDefaultSslSocketFactory(X509TrustManager trustManager) {
- KeyManager[] defaultKeyManager;
- try {
- defaultKeyManager = getDefaultKeyManager();
- } catch (Exception e) {
- throw new IllegalStateException("Unable to get default key manager", e);
- }
- try {
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(defaultKeyManager, new TrustManager[] {trustManager}, null);
- return sslContext.getSocketFactory();
- } catch (GeneralSecurityException e) {
- // The system has no TLS. Just give up.
- throw new AssertionError(e);
- }
- }
-
- private static void logDebug(String msg) {
- boolean debugEnabled = "all".equals(System.getProperty("javax.net.debug"));
- if (debugEnabled) {
- System.out.println(msg);
- }
- }
-
- /**
- * Inspired from sun.security.ssl.SSLContextImpl#getDefaultKeyManager()
- */
- private static synchronized KeyManager[] getDefaultKeyManager() throws Exception {
-
- final String defaultKeyStore = System.getProperty("javax.net.ssl.keyStore", "");
- String defaultKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
- String defaultKeyStoreProvider = System.getProperty("javax.net.ssl.keyStoreProvider", "");
-
- logDebug("keyStore is : " + defaultKeyStore);
- logDebug("keyStore type is : " + defaultKeyStoreType);
- logDebug("keyStore provider is : " + defaultKeyStoreProvider);
-
- if (P11KEYSTORE.equals(defaultKeyStoreType) && !NONE.equals(defaultKeyStore)) {
- throw new IllegalArgumentException("if keyStoreType is " + P11KEYSTORE + ", then keyStore must be " + NONE);
- }
-
- KeyStore ks = null;
- String defaultKeyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword", "");
- char[] passwd = defaultKeyStorePassword.isEmpty() ? null : defaultKeyStorePassword.toCharArray();
-
- /**
- * Try to initialize key store.
- */
- if (!defaultKeyStoreType.isEmpty()) {
- logDebug("init keystore");
- if (defaultKeyStoreProvider.isEmpty()) {
- ks = KeyStore.getInstance(defaultKeyStoreType);
- } else {
- ks = KeyStore.getInstance(defaultKeyStoreType, defaultKeyStoreProvider);
- }
- if (!defaultKeyStore.isEmpty() && !NONE.equals(defaultKeyStore)) {
- try (FileInputStream fs = new FileInputStream(defaultKeyStore)) {
- ks.load(fs, passwd);
- }
- } else {
- ks.load(null, passwd);
- }
- }
-
- /*
- * Try to initialize key manager.
- */
- logDebug("init keymanager of type " + KeyManagerFactory.getDefaultAlgorithm());
- KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
-
- if (P11KEYSTORE.equals(defaultKeyStoreType)) {
- // do not pass key passwd if using token
- kmf.init(ks, null);
- } else {
- kmf.init(ks, passwd);
- }
-
- return kmf.getKeyManagers();
+ okHttpClientBuilder.setProxy(builder.proxy);
+ okHttpClientBuilder.setProxyLogin(builder.proxyLogin);
+ okHttpClientBuilder.setProxyPassword(builder.proxyPassword);
+ okHttpClientBuilder.setConnectTimeoutMs(builder.connectTimeoutMs);
+ okHttpClientBuilder.setReadTimeoutMs(builder.readTimeoutMs);
+ this.okHttpClient = okHttpClientBuilder.build();
}
@Override
return baseUrl.url().toExternalForm();
}
- @CheckForNull
- public String userAgent() {
- return userAgent;
- }
-
public OkHttpClient okHttpClient() {
return okHttpClient;
}
if (credentials != null) {
okHttpRequestBuilder.header("Authorization", credentials);
}
- if (proxyCredentials != null) {
- okHttpRequestBuilder.header("Proxy-Authorization", proxyCredentials);
- }
- if (userAgent != null) {
- okHttpRequestBuilder.addHeader("User-Agent", userAgent);
- }
return okHttpRequestBuilder;
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.sonarqube.ws.client;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.Proxy;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import okhttp3.ConnectionSpec;
+import okhttp3.Credentials;
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import static com.google.common.base.Strings.nullToEmpty;
+import static java.util.Arrays.asList;
+
+/**
+ * Helper to build an instance of {@link okhttp3.OkHttpClient} that
+ * correctly supports HTTPS and proxy authentication. It also handles
+ * sending of User-Agent header.
+ */
+public class OkHttpClientBuilder {
+
+ private static final String NONE = "NONE";
+ private static final String P11KEYSTORE = "PKCS11";
+
+ private String userAgent;
+ private Proxy proxy;
+ private String proxyLogin;
+ private String proxyPassword;
+ private long connectTimeoutMs = -1;
+ private long readTimeoutMs = -1;
+
+ /**
+ * Optional User-Agent. If set, then all the requests sent by the
+ * {@link OkHttpClient} will include the header "User-Agent".
+ */
+ public OkHttpClientBuilder setUserAgent(@Nullable String s) {
+ this.userAgent = s;
+ return this;
+ }
+
+ /**
+ * Optional proxy. If set, then all the requests sent by the
+ * {@link OkHttpClient} will reach the proxy. If not set,
+ * then the system-wide proxy is used.
+ */
+ public OkHttpClientBuilder setProxy(@Nullable Proxy proxy) {
+ this.proxy = proxy;
+ return this;
+ }
+
+ /**
+ * Login required for proxy authentication.
+ */
+ public OkHttpClientBuilder setProxyLogin(@Nullable String s) {
+ this.proxyLogin = s;
+ return this;
+ }
+
+ /**
+ * Password used for proxy authentication. It is ignored if
+ * proxy login is not defined (see {@link #setProxyLogin(String)}).
+ * It can be null or empty when login is defined.
+ */
+ public OkHttpClientBuilder setProxyPassword(@Nullable String s) {
+ this.proxyPassword = s;
+ return this;
+ }
+
+ /**
+ * Sets the default connect timeout for new connections. A value of 0 means no timeout.
+ * Default is defined by OkHttp (10 seconds in OkHttp 3.3).
+ */
+ public OkHttpClientBuilder setConnectTimeoutMs(long l) {
+ if (l < 0) {
+ throw new IllegalArgumentException("Connect timeout must be positive. Got " + l);
+ }
+ this.connectTimeoutMs = l;
+ return this;
+ }
+
+ /**
+ * Sets the default read timeout for new connections. A value of 0 means no timeout.
+ * Default is defined by OkHttp (10 seconds in OkHttp 3.3).
+ */
+ public OkHttpClientBuilder setReadTimeoutMs(long l) {
+ if (l < 0) {
+ throw new IllegalArgumentException("Read timeout must be positive. Got " + l);
+ }
+ this.readTimeoutMs = l;
+ return this;
+ }
+
+ public OkHttpClient build() {
+ OkHttpClient.Builder builder = new OkHttpClient.Builder();
+ builder.proxy(proxy);
+ if (connectTimeoutMs >= 0) {
+ builder.connectTimeout(connectTimeoutMs, TimeUnit.MILLISECONDS);
+ }
+ if (readTimeoutMs >= 0) {
+ builder.readTimeout(readTimeoutMs, TimeUnit.MILLISECONDS);
+ }
+ builder.addInterceptor(this::completeHeaders);
+
+ ConnectionSpec tls = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .allEnabledTlsVersions()
+ .allEnabledCipherSuites()
+ .supportsTlsExtensions(true)
+ .build();
+ builder.connectionSpecs(asList(tls, ConnectionSpec.CLEARTEXT));
+ X509TrustManager systemDefaultTrustManager = systemDefaultTrustManager();
+ builder.sslSocketFactory(systemDefaultSslSocketFactory(systemDefaultTrustManager), systemDefaultTrustManager);
+
+ return builder.build();
+ }
+
+ private Response completeHeaders(Interceptor.Chain chain) throws IOException {
+ Request.Builder newRequest = chain.request().newBuilder();
+ if (userAgent != null) {
+ newRequest.header("User-Agent", userAgent);
+ }
+ if (proxyLogin != null) {
+ newRequest.header("Proxy-Authorization", Credentials.basic(proxyLogin, nullToEmpty(proxyPassword)));
+ }
+ return chain.proceed(newRequest.build());
+ }
+
+ private static X509TrustManager systemDefaultTrustManager() {
+ try {
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init((KeyStore) null);
+ TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+ if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
+ throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
+ }
+ return (X509TrustManager) trustManagers[0];
+ } catch (GeneralSecurityException e) {
+ // The system has no TLS. Just give up.
+ throw new AssertionError(e);
+ }
+ }
+
+ private static SSLSocketFactory systemDefaultSslSocketFactory(X509TrustManager trustManager) {
+ KeyManager[] defaultKeyManager;
+ try {
+ defaultKeyManager = getDefaultKeyManager();
+ } catch (Exception e) {
+ throw new IllegalStateException("Unable to get default key manager", e);
+ }
+ try {
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(defaultKeyManager, new TrustManager[] {trustManager}, null);
+ return sslContext.getSocketFactory();
+ } catch (GeneralSecurityException e) {
+ // The system has no TLS. Just give up.
+ throw new AssertionError(e);
+ }
+ }
+
+ private static void logDebug(String msg) {
+ boolean debugEnabled = "all".equals(System.getProperty("javax.net.debug"));
+ if (debugEnabled) {
+ System.out.println(msg);
+ }
+ }
+
+ /**
+ * Inspired from sun.security.ssl.SSLContextImpl#getDefaultKeyManager()
+ */
+ private static synchronized KeyManager[] getDefaultKeyManager() throws Exception {
+ final String defaultKeyStore = System.getProperty("javax.net.ssl.keyStore", "");
+ String defaultKeyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
+ String defaultKeyStoreProvider = System.getProperty("javax.net.ssl.keyStoreProvider", "");
+
+ logDebug("keyStore is : " + defaultKeyStore);
+ logDebug("keyStore type is : " + defaultKeyStoreType);
+ logDebug("keyStore provider is : " + defaultKeyStoreProvider);
+
+ if (P11KEYSTORE.equals(defaultKeyStoreType) && !NONE.equals(defaultKeyStore)) {
+ throw new IllegalArgumentException("if keyStoreType is " + P11KEYSTORE + ", then keyStore must be " + NONE);
+ }
+
+ KeyStore ks = null;
+ String defaultKeyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword", "");
+ char[] passwd = defaultKeyStorePassword.isEmpty() ? null : defaultKeyStorePassword.toCharArray();
+
+ // Try to initialize key store.
+ if (!defaultKeyStoreType.isEmpty()) {
+ logDebug("init keystore");
+ if (defaultKeyStoreProvider.isEmpty()) {
+ ks = KeyStore.getInstance(defaultKeyStoreType);
+ } else {
+ ks = KeyStore.getInstance(defaultKeyStoreType, defaultKeyStoreProvider);
+ }
+ if (!defaultKeyStore.isEmpty() && !NONE.equals(defaultKeyStore)) {
+ try (FileInputStream fs = new FileInputStream(defaultKeyStore)) {
+ ks.load(fs, passwd);
+ }
+ } else {
+ ks.load(null, passwd);
+ }
+ }
+
+ // Try to initialize key manager.
+ logDebug("init keymanager of type " + KeyManagerFactory.getDefaultAlgorithm());
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+
+ if (P11KEYSTORE.equals(defaultKeyStoreType)) {
+ // do not pass key passwd if using token
+ kmf.init(ks, null);
+ } else {
+ kmf.init(ks, passwd);
+ }
+
+ return kmf.getKeyManagers();
+ }
+}
@Rule
public ExpectedException expectedException = ExpectedException.none();
- MockWebServer server;
- String serverUrl;
-
- HttpConnector underTest;
+ private MockWebServer server;
+ private String serverUrl;
+ private HttpConnector underTest;
@Before
public void setUp() throws Exception {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.sonarqube.ws.client;
+
+import okhttp3.OkHttpClient;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OkHttpClientBuilderTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private OkHttpClientBuilder underTest = new OkHttpClientBuilder();
+
+ @Test
+ public void build_default_instance_of_OkHttpClient() {
+ OkHttpClient okHttpClient = underTest.build();
+
+ assertThat(okHttpClient.proxy()).isNull();
+ assertThat(okHttpClient.interceptors()).hasSize(1);
+ assertThat(okHttpClient.sslSocketFactory()).isNotNull();
+ }
+
+ @Test
+ public void build_throws_IAE_if_connect_timeout_is_negative() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Connect timeout must be positive. Got -10");
+
+ underTest.setConnectTimeoutMs(-10);
+ }
+
+ @Test
+ public void build_throws_IAE_if_read_timeout_is_negative() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Read timeout must be positive. Got -10");
+
+ underTest.setReadTimeoutMs(-10);
+ }
+}