aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2024-04-12 18:52:43 +0200
committersonartech <sonartech@sonarsource.com>2024-04-15 20:02:44 +0000
commitbc04c220c0c81f240149e2ee6c5af7fff6fb6f54 (patch)
tree88ee6a45049231894463ba7df877264e1cf2ec2a /sonar-scanner-engine
parent94d11b4e7035ba66617247ebe027943eb37f914d (diff)
downloadsonarqube-bc04c220c0c81f240149e2ee6c5af7fff6fb6f54.tar.gz
sonarqube-bc04c220c0c81f240149e2ee6c5af7fff6fb6f54.zip
SONAR-22039 Support new timeout properties
Diffstat (limited to 'sonar-scanner-engine')
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java48
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java40
2 files changed, 77 insertions, 11 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java
index 76f67edbd49..2d8f7328cd4 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/ScannerWsClientProvider.java
@@ -21,6 +21,8 @@ package org.sonar.scanner.bootstrap;
import java.net.InetSocketAddress;
import java.net.Proxy;
+import java.time.Duration;
+import java.time.format.DateTimeParseException;
import org.sonar.api.CoreProperties;
import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.utils.System2;
@@ -37,11 +39,16 @@ import static org.sonar.core.config.ProxyProperties.HTTP_PROXY_PASSWORD;
import static org.sonar.core.config.ProxyProperties.HTTP_PROXY_USER;
public class ScannerWsClientProvider {
- static final int CONNECT_TIMEOUT_MS = 5_000;
+ static final int DEFAULT_CONNECT_TIMEOUT = 5;
+ static final int DEFAULT_RESPONSE_TIMEOUT = 0;
static final String READ_TIMEOUT_SEC_PROPERTY = "sonar.ws.timeout";
public static final String TOKEN_PROPERTY = "sonar.token";
private static final String TOKEN_ENV_VARIABLE = "SONAR_TOKEN";
static final int DEFAULT_READ_TIMEOUT_SEC = 60;
+ public static final String SONAR_SCANNER_PROXY_PORT = "sonar.scanner.proxyPort";
+ public static final String SONAR_SCANNER_CONNECT_TIMEOUT = "sonar.scanner.connectTimeout";
+ public static final String SONAR_SCANNER_SOCKET_TIMEOUT = "sonar.scanner.socketTimeout";
+ public static final String SONAR_SCANNER_RESPONSE_TIMEOUT = "sonar.scanner.responseTimeout";
@Bean("DefaultScannerWsClient")
public DefaultScannerWsClient provide(ScannerProperties scannerProps, EnvironmentInformation env, GlobalAnalysisMode globalMode,
@@ -49,13 +56,17 @@ public class ScannerWsClientProvider {
String url = defaultIfBlank(scannerProps.property("sonar.host.url"), "http://localhost:9000");
HttpConnector.Builder connectorBuilder = HttpConnector.newBuilder().acceptGzip(true);
- String timeoutSec = defaultIfBlank(scannerProps.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC));
+ String oldSocketTimeout = defaultIfBlank(scannerProps.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC));
+ String socketTimeout = defaultIfBlank(scannerProps.property(SONAR_SCANNER_SOCKET_TIMEOUT), oldSocketTimeout);
+ String connectTimeout = defaultIfBlank(scannerProps.property(SONAR_SCANNER_CONNECT_TIMEOUT), valueOf(DEFAULT_CONNECT_TIMEOUT));
+ String responseTimeout = defaultIfBlank(scannerProps.property(SONAR_SCANNER_RESPONSE_TIMEOUT), valueOf(DEFAULT_RESPONSE_TIMEOUT));
String envVarToken = defaultIfBlank(system.envVariable(TOKEN_ENV_VARIABLE), null);
String token = defaultIfBlank(scannerProps.property(TOKEN_PROPERTY), envVarToken);
String login = defaultIfBlank(scannerProps.property(CoreProperties.LOGIN), token);
connectorBuilder
- .readTimeoutMilliseconds(parseInt(timeoutSec) * 1_000)
- .connectTimeoutMilliseconds(CONNECT_TIMEOUT_MS)
+ .readTimeoutMilliseconds(parseDurationProperty(socketTimeout, SONAR_SCANNER_SOCKET_TIMEOUT))
+ .connectTimeoutMilliseconds(parseDurationProperty(connectTimeout, SONAR_SCANNER_CONNECT_TIMEOUT))
+ .responseTimeoutMilliseconds(parseDurationProperty(responseTimeout, SONAR_SCANNER_RESPONSE_TIMEOUT))
.userAgent(env.toString())
.url(url)
.credentials(login, scannerProps.property(CoreProperties.PASSWORD));
@@ -63,13 +74,8 @@ public class ScannerWsClientProvider {
// OkHttp detects 'http.proxyHost' java property already, so just focus on sonar properties
String proxyHost = defaultIfBlank(scannerProps.property("sonar.scanner.proxyHost"), null);
if (proxyHost != null) {
- int proxyPort;
- String proxyPortStr = defaultIfBlank(scannerProps.property("sonar.scanner.proxyPort"), url.startsWith("https") ? "443" : "80");
- try {
- proxyPort = parseInt(proxyPortStr);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("Invalid proxy port: " + proxyPortStr, e);
- }
+ String proxyPortStr = defaultIfBlank(scannerProps.property(SONAR_SCANNER_PROXY_PORT), url.startsWith("https") ? "443" : "80");
+ var proxyPort = parseIntProperty(proxyPortStr, SONAR_SCANNER_PROXY_PORT);
connectorBuilder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
}
@@ -83,4 +89,24 @@ public class ScannerWsClientProvider {
return new DefaultScannerWsClient(WsClientFactories.getDefault().newClient(connectorBuilder.build()), login != null, globalMode, analysisWarnings);
}
+
+ 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);
+ }
+ }
+
+ /**
+ * 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 int parseDurationProperty(String propValue, String propKey) {
+ try {
+ return (int) Duration.parse(propValue).toMillis();
+ } catch (DateTimeParseException e) {
+ return parseIntProperty(propValue, propKey) * 1_000;
+ }
+ }
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java
index 141a3af7670..2e63d4c0139 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/bootstrap/ScannerWsClientProviderTest.java
@@ -126,6 +126,46 @@ class ScannerWsClientProviderTest {
}
@Test
+ void it_should_timeout_on_long_response() {
+ scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
+ scannerProps.put("sonar.scanner.responseTimeout", "PT0.2S");
+
+ DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);
+
+ sonarqubeMock.stubFor(get("/api/plugins/installed")
+ .willReturn(aResponse().withStatus(200)
+ .withFixedDelay(2000)
+ .withBody("Success")));
+
+ HttpConnector httpConnector = (HttpConnector) client.wsConnector();
+
+ var getRequest = new GetRequest("api/plugins/installed");
+ var thrown = assertThrows(IllegalStateException.class, () -> httpConnector.call(getRequest));
+
+ assertThat(thrown).hasStackTraceContaining("timeout");
+ }
+
+ @Test
+ void it_should_timeout_on_slow_response() {
+ scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
+ scannerProps.put("sonar.scanner.socketTimeout", "PT0.2S");
+
+ DefaultScannerWsClient client = underTest.provide(new ScannerProperties(scannerProps), env, GLOBAL_ANALYSIS_MODE, system2, ANALYSIS_WARNINGS);
+
+ sonarqubeMock.stubFor(get("/api/plugins/installed")
+ .willReturn(aResponse().withStatus(200)
+ .withChunkedDribbleDelay(2, 2000)
+ .withBody("Success")));
+
+ HttpConnector httpConnector = (HttpConnector) client.wsConnector();
+
+ var getRequest = new GetRequest("api/plugins/installed");
+ var thrown = assertThrows(IllegalStateException.class, () -> httpConnector.call(getRequest));
+
+ assertThat(thrown).hasStackTraceContaining("timeout");
+ }
+
+ @Test
void it_should_honor_scanner_proxy_settings() {
scannerProps.put("sonar.host.url", sonarqubeMock.baseUrl());
scannerProps.put("sonar.scanner.proxyHost", "localhost");