Add WebProxySettings (use when proxying requests) (#370)

* webproxy part 1

* fixed

* Push to MyGet

* WebProxy standalone

* -n true

* nuget --- "-n true"

* AllowAutoRedirect

* .
This commit is contained in:
Stef Heyenrath
2019-12-07 08:52:04 +01:00
committed by GitHub
parent 8bafd6a1ba
commit 1b326a54d6
16 changed files with 266 additions and 51 deletions

View File

@@ -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

View File

@@ -105,6 +105,24 @@ namespace WireMock.Net.ConsoleApplication
.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()

View File

@@ -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 @@ namespace WireMock.Net.StandAlone
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}'")));

View File

@@ -96,5 +96,10 @@ namespace WireMock.Admin.Mappings
/// Gets or sets the fault.
/// </summary>
public FaultModel Fault { get; set; }
/// <summary>
/// Gets or sets the WebProxy settings.
/// </summary>
public WebProxyModel WebProxy { get; set; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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

View File

@@ -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 @@ namespace WireMock.Http
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;

View File

@@ -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;
}
}
}

View File

@@ -21,8 +21,6 @@ namespace WireMock.ResponseBuilders
/// </summary>
public partial class Response : IResponseBuilder
{
private HttpClient _httpClientForProxy;
/// <summary>
/// The delay
/// </summary>
@@ -38,16 +36,6 @@ namespace WireMock.ResponseBuilders
/// </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>
@@ -339,25 +327,6 @@ namespace WireMock.ResponseBuilders
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)
{

View File

@@ -4,6 +4,7 @@ using WireMock.Admin.Mappings;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Settings;
using WireMock.Util;
using WireMock.Validation;
@@ -116,12 +117,14 @@ namespace WireMock.Serialization
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 @@ namespace WireMock.Serialization
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;

View File

@@ -254,7 +254,7 @@ namespace WireMock.Server
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 @@ namespace WireMock.Server
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)

View File

@@ -44,5 +44,15 @@ namespace WireMock.Settings
/// 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; }
}
}
}

View File

@@ -0,0 +1,20 @@
namespace WireMock.Settings
{
public interface IWebProxySettings
{
/// <summary>
/// A string instance that contains the address of the proxy server.
/// </summary>
string Address { get; set; }
/// <summary>
/// The user name associated with the credentials.
/// </summary>
string UserName { get; set; }
/// <summary>
/// The password for the user name associated with the credentials.
/// </summary>
string Password { get; set; }
}
}

View File

@@ -34,5 +34,13 @@ namespace WireMock.Settings
/// <inheritdoc cref="IProxyAndRecordSettings.BlackListedCookies"/>
[PublicAPI]
public string[] BlackListedCookies { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.WebProxySettings"/>
[PublicAPI]
public IWebProxySettings WebProxySettings { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.AllowAutoRedirect"/>
[PublicAPI]
public bool? AllowAutoRedirect { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
using JetBrains.Annotations;
namespace WireMock.Settings
{
public class WebProxySettings : IWebProxySettings
{
/// <inheritdoc cref="IWebProxySettings.Address"/>
[PublicAPI]
public string Address { get; set; }
/// <inheritdoc cref="IWebProxySettings.UserName"/>
[PublicAPI]
public string UserName { get; set; }
/// <inheritdoc cref="IWebProxySettings.Password"/>
[PublicAPI]
public string Password { get; set; }
}
}

View File

@@ -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 @@ namespace WireMock.Net.Tests.ResponseBuilders
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<HttpRequestException>();
}
public void Dispose()
{
_server?.Dispose();