Skip to content

Commit 0ec4364

Browse files
SLVS-1840 Provide system proxy settings to SonarQubeClient (#6040)
1 parent 402f459 commit 0ec4364

File tree

41 files changed

+385
-512
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+385
-512
lines changed

src/Integration/MefServices/MefSonarQubeService.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,14 @@ namespace SonarLint.VisualStudio.Integration.MefServices;
3737
[method: ImportingConstructor]
3838
public sealed class MefSonarQubeService(IUserAgentProvider userAgentProvider, ILogger logger, IThreadHandling threadHandling)
3939
: SonarQubeService(
40-
new HttpClientHandler(),
4140
userAgent: userAgentProvider.UserAgent,
4241
logger: new LoggerAdapter(logger))
4342
{
4443
protected override async Task<TResponse> InvokeUncheckedRequestAsync<TRequest, TResponse>(Action<TRequest> configure, HttpClient httpClient, CancellationToken token)
4544
{
4645
CodeMarkers.Instance.WebClientCallStart(typeof(TRequest).Name);
4746

48-
var result = await threadHandling.RunOnBackgroundThread( () => base.InvokeUncheckedRequestAsync<TRequest, TResponse>(configure, httpClient, token));
47+
var result = await threadHandling.RunOnBackgroundThread(() => base.InvokeUncheckedRequestAsync<TRequest, TResponse>(configure, httpClient, token));
4948

5049
CodeMarkers.Instance.WebClientCallStop(typeof(TRequest).Name);
5150

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* SonarLint for Visual Studio
3+
* Copyright (C) 2016-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
21+
using System.Net;
22+
using SonarQube.Client.Logging;
23+
24+
namespace SonarQube.Client.Tests;
25+
26+
[TestClass]
27+
public class HttpClientHandlerFactoryTests
28+
{
29+
private ILogger logger;
30+
private IProxyDetector proxyDetector;
31+
private HttpClientHandlerFactory httpClientHandlerFactory;
32+
33+
[TestInitialize]
34+
public void TestInitialize()
35+
{
36+
logger = Substitute.For<ILogger>();
37+
proxyDetector = Substitute.For<IProxyDetector>();
38+
httpClientHandlerFactory = new HttpClientHandlerFactory(proxyDetector, logger);
39+
}
40+
41+
[TestMethod]
42+
public void SystemProxyConfigured_ConfiguresHttpClientHandler()
43+
{
44+
var baseAddress = new Uri("http://localhost");
45+
var proxyUri = new Uri("http://proxy");
46+
proxyDetector.GetProxyUri(baseAddress).Returns(proxyUri);
47+
48+
var httpClientHandler = httpClientHandlerFactory.Create(baseAddress);
49+
50+
httpClientHandler.Should().NotBeNull();
51+
var webProxy = httpClientHandler.Proxy as WebProxy;
52+
webProxy.Should().NotBeNull();
53+
webProxy.Address.Should().Be(proxyUri);
54+
httpClientHandler.UseProxy.Should().BeTrue();
55+
logger.Received(1).Debug($"System proxy detected and configured: {proxyUri}");
56+
}
57+
58+
[TestMethod]
59+
public void NoSystemProxyConfigured_DoesNotConfigureHttpClientHandler()
60+
{
61+
var baseAddress = new Uri("http://localhost");
62+
proxyDetector.GetProxyUri(baseAddress).Returns(baseAddress);
63+
64+
var httpClientHandler = httpClientHandlerFactory.Create(baseAddress);
65+
66+
httpClientHandler.Should().NotBeNull();
67+
httpClientHandler.Proxy.Should().BeNull();
68+
httpClientHandler.UseProxy.Should().BeTrue(); // default value is true
69+
logger.Received(1).Debug("No system proxy detected");
70+
}
71+
}

src/SonarQube.Client.Tests/Infra/MocksHelper.cs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,9 @@
1818
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
*/
2020

21-
using System;
22-
using System.Linq;
2321
using System.Net;
2422
using System.Net.Http;
2523
using System.Net.Http.Headers;
26-
using System.Threading;
27-
using System.Threading.Tasks;
2824
using Moq;
2925
using Moq.Protected;
3026

@@ -34,41 +30,41 @@ internal static class MocksHelper
3430
{
3531
public const string ValidBaseAddress = "http://localhost";
3632

37-
public const string EmptyGetIssuesResponse = @"{""total"":0,""p"":1,""ps"":10,""paging"":{""pageIndex"":1,""pageSize"":10,""total"":0},""effortTotal"":0,""debtTotal"":0,""issues"":[],""components"":[],""organizations"":[],""facets"":[]}";
33+
public const string EmptyGetIssuesResponse
34+
= @"{""total"":0,""p"":1,""ps"":10,""paging"":{""pageIndex"":1,""pageSize"":10,""total"":0},""effortTotal"":0,""debtTotal"":0,""issues"":[],""components"":[],""organizations"":[],""facets"":[]}";
3835

3936
/// <summary>
4037
/// Sets up the HTTP message handler mock to respond to any request string
4138
/// </summary>
42-
public static void SetupHttpRequest(Mock<HttpMessageHandler> messageHandlerMock, string response,
39+
public static void SetupHttpRequest(
40+
Mock<HttpClientHandler> messageHandlerMock,
41+
string response,
4342
HttpStatusCode statusCode = HttpStatusCode.OK)
4443
{
4544
messageHandlerMock.Protected()
4645
.Setup<Task<HttpResponseMessage>>("SendAsync",
4746
ItExpr.IsAny<HttpRequestMessage>(),
4847
ItExpr.IsAny<CancellationToken>())
49-
.Returns(Task.FromResult(new HttpResponseMessage
50-
{
51-
StatusCode = statusCode,
52-
Content = new StringContent(response)
53-
}));
48+
.Returns(Task.FromResult(new HttpResponseMessage { StatusCode = statusCode, Content = new StringContent(response) }));
5449
}
5550

5651
/// <summary>
5752
/// Sets up the HTTP message handler mock to reply to a specific request string
5853
/// </summary>
59-
public static void SetupHttpRequest(Mock<HttpMessageHandler> messageHandlerMock, string requestRelativePath, string response,
60-
HttpStatusCode statusCode = HttpStatusCode.OK, string basePath = ValidBaseAddress)
54+
public static void SetupHttpRequest(
55+
Mock<HttpClientHandler> messageHandlerMock,
56+
string requestRelativePath,
57+
string response,
58+
HttpStatusCode statusCode = HttpStatusCode.OK,
59+
string basePath = ValidBaseAddress)
6160
{
62-
var responseMessage = new HttpResponseMessage
63-
{
64-
StatusCode = statusCode,
65-
Content = new StringContent(response)
66-
};
61+
var responseMessage = new HttpResponseMessage { StatusCode = statusCode, Content = new StringContent(response) };
6762

6863
SetupHttpRequest(messageHandlerMock, requestRelativePath, responseMessage, basePath);
6964
}
7065

71-
public static void SetupHttpRequest(Mock<HttpMessageHandler> messageHandlerMock,
66+
public static void SetupHttpRequest(
67+
Mock<HttpClientHandler> messageHandlerMock,
7268
string requestRelativePath,
7369
HttpResponseMessage responseMessage,
7470
string basePath = ValidBaseAddress,
@@ -88,7 +84,7 @@ public static void SetupHttpRequest(Mock<HttpMessageHandler> messageHandlerMock,
8884
/// <summary>
8985
/// Returns the actual requests passed to the SendAsync method
9086
/// </summary>
91-
public static HttpRequestMessage[] GetSendAsyncRequests(this Mock<HttpMessageHandler> handler) =>
87+
public static HttpRequestMessage[] GetSendAsyncRequests(this Mock<HttpClientHandler> handler) =>
9288
handler.Invocations.Where(x => x.Method.Name == "SendAsync")
9389
.Select(x => (HttpRequestMessage)x.Arguments[0])
9490
.ToArray();

src/SonarQube.Client.Tests/Requests/Api/V7_20/GetExclusionsRequestTests.cs

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,7 @@
1818
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
*/
2020

21-
using System;
2221
using System.Net.Http;
23-
using System.Threading;
24-
using System.Threading.Tasks;
25-
using FluentAssertions;
26-
using Microsoft.VisualStudio.TestTools.UnitTesting;
2722
using Moq;
2823
using SonarQube.Client.Api.V7_20;
2924
using SonarQube.Client.Tests.Infra;
@@ -45,11 +40,8 @@ public async Task InvokeAsync_AllSettingAreMissing_ReturnsEmptyConfiguration(str
4540

4641
var testSubject = CreateTestSubject(projectKey);
4742

48-
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
49-
var httpClient = new HttpClient(handlerMock.Object)
50-
{
51-
BaseAddress = new Uri(ValidBaseAddress)
52-
};
43+
var handlerMock = new Mock<HttpClientHandler>(MockBehavior.Strict);
44+
var httpClient = new HttpClient(handlerMock.Object) { BaseAddress = new Uri(ValidBaseAddress) };
5345

5446
var request = $"api/settings/values?component={projectKey}&keys=sonar.exclusions%2Csonar.global.exclusions%2Csonar.inclusions";
5547

@@ -71,11 +63,8 @@ public async Task InvokeAsync_SomeMissingSetting_ReturnsDefinedProperties()
7163

7264
var testSubject = CreateTestSubject(projectKey);
7365

74-
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
75-
var httpClient = new HttpClient(handlerMock.Object)
76-
{
77-
BaseAddress = new Uri(ValidBaseAddress)
78-
};
66+
var handlerMock = new Mock<HttpClientHandler>(MockBehavior.Strict);
67+
var httpClient = new HttpClient(handlerMock.Object) { BaseAddress = new Uri(ValidBaseAddress) };
7968

8069
var request = $"api/settings/values?component={projectKey}&keys=sonar.exclusions%2Csonar.global.exclusions%2Csonar.inclusions";
8170
var response = @"{
@@ -106,11 +95,8 @@ public async Task InvokeAsync_ExistingSetting_ReturnsDefinedProperties()
10695

10796
var testSubject = CreateTestSubject(projectKey);
10897

109-
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
110-
var httpClient = new HttpClient(handlerMock.Object)
111-
{
112-
BaseAddress = new Uri(ValidBaseAddress)
113-
};
98+
var handlerMock = new Mock<HttpClientHandler>(MockBehavior.Strict);
99+
var httpClient = new HttpClient(handlerMock.Object) { BaseAddress = new Uri(ValidBaseAddress) };
114100

115101
var request = $"api/settings/values?component={projectKey}&keys=sonar.exclusions%2Csonar.global.exclusions%2Csonar.inclusions";
116102
var response = @"{
@@ -148,14 +134,9 @@ public async Task InvokeAsync_ExistingSetting_ReturnsDefinedProperties()
148134
result.Inclusions.Should().BeEquivalentTo("**/111");
149135
}
150136

151-
152137
private static GetExclusionsRequest CreateTestSubject(string projectKey)
153138
{
154-
var testSubject = new GetExclusionsRequest
155-
{
156-
Logger = new TestLogger(),
157-
ProjectKey = projectKey
158-
};
139+
var testSubject = new GetExclusionsRequest { Logger = new TestLogger(), ProjectKey = projectKey };
159140

160141
return testSubject;
161142
}

0 commit comments

Comments
 (0)