diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 9d28e613d..676c02f40 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -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
diff --git a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
index 294a1e6d0..55bf86f3f 100644
--- a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
+++ b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
@@ -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()
diff --git a/src/WireMock.Net.StandAlone/StandAloneApp.cs b/src/WireMock.Net.StandAlone/StandAloneApp.cs
index a1b22056c..21c597c6f 100644
--- a/src/WireMock.Net.StandAlone/StandAloneApp.cs
+++ b/src/WireMock.Net.StandAlone/StandAloneApp.cs
@@ -1,6 +1,5 @@
-using System;
+using JetBrains.Annotations;
using System.Linq;
-using JetBrains.Annotations;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
@@ -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}'")));
diff --git a/src/WireMock.Net/Admin/Mappings/ResponseModel.cs b/src/WireMock.Net/Admin/Mappings/ResponseModel.cs
index 158624f46..78ea85aab 100644
--- a/src/WireMock.Net/Admin/Mappings/ResponseModel.cs
+++ b/src/WireMock.Net/Admin/Mappings/ResponseModel.cs
@@ -96,5 +96,10 @@ public class ResponseModel
/// Gets or sets the fault.
///
public FaultModel Fault { get; set; }
+
+ ///
+ /// Gets or sets the WebProxy settings.
+ ///
+ public WebProxyModel WebProxy { get; set; }
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Admin/Mappings/WebProxyModel.cs b/src/WireMock.Net/Admin/Mappings/WebProxyModel.cs
new file mode 100644
index 000000000..a05591188
--- /dev/null
+++ b/src/WireMock.Net/Admin/Mappings/WebProxyModel.cs
@@ -0,0 +1,23 @@
+namespace WireMock.Admin.Mappings
+{
+ ///
+ /// WebProxy settings
+ ///
+ public class WebProxyModel
+ {
+ ///
+ /// A string instance that contains the address of the proxy server.
+ ///
+ public string Address { get; set; }
+
+ ///
+ /// The user name associated with the credentials.
+ ///
+ public string UserName { get; set; }
+
+ ///
+ /// The password for the user name associated with the credentials.
+ ///
+ public string Password { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Compatibility/WebProxy.cs b/src/WireMock.Net/Compatibility/WebProxy.cs
new file mode 100644
index 000000000..538c9c140
--- /dev/null
+++ b/src/WireMock.Net/Compatibility/WebProxy.cs
@@ -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
\ No newline at end of file
diff --git a/src/WireMock.Net/Http/HttpClientHelper.cs b/src/WireMock.Net/Http/HttpClientHelper.cs
index 4b5b05e33..296cdabc9 100644
--- a/src/WireMock.Net/Http/HttpClientHelper.cs
+++ b/src/WireMock.Net/Http/HttpClientHelper.cs
@@ -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
@@ -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;
diff --git a/src/WireMock.Net/ResponseBuilders/Response.WithProxy.cs b/src/WireMock.Net/ResponseBuilders/Response.WithProxy.cs
new file mode 100644
index 000000000..b43880cbc
--- /dev/null
+++ b/src/WireMock.Net/ResponseBuilders/Response.WithProxy.cs
@@ -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;
+
+ ///
+ /// The Proxy URL to use.
+ ///
+ public string ProxyUrl { get; private set; }
+
+ ///
+ /// The WebProxy settings.
+ ///
+ public IWebProxySettings WebProxySettings { get; private set; }
+
+ ///
+ public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
+ {
+ Check.NotNullOrEmpty(proxyUrl, nameof(proxyUrl));
+
+ var settings = new ProxyAndRecordSettings
+ {
+ Url = proxyUrl,
+ ClientX509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName
+ };
+
+ return WithProxy(settings);
+ }
+
+ ///
+ public IResponseBuilder WithProxy(IProxyAndRecordSettings settings)
+ {
+ Check.NotNull(settings, nameof(settings));
+
+ ProxyUrl = settings.Url;
+ WebProxySettings = settings.WebProxySettings;
+
+ _httpClientForProxy = HttpClientHelper.CreateHttpClient(settings);
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/ResponseBuilders/Response.cs b/src/WireMock.Net/ResponseBuilders/Response.cs
index 8d042251d..9d716286f 100644
--- a/src/WireMock.Net/ResponseBuilders/Response.cs
+++ b/src/WireMock.Net/ResponseBuilders/Response.cs
@@ -21,8 +21,6 @@ namespace WireMock.ResponseBuilders
///
public partial class Response : IResponseBuilder
{
- private HttpClient _httpClientForProxy;
-
///
/// The delay
///
@@ -38,16 +36,6 @@ public partial class Response : IResponseBuilder
///
public bool UseTransformerForBodyAsFile { get; private set; }
- ///
- /// The Proxy URL to use.
- ///
- public string ProxyUrl { get; private set; }
-
- ///
- /// The client X509Certificate2 Thumbprint or SubjectName to use.
- ///
- public string ClientX509Certificate2ThumbprintOrSubjectName { get; private set; }
-
///
/// Gets the response message.
///
@@ -339,25 +327,6 @@ public IResponseBuilder WithDelay(int milliseconds)
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
}
- ///
- public IResponseBuilder WithProxy(string proxyUrl, string clientX509Certificate2ThumbprintOrSubjectName = null)
- {
- Check.NotNullOrEmpty(proxyUrl, nameof(proxyUrl));
-
- ProxyUrl = proxyUrl;
- ClientX509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
- _httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
- return this;
- }
-
- ///
- public IResponseBuilder WithProxy(IProxyAndRecordSettings settings)
- {
- Check.NotNull(settings, nameof(settings));
-
- return WithProxy(settings.Url, settings.ClientX509Certificate2ThumbprintOrSubjectName);
- }
-
///
public IResponseBuilder WithCallback(Func callbackHandler)
{
diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs
index 144ecc67c..383d4135f 100644
--- a/src/WireMock.Net/Serialization/MappingConverter.cs
+++ b/src/WireMock.Net/Serialization/MappingConverter.cs
@@ -4,6 +4,7 @@
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
+using WireMock.Settings;
using WireMock.Util;
using WireMock.Validation;
@@ -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;
@@ -181,14 +184,25 @@ public MappingModel ToMappingModel(IMapping mapping)
return mappingModel;
}
- private static IDictionary Map(IDictionary> 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 MapHeaders(IDictionary> dictionary)
+ {
+ var newDictionary = new Dictionary();
+
if (dictionary == null || dictionary.Count == 0)
{
- return null;
+ return newDictionary;
}
- var newDictionary = new Dictionary();
foreach (var entry in dictionary)
{
object value = entry.Value.Count == 1 ? (object)entry.Value.ToString() : entry.Value;
diff --git a/src/WireMock.Net/Server/FluentMockServer.Admin.cs b/src/WireMock.Net/Server/FluentMockServer.Admin.cs
index 239006f39..94d94e198 100644
--- a/src/WireMock.Net/Server/FluentMockServer.Admin.cs
+++ b/src/WireMock.Net/Server/FluentMockServer.Admin.cs
@@ -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)
@@ -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)
diff --git a/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs
index 2e1735532..26b93b5df 100644
--- a/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs
+++ b/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs
@@ -44,5 +44,15 @@ public interface IProxyAndRecordSettings
/// Defines a list of cookies which will excluded from the saved mappings.
///
string[] BlackListedCookies { get; set; }
+
+ ///
+ /// Defines the WebProxySettings.
+ ///
+ IWebProxySettings WebProxySettings { get; set; }
+
+ ///
+ /// Proxy requests should follow redirection (30x).
+ ///
+ bool? AllowAutoRedirect { get; set; }
}
-}
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/IWebProxySettings.cs b/src/WireMock.Net/Settings/IWebProxySettings.cs
new file mode 100644
index 000000000..3cdbe172d
--- /dev/null
+++ b/src/WireMock.Net/Settings/IWebProxySettings.cs
@@ -0,0 +1,20 @@
+namespace WireMock.Settings
+{
+ public interface IWebProxySettings
+ {
+ ///
+ /// A string instance that contains the address of the proxy server.
+ ///
+ string Address { get; set; }
+
+ ///
+ /// The user name associated with the credentials.
+ ///
+ string UserName { get; set; }
+
+ ///
+ /// The password for the user name associated with the credentials.
+ ///
+ string Password { get; set; }
+ }
+}
diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
index 09c7a48e9..a4be77766 100644
--- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
+++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
@@ -34,5 +34,13 @@ public class ProxyAndRecordSettings : IProxyAndRecordSettings
///
[PublicAPI]
public string[] BlackListedCookies { get; set; }
+
+ ///
+ [PublicAPI]
+ public IWebProxySettings WebProxySettings { get; set; }
+
+ ///
+ [PublicAPI]
+ public bool? AllowAutoRedirect { get; set; }
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/WebProxySettings.cs b/src/WireMock.Net/Settings/WebProxySettings.cs
new file mode 100644
index 000000000..1de8b380c
--- /dev/null
+++ b/src/WireMock.Net/Settings/WebProxySettings.cs
@@ -0,0 +1,19 @@
+using JetBrains.Annotations;
+
+namespace WireMock.Settings
+{
+ public class WebProxySettings : IWebProxySettings
+ {
+ ///
+ [PublicAPI]
+ public string Address { get; set; }
+
+ ///
+ [PublicAPI]
+ public string UserName { get; set; }
+
+ ///
+ [PublicAPI]
+ public string Password { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithProxyTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithProxyTests.cs
index 733ed7528..d25b4680b 100644
--- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithProxyTests.cs
+++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithProxyTests.cs
@@ -2,6 +2,7 @@
using NFluent;
using System;
using System.Collections.Generic;
+using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Models;
using WireMock.RequestBuilders;
@@ -45,6 +46,28 @@ public async Task Response_WithProxy()
Check.That(responseMessage.Headers["Content-Type"].ToString()).IsEqualTo("application/json");
}
+ [Fact]
+ public void Response_WithProxy_WebProxySettings()
+ {
+ // Assign
+ var settings = new ProxyAndRecordSettings
+ {
+ Url = "http://test.nl",
+ WebProxySettings = new WebProxySettings
+ {
+ Address = "http://company",
+ UserName = "x",
+ Password = "y"
+ }
+ };
+ var response = Response.Create().WithProxy(settings);
+
+ // Act
+ var request = new RequestMessage(new UrlDetails($"{_server.Urls[0]}/{_guid}"), "GET", "::1");
+
+ Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settingsMock.Object)).Throws();
+ }
+
public void Dispose()
{
_server?.Dispose();