Skip to content

Commit dfff78b

Browse files
fix(http-client-wrapper): implement skipping SSL cert verification (#46)
Co-authored-by: Mohammad-Haris <[email protected]>
1 parent 95186f3 commit dfff78b

File tree

5 files changed

+131
-6
lines changed

5 files changed

+131
-6
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ dotnet_diagnostic.CA1310.severity = none
179179
# JSON002: Probable JSON string detected
180180
dotnet_diagnostic.JSON002.severity = none
181181

182+
# S4830: Server certificates should be verified during SSL/TLS connection
183+
dotnet_diagnostic.S4830.severity = none
184+
182185
[*.vb]
183186
###############################
184187
# VB Coding Conventions #

APIMatic.Core.Test/Http/CoreHttpClientConfigurationTest.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public void Builder_BuildWithPrameters_CoreHttpClientConfiguration()
2222
{
2323
// Arrange
2424
var timeout = 180;
25+
var skipSslCertVerification = true;
2526
var numberOfRetries = 3;
2627
var backoffFactor = 3;
2728
var retryInterval = 4.5;
@@ -32,6 +33,7 @@ public void Builder_BuildWithPrameters_CoreHttpClientConfiguration()
3233
// Act
3334
var config = _config.ToBuilder()
3435
.Timeout(TimeSpan.FromSeconds(timeout))
36+
.SkipSslCertVerification(skipSslCertVerification)
3537
.NumberOfRetries(numberOfRetries)
3638
.BackoffFactor(backoffFactor)
3739
.RetryInterval(retryInterval)
@@ -43,6 +45,7 @@ public void Builder_BuildWithPrameters_CoreHttpClientConfiguration()
4345
// Assert
4446
Assert.NotNull(config);
4547
Assert.AreEqual(config.Timeout, TimeSpan.FromSeconds(timeout));
48+
Assert.AreEqual(config.SkipSslCertVerification, skipSslCertVerification);
4649
Assert.AreEqual(config.NumberOfRetries, numberOfRetries);
4750
Assert.AreEqual(config.BackoffFactor, backoffFactor);
4851
Assert.AreEqual(config.RetryInterval, retryInterval);
@@ -60,10 +63,12 @@ public void Builder_BuildWithInvalidPrameters_CoreHttpClientConfiguration()
6063
var backoffFactor = 0;
6164
var retryInterval = -1;
6265
var maximumRetryWaitTime = -1;
66+
var skipSslCertVerification = false;
6367

6468
var config = _config.ToBuilder()
6569
.HttpClientInstance(null)
6670
.Timeout(TimeSpan.FromSeconds(timeout))
71+
.SkipSslCertVerification(skipSslCertVerification)
6772
.NumberOfRetries(numberOfRetries)
6873
.BackoffFactor(backoffFactor)
6974
.RetryInterval(retryInterval)
@@ -83,6 +88,7 @@ public void Builder_BuildWithInvalidPrameters_CoreHttpClientConfiguration()
8388
Assert.NotNull(config);
8489
Assert.NotNull(config.HttpClientInstance);
8590
Assert.AreEqual(config.Timeout, TimeSpan.FromSeconds(defaultTimeout));
91+
Assert.AreEqual(config.SkipSslCertVerification, skipSslCertVerification);
8692
Assert.AreEqual(config.NumberOfRetries, defaultNumberOfRetries);
8793
Assert.AreEqual(config.BackoffFactor, defaultBackoffFactor);
8894
Assert.AreEqual(config.RetryInterval, defaultRetryInterval);
@@ -98,7 +104,7 @@ public void ToString_Default_CoreHttpClientConfiguration()
98104
var actual = _config.ToString();
99105

100106
// Assert
101-
var expected = "HttpClientConfiguration: 00:01:40 , 0 , 2 , 1 , 00:02:00 , System.Collections.Immutable.ImmutableList`1[System.Int32] , System.Collections.Immutable.ImmutableList`1[System.Net.Http.HttpMethod] , System.Net.Http.HttpClient , True ";
107+
var expected = "HttpClientConfiguration: 00:01:40 , False , 0 , 2 , 1 , 00:02:00 , System.Collections.Immutable.ImmutableList`1[System.Int32] , System.Collections.Immutable.ImmutableList`1[System.Net.Http.HttpMethod] , System.Net.Http.HttpClient , True ";
102108
Assert.AreEqual(expected, actual);
103109
}
104110
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
using System.Net.Http;
5+
using System.Threading.Tasks;
6+
using APIMatic.Core.Http.Configuration;
7+
using NUnit.Framework;
8+
9+
namespace APIMatic.Core.Test.Http
10+
{
11+
[TestFixture]
12+
public class HttpClientWrapperSSLTest : TestBase
13+
{
14+
private readonly string expiredSSLCertUrl = "https://expired.badssl.com/";
15+
16+
[Test]
17+
public void TestHttpClientSSLCertificateVerification_ExceptionResponse()
18+
{
19+
var expectedValue = "The SSL connection could not be established, see inner exception.";
20+
var clientConfiguration = new CoreHttpClientConfiguration.Builder()
21+
.Build();
22+
23+
var config = new GlobalConfiguration.Builder()
24+
.ServerUrls(new Dictionary<Enum, string>
25+
{
26+
{ MockServer.Server1, expiredSSLCertUrl },
27+
}, MockServer.Server1)
28+
.HttpConfiguration(clientConfiguration)
29+
.ApiCallback(ApiCallBack)
30+
.Build();
31+
32+
var client = config.HttpClient;
33+
34+
var request = config.GlobalRequestBuilder()
35+
.Setup(HttpMethod.Get, string.Empty)
36+
.Build();
37+
38+
// Act
39+
var ex = Assert.ThrowsAsync<HttpRequestException>(() => client.ExecuteAsync(request));
40+
Assert.AreEqual(expectedValue, ex.Message);
41+
}
42+
43+
[Test]
44+
public async Task TestHttpClientSkipSSLCertificateVerification_OKResponse()
45+
{
46+
var clientConfiguration = new CoreHttpClientConfiguration.Builder()
47+
.SkipSslCertVerification(true)
48+
.Build();
49+
50+
var config = new GlobalConfiguration.Builder()
51+
.ServerUrls(new Dictionary<Enum, string>
52+
{
53+
{ MockServer.Server1, expiredSSLCertUrl },
54+
}, MockServer.Server1)
55+
.HttpConfiguration(clientConfiguration)
56+
.ApiCallback(ApiCallBack)
57+
.Build();
58+
59+
var client = config.HttpClient;
60+
61+
var request = config.GlobalRequestBuilder()
62+
.Setup(HttpMethod.Get, string.Empty)
63+
.Build();
64+
65+
// Act
66+
var actual = await client.ExecuteAsync(request);
67+
68+
Assert.AreEqual(actual.StatusCode, (int)HttpStatusCode.OK);
69+
}
70+
}
71+
}

APIMatic.Core/Http/Configuration/CoreHttpClientConfiguration.cs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class CoreHttpClientConfiguration : ICoreHttpClientConfiguration
2020
/// </summary>
2121
private CoreHttpClientConfiguration(
2222
TimeSpan timeout,
23+
bool skipSslCertVerification,
2324
int numberOfRetries,
2425
int backoffFactor,
2526
double retryInterval,
@@ -30,6 +31,7 @@ private CoreHttpClientConfiguration(
3031
bool overrideHttpClientConfiguration)
3132
{
3233
Timeout = timeout;
34+
SkipSslCertVerification = skipSslCertVerification;
3335
NumberOfRetries = numberOfRetries;
3436
BackoffFactor = backoffFactor;
3537
RetryInterval = retryInterval;
@@ -45,6 +47,11 @@ private CoreHttpClientConfiguration(
4547
/// </summary>
4648
public TimeSpan Timeout { get; }
4749

50+
/// <summary>
51+
/// Gets Whether to skip verification of SSL certificates.
52+
/// </summary>
53+
public bool SkipSslCertVerification { get; }
54+
4855
/// <summary>
4956
/// Gets Number of times the request is retried.
5057
/// </summary>
@@ -90,6 +97,7 @@ public override string ToString()
9097
{
9198
return "HttpClientConfiguration: " +
9299
$"{Timeout} , " +
100+
$"{SkipSslCertVerification} , " +
93101
$"{NumberOfRetries} , " +
94102
$"{BackoffFactor} , " +
95103
$"{RetryInterval} , " +
@@ -108,6 +116,7 @@ public Builder ToBuilder()
108116
{
109117
var builder = new Builder()
110118
.Timeout(Timeout)
119+
.SkipSslCertVerification(SkipSslCertVerification)
111120
.NumberOfRetries(NumberOfRetries)
112121
.BackoffFactor(BackoffFactor)
113122
.RetryInterval(RetryInterval)
@@ -125,6 +134,7 @@ public Builder ToBuilder()
125134
public class Builder
126135
{
127136
private TimeSpan timeout = TimeSpan.FromSeconds(100);
137+
private bool skipSslCertVerification = false;
128138
private int numberOfRetries = 0;
129139
private int backoffFactor = 2;
130140
private double retryInterval = 1;
@@ -137,7 +147,7 @@ public class Builder
137147
{
138148
"GET", "PUT"
139149
}.Select(val => new HttpMethod(val)).ToImmutableList();
140-
private HttpClient httpClientInstance = new HttpClient();
150+
private HttpClient httpClientInstance = null;
141151
private bool overrideHttpClientConfiguration = true;
142152

143153
/// <summary>
@@ -151,6 +161,17 @@ public Builder Timeout(TimeSpan timeout)
151161
return this;
152162
}
153163

164+
/// <summary>
165+
/// Sets the SkipSslCertVerification.
166+
/// </summary>
167+
/// <param name="skipSslCertVerification">Bool for skipping (or not) SSL certificate verification</param>
168+
/// <returns>Builder.</returns>
169+
public Builder SkipSslCertVerification(bool skipSslCertVerification)
170+
{
171+
this.skipSslCertVerification = skipSslCertVerification;
172+
return this;
173+
}
174+
154175
/// <summary>
155176
/// Sets the NumberOfRetries.
156177
/// </summary>
@@ -225,7 +246,7 @@ public Builder RequestMethodsToRetry(IList<HttpMethod> requestMethodsToRetry)
225246
/// <returns>Builder.</returns>
226247
public Builder HttpClientInstance(HttpClient httpClientInstance, bool overrideHttpClientConfiguration = true)
227248
{
228-
this.httpClientInstance = httpClientInstance ?? new HttpClient();
249+
this.httpClientInstance = httpClientInstance;
229250
this.overrideHttpClientConfiguration = overrideHttpClientConfiguration;
230251
return this;
231252
}
@@ -238,15 +259,41 @@ public CoreHttpClientConfiguration Build()
238259
{
239260
return new CoreHttpClientConfiguration(
240261
timeout,
262+
skipSslCertVerification,
241263
numberOfRetries,
242264
backoffFactor,
243265
retryInterval,
244266
maximumRetryWaitTime,
245267
statusCodesToRetry,
246268
requestMethodsToRetry,
247-
httpClientInstance,
269+
GetInitializedHttpClientInstance(),
248270
overrideHttpClientConfiguration);
249271
}
272+
273+
private HttpClient GetInitializedHttpClientInstance()
274+
{
275+
if (overrideHttpClientConfiguration)
276+
{
277+
if (skipSslCertVerification)
278+
{
279+
var httpClientHandler = new HttpClientHandler
280+
{
281+
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; }
282+
};
283+
return new HttpClient(httpClientHandler, disposeHandler: true)
284+
{
285+
Timeout = timeout,
286+
};
287+
}
288+
289+
var httpClient = httpClientInstance ?? new HttpClient();
290+
httpClient.Timeout = timeout;
291+
292+
return httpClient;
293+
}
294+
295+
return httpClientInstance ?? new HttpClient();
296+
}
250297
}
251298
}
252299
}

APIMatic.Core/Http/HttpClientWrapper.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ public HttpClientWrapper(ICoreHttpClientConfiguration httpClientConfig)
5858
_backoffFactor = httpClientConfig.BackoffFactor;
5959
_retryInterval = httpClientConfig.RetryInterval;
6060
_maximumRetryWaitTime = httpClientConfig.MaximumRetryWaitTime;
61-
_client.Timeout = httpClientConfig.Timeout;
6261
}
6362
}
6463

@@ -292,7 +291,6 @@ private double GetExponentialWaitTime(int retryAttempt)
292291
{
293292
double noise = new Random().NextDouble() * 100;
294293
return (1000 * _retryInterval * Math.Pow(_backoffFactor, retryAttempt - 1)) + noise;
295-
296294
}
297295

298296
private static Dictionary<string, string> GetCombinedResponseHeaders(HttpResponseMessage responseMessage)

0 commit comments

Comments
 (0)