Skip to content
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ dotnet_diagnostic.CA1310.severity = none
# JSON002: Probable JSON string detected
dotnet_diagnostic.JSON002.severity = none

# S4830: Server certificates should be verified during SSL/TLS connection
dotnet_diagnostic.S4830.severity = none

[*.vb]
###############################
# VB Coding Conventions #
Expand Down
8 changes: 7 additions & 1 deletion APIMatic.Core.Test/Http/CoreHttpClientConfigurationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public void Builder_BuildWithPrameters_CoreHttpClientConfiguration()
{
// Arrange
var timeout = 180;
var skipSslCertVerification = true;
var numberOfRetries = 3;
var backoffFactor = 3;
var retryInterval = 4.5;
Expand All @@ -32,6 +33,7 @@ public void Builder_BuildWithPrameters_CoreHttpClientConfiguration()
// Act
var config = _config.ToBuilder()
.Timeout(TimeSpan.FromSeconds(timeout))
.SkipSslCertVerification(skipSslCertVerification)
.NumberOfRetries(numberOfRetries)
.BackoffFactor(backoffFactor)
.RetryInterval(retryInterval)
Expand All @@ -43,6 +45,7 @@ public void Builder_BuildWithPrameters_CoreHttpClientConfiguration()
// Assert
Assert.NotNull(config);
Assert.AreEqual(config.Timeout, TimeSpan.FromSeconds(timeout));
Assert.AreEqual(config.SkipSslCertVerification, skipSslCertVerification);
Assert.AreEqual(config.NumberOfRetries, numberOfRetries);
Assert.AreEqual(config.BackoffFactor, backoffFactor);
Assert.AreEqual(config.RetryInterval, retryInterval);
Expand All @@ -60,10 +63,12 @@ public void Builder_BuildWithInvalidPrameters_CoreHttpClientConfiguration()
var backoffFactor = 0;
var retryInterval = -1;
var maximumRetryWaitTime = -1;
var skipSslCertVerification = false;

var config = _config.ToBuilder()
.HttpClientInstance(null)
.Timeout(TimeSpan.FromSeconds(timeout))
.SkipSslCertVerification(skipSslCertVerification)
.NumberOfRetries(numberOfRetries)
.BackoffFactor(backoffFactor)
.RetryInterval(retryInterval)
Expand All @@ -83,6 +88,7 @@ public void Builder_BuildWithInvalidPrameters_CoreHttpClientConfiguration()
Assert.NotNull(config);
Assert.NotNull(config.HttpClientInstance);
Assert.AreEqual(config.Timeout, TimeSpan.FromSeconds(defaultTimeout));
Assert.AreEqual(config.SkipSslCertVerification, skipSslCertVerification);
Assert.AreEqual(config.NumberOfRetries, defaultNumberOfRetries);
Assert.AreEqual(config.BackoffFactor, defaultBackoffFactor);
Assert.AreEqual(config.RetryInterval, defaultRetryInterval);
Expand All @@ -98,7 +104,7 @@ public void ToString_Default_CoreHttpClientConfiguration()
var actual = _config.ToString();

// Assert
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 ";
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 ";
Assert.AreEqual(expected, actual);
}
}
Expand Down
71 changes: 71 additions & 0 deletions APIMatic.Core.Test/Http/HttpClientWrapperSSLTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using APIMatic.Core.Http.Configuration;
using NUnit.Framework;

namespace APIMatic.Core.Test.Http
{
[TestFixture]
public class HttpClientWrapperSSLTest : TestBase
{
private readonly string expiredSSLCertUrl = "https://expired.badssl.com/";

[Test]
public void TestHttpClientSSLCertificateVerification_ExceptionResponse()
{
var expectedValue = "The SSL connection could not be established, see inner exception.";
var clientConfiguration = new CoreHttpClientConfiguration.Builder()
.Build();

var config = new GlobalConfiguration.Builder()
.ServerUrls(new Dictionary<Enum, string>
{
{ MockServer.Server1, expiredSSLCertUrl },
}, MockServer.Server1)
.HttpConfiguration(clientConfiguration)
.ApiCallback(ApiCallBack)
.Build();

var client = config.HttpClient;

var request = config.GlobalRequestBuilder()
.Setup(HttpMethod.Get, string.Empty)
.Build();

// Act
var ex = Assert.ThrowsAsync<HttpRequestException>(() => client.ExecuteAsync(request));
Assert.AreEqual(expectedValue, ex.Message);
}

[Test]
public async Task TestHttpClientSkipSSLCertificateVerification_OKResponse()
{
var clientConfiguration = new CoreHttpClientConfiguration.Builder()
.SkipSslCertVerification(true)
.Build();

var config = new GlobalConfiguration.Builder()
.ServerUrls(new Dictionary<Enum, string>
{
{ MockServer.Server1, expiredSSLCertUrl },
}, MockServer.Server1)
.HttpConfiguration(clientConfiguration)
.ApiCallback(ApiCallBack)
.Build();

var client = config.HttpClient;

var request = config.GlobalRequestBuilder()
.Setup(HttpMethod.Get, string.Empty)
.Build();

// Act
var actual = await client.ExecuteAsync(request);

Assert.AreEqual(actual.StatusCode, (int)HttpStatusCode.OK);
}
}
}
53 changes: 50 additions & 3 deletions APIMatic.Core/Http/Configuration/CoreHttpClientConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class CoreHttpClientConfiguration : ICoreHttpClientConfiguration
/// </summary>
private CoreHttpClientConfiguration(
TimeSpan timeout,
bool skipSslCertVerification,
int numberOfRetries,
int backoffFactor,
double retryInterval,
Expand All @@ -30,6 +31,7 @@ private CoreHttpClientConfiguration(
bool overrideHttpClientConfiguration)
{
Timeout = timeout;
SkipSslCertVerification = skipSslCertVerification;
NumberOfRetries = numberOfRetries;
BackoffFactor = backoffFactor;
RetryInterval = retryInterval;
Expand All @@ -45,6 +47,11 @@ private CoreHttpClientConfiguration(
/// </summary>
public TimeSpan Timeout { get; }

/// <summary>
/// Gets Whether to skip verification of SSL certificates.
/// </summary>
public bool SkipSslCertVerification { get; }

/// <summary>
/// Gets Number of times the request is retried.
/// </summary>
Expand Down Expand Up @@ -90,6 +97,7 @@ public override string ToString()
{
return "HttpClientConfiguration: " +
$"{Timeout} , " +
$"{SkipSslCertVerification} , " +
$"{NumberOfRetries} , " +
$"{BackoffFactor} , " +
$"{RetryInterval} , " +
Expand All @@ -108,6 +116,7 @@ public Builder ToBuilder()
{
var builder = new Builder()
.Timeout(Timeout)
.SkipSslCertVerification(SkipSslCertVerification)
.NumberOfRetries(NumberOfRetries)
.BackoffFactor(BackoffFactor)
.RetryInterval(RetryInterval)
Expand All @@ -125,6 +134,7 @@ public Builder ToBuilder()
public class Builder
{
private TimeSpan timeout = TimeSpan.FromSeconds(100);
private bool skipSslCertVerification = false;
private int numberOfRetries = 0;
private int backoffFactor = 2;
private double retryInterval = 1;
Expand All @@ -137,7 +147,7 @@ public class Builder
{
"GET", "PUT"
}.Select(val => new HttpMethod(val)).ToImmutableList();
private HttpClient httpClientInstance = new HttpClient();
private HttpClient httpClientInstance = null;
private bool overrideHttpClientConfiguration = true;

/// <summary>
Expand All @@ -151,6 +161,17 @@ public Builder Timeout(TimeSpan timeout)
return this;
}

/// <summary>
/// Sets the SkipSslCertVerification.
/// </summary>
/// <param name="skipSslCertVerification">Bool for skipping (or not) SSL certificate verification</param>
/// <returns>Builder.</returns>
public Builder SkipSslCertVerification(bool skipSslCertVerification)
{
this.skipSslCertVerification = skipSslCertVerification;
return this;
}

/// <summary>
/// Sets the NumberOfRetries.
/// </summary>
Expand Down Expand Up @@ -225,7 +246,7 @@ public Builder RequestMethodsToRetry(IList<HttpMethod> requestMethodsToRetry)
/// <returns>Builder.</returns>
public Builder HttpClientInstance(HttpClient httpClientInstance, bool overrideHttpClientConfiguration = true)
{
this.httpClientInstance = httpClientInstance ?? new HttpClient();
this.httpClientInstance = httpClientInstance;
this.overrideHttpClientConfiguration = overrideHttpClientConfiguration;
return this;
}
Expand All @@ -238,15 +259,41 @@ public CoreHttpClientConfiguration Build()
{
return new CoreHttpClientConfiguration(
timeout,
skipSslCertVerification,
numberOfRetries,
backoffFactor,
retryInterval,
maximumRetryWaitTime,
statusCodesToRetry,
requestMethodsToRetry,
httpClientInstance,
GetInitializedHttpClientInstance(),
overrideHttpClientConfiguration);
}

private HttpClient GetInitializedHttpClientInstance()
{
if (overrideHttpClientConfiguration)
{
if (skipSslCertVerification)
{
var httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; }
};
return new HttpClient(httpClientHandler, disposeHandler: true)
{
Timeout = timeout,
};
}

var httpClient = httpClientInstance ?? new HttpClient();
httpClient.Timeout = timeout;

return httpClient;
}

return httpClientInstance ?? new HttpClient();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public interface ICoreHttpClientConfiguration
/// </summary>
TimeSpan Timeout { get; }

/// <summary>
/// Gets Whether to skip verification of SSL certificates.
/// </summary>
bool SkipSslCertVerification { get; }

/// <summary>
/// Number of times the request is retried.
/// </summary>
Expand Down
2 changes: 0 additions & 2 deletions APIMatic.Core/Http/HttpClientWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public HttpClientWrapper(ICoreHttpClientConfiguration httpClientConfig)
_backoffFactor = httpClientConfig.BackoffFactor;
_retryInterval = httpClientConfig.RetryInterval;
_maximumRetryWaitTime = httpClientConfig.MaximumRetryWaitTime;
_client.Timeout = httpClientConfig.Timeout;
}
}

Expand Down Expand Up @@ -292,7 +291,6 @@ private double GetExponentialWaitTime(int retryAttempt)
{
double noise = new Random().NextDouble() * 100;
return (1000 * _retryInterval * Math.Pow(_backoffFactor, retryAttempt - 1)) + noise;

}

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