Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WebProxySettings (use when proxying requests) #370

Merged
merged 16 commits into from
Dec 7, 2019
1 change: 1 addition & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ steps:
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'

# https://github.com/NuGet/Home/issues/8148
- task: DotNetCoreCLI@2
displayName: Push to MyGet
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
Expand Down
18 changes: 18 additions & 0 deletions examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,24 @@ public static void Run()
.WithProxy(new ProxyAndRecordSettings { Url = "http://postman-echo.com/get" })
);

server
.Given(Request.Create()
.UsingGet()
.WithHeader("postmanecho", "get2")
)
.RespondWith(Response.Create()
.WithProxy(new ProxyAndRecordSettings
{
Url = "http://postman-echo.com/get",
WebProxySettings = new WebProxySettings
{
Address = "http://company",
UserName = "test",
Password = "pwd"
}
})
);

server
.Given(Request.Create()
.UsingGet()
Expand Down
17 changes: 14 additions & 3 deletions src/WireMock.Net.StandAlone/StandAloneApp.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using JetBrains.Annotations;
using System.Linq;
using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
Expand Down Expand Up @@ -87,8 +86,20 @@ public static FluentMockServer Start([NotNull] string[] args, [CanBeNull] IWireM
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern"),
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
BlackListedHeaders = parser.GetValues("BlackListedHeaders"),
BlackListedCookies = parser.GetValues("BlackListedCookies")
BlackListedCookies = parser.GetValues("BlackListedCookies"),
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect")
};

string proxyAddress = parser.GetStringValue("WebProxyAddress");
if (!string.IsNullOrEmpty(proxyAddress))
{
settings.ProxyAndRecordSettings.WebProxySettings = new WebProxySettings
{
Address = proxyAddress,
UserName = parser.GetStringValue("WebProxyUserName"),
Password = parser.GetStringValue("WebProxyPassword")
};
}
}

settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
Expand Down
5 changes: 5 additions & 0 deletions src/WireMock.Net/Admin/Mappings/ResponseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,10 @@ public class ResponseModel
/// Gets or sets the fault.
/// </summary>
public FaultModel Fault { get; set; }

/// <summary>
/// Gets or sets the WebProxy settings.
/// </summary>
public WebProxyModel WebProxy { get; set; }
}
}
23 changes: 23 additions & 0 deletions src/WireMock.Net/Admin/Mappings/WebProxyModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace WireMock.Admin.Mappings
{
/// <summary>
/// WebProxy settings
/// </summary>
public class WebProxyModel
{
/// <summary>
/// A string instance that contains the address of the proxy server.
/// </summary>
public string Address { get; set; }

/// <summary>
/// The user name associated with the credentials.
/// </summary>
public string UserName { get; set; }

/// <summary>
/// The password for the user name associated with the credentials.
/// </summary>
public string Password { get; set; }
}
}
28 changes: 28 additions & 0 deletions src/WireMock.Net/Compatibility/WebProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#if NETSTANDARD1_3
using System;
using System.Net;

namespace System.Net
{
internal class WebProxy : IWebProxy
{
private readonly string _proxy;
public ICredentials Credentials { get; set; }

public WebProxy(string proxy)
{
_proxy = proxy;
}

public Uri GetProxy(Uri destination)
{
return new Uri(_proxy);
}

public bool IsBypassed(Uri host)
{
return true;
}
}
}
#endif
25 changes: 18 additions & 7 deletions src/WireMock.Net/Http/HttpClientHelper.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
using System;
using JetBrains.Annotations;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.HttpsCertificate;
using WireMock.Settings;
using WireMock.Validation;

namespace WireMock.Http
{
internal static class HttpClientHelper
{
public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
public static HttpClient CreateHttpClient(IProxyAndRecordSettings settings)
{
#if NETSTANDARD
var handler = new HttpClientHandler
Expand All @@ -36,20 +37,30 @@ public static HttpClient CreateHttpClient(string clientX509Certificate2Thumbprin
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
#endif

if (!string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
if (!string.IsNullOrEmpty(settings.ClientX509Certificate2ThumbprintOrSubjectName))
{
handler.ClientCertificateOptions = ClientCertificateOption.Manual;

var x509Certificate2 = ClientCertificateHelper.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
var x509Certificate2 = ClientCertificateHelper.GetCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
}

// For proxy we shouldn't follow auto redirects
handler.AllowAutoRedirect = false;
handler.AllowAutoRedirect = settings.AllowAutoRedirect == true;

// If UseCookies enabled, httpClient ignores Cookie header
handler.UseCookies = false;

if (settings.WebProxySettings != null)
{
handler.UseProxy = true;

handler.Proxy = new WebProxy(settings.WebProxySettings.Address);
if (settings.WebProxySettings.UserName != null && settings.WebProxySettings.Password != null)
{
handler.Proxy.Credentials = new NetworkCredential(settings.WebProxySettings.UserName, settings.WebProxySettings.Password);
}
}

var client = new HttpClient(handler);
#if NET452 || NET46
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
Expand Down
48 changes: 48 additions & 0 deletions src/WireMock.Net/ResponseBuilders/Response.WithProxy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Net.Http;
using WireMock.Http;
using WireMock.Settings;
using WireMock.Validation;

namespace WireMock.ResponseBuilders
{
public partial class Response
{
private HttpClient _httpClientForProxy;

/// <summary>
/// The Proxy URL to use.
/// </summary>
public string ProxyUrl { get; private set; }

/// <summary>
/// The WebProxy settings.
/// </summary>
public IWebProxySettings WebProxySettings { get; private set; }

/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(string, string)"/>
public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
{
Check.NotNullOrEmpty(proxyUrl, nameof(proxyUrl));

var settings = new ProxyAndRecordSettings
{
Url = proxyUrl,
ClientX509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName
};

return WithProxy(settings);
}

/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(IProxyAndRecordSettings)"/>
public IResponseBuilder WithProxy(IProxyAndRecordSettings settings)
{
Check.NotNull(settings, nameof(settings));

ProxyUrl = settings.Url;
WebProxySettings = settings.WebProxySettings;

_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings);
return this;
}
}
}
31 changes: 0 additions & 31 deletions src/WireMock.Net/ResponseBuilders/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ namespace WireMock.ResponseBuilders
/// </summary>
public partial class Response : IResponseBuilder
{
private HttpClient _httpClientForProxy;

/// <summary>
/// The delay
/// </summary>
Expand All @@ -38,16 +36,6 @@ public partial class Response : IResponseBuilder
/// </summary>
public bool UseTransformerForBodyAsFile { get; private set; }

/// <summary>
/// The Proxy URL to use.
/// </summary>
public string ProxyUrl { get; private set; }

/// <summary>
/// The client X509Certificate2 Thumbprint or SubjectName to use.
/// </summary>
public string ClientX509Certificate2ThumbprintOrSubjectName { get; private set; }

/// <summary>
/// Gets the response message.
/// </summary>
Expand Down Expand Up @@ -339,25 +327,6 @@ public IResponseBuilder WithDelay(int milliseconds)
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
}

/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(string, string)"/>
public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
{
Check.NotNullOrEmpty(proxyUrl, nameof(proxyUrl));

ProxyUrl = proxyUrl;
ClientX509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
_httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
return this;
}

/// <inheritdoc cref="IProxyResponseBuilder.WithProxy(IProxyAndRecordSettings)"/>
public IResponseBuilder WithProxy(IProxyAndRecordSettings settings)
{
Check.NotNull(settings, nameof(settings));

return WithProxy(settings.Url, settings.ClientX509Certificate2ThumbprintOrSubjectName);
}

/// <inheritdoc cref="ICallbackResponseBuilder.WithCallback"/>
public IResponseBuilder WithCallback(Func<RequestMessage, ResponseMessage> callbackHandler)
{
Expand Down
22 changes: 18 additions & 4 deletions src/WireMock.Net/Serialization/MappingConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using WireMock.Validation;

Expand Down Expand Up @@ -116,12 +117,14 @@ public MappingModel ToMappingModel(IMapping mapping)
mappingModel.Response.BodyEncoding = null;
mappingModel.Response.ProxyUrl = response.ProxyUrl;
mappingModel.Response.Fault = null;
mappingModel.Response.WebProxy = MapWebProxy(response.WebProxySettings);
}
else
{
mappingModel.Response.WebProxy = null;
mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination;
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
mappingModel.Response.Headers = Map(response.ResponseMessage.Headers);
mappingModel.Response.Headers = MapHeaders(response.ResponseMessage.Headers);
if (response.UseTransformer)
{
mappingModel.Response.UseTransformer = response.UseTransformer;
Expand Down Expand Up @@ -181,14 +184,25 @@ public MappingModel ToMappingModel(IMapping mapping)
return mappingModel;
}

private static IDictionary<string, object> Map(IDictionary<string, WireMockList<string>> dictionary)
private static WebProxyModel MapWebProxy(IWebProxySettings settings)
{
return settings != null ? new WebProxyModel
{
Address = settings.Address,
UserName = settings.UserName,
Password = settings.Password
} : null;
}

private static IDictionary<string, object> MapHeaders(IDictionary<string, WireMockList<string>> dictionary)
{
var newDictionary = new Dictionary<string, object>();

if (dictionary == null || dictionary.Count == 0)
{
return null;
return newDictionary;
}

var newDictionary = new Dictionary<string, object>();
foreach (var entry in dictionary)
{
object value = entry.Value.Count == 1 ? (object)entry.Value.ToString() : entry.Value;
Expand Down
17 changes: 12 additions & 5 deletions src/WireMock.Net/Server/FluentMockServer.Admin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ public bool ReadStaticMappingAndAddOrUpdate([NotNull] string path)

private void InitProxyAndRecord(IFluentMockServerSettings settings)
{
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.ProxyAndRecordSettings.ClientX509Certificate2ThumbprintOrSubjectName);
_httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.ProxyAndRecordSettings);

var respondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod());
if (settings.StartAdminInterface == true)
Expand Down Expand Up @@ -790,12 +790,19 @@ private IResponseBuilder InitResponseBuilder(ResponseModel responseModel)

if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
{
if (string.IsNullOrEmpty(responseModel.X509Certificate2ThumbprintOrSubjectName))
var proxyAndRecordSettings = new ProxyAndRecordSettings
{
return responseBuilder.WithProxy(responseModel.ProxyUrl);
}
Url = responseModel.ProxyUrl,
ClientX509Certificate2ThumbprintOrSubjectName = responseModel.X509Certificate2ThumbprintOrSubjectName,
WebProxySettings = responseModel.WebProxy != null ? new WebProxySettings
{
Address = responseModel.WebProxy.Address,
UserName = responseModel.WebProxy.UserName,
Password = responseModel.WebProxy.Password
} : null
};

return responseBuilder.WithProxy(responseModel.ProxyUrl, responseModel.X509Certificate2ThumbprintOrSubjectName);
return responseBuilder.WithProxy(proxyAndRecordSettings);
}

if (responseModel.StatusCode.HasValue)
Expand Down
12 changes: 11 additions & 1 deletion src/WireMock.Net/Settings/IProxyAndRecordSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,15 @@ public interface IProxyAndRecordSettings
/// Defines a list of cookies which will excluded from the saved mappings.
/// </summary>
string[] BlackListedCookies { get; set; }

/// <summary>
/// Defines the WebProxySettings.
/// </summary>
IWebProxySettings WebProxySettings { get; set; }

/// <summary>
/// Proxy requests should follow redirection (30x).
/// </summary>
bool? AllowAutoRedirect { get; set; }
}
}
}
Loading