mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-11 22:30:41 +01:00
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:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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}'")));
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
23
src/WireMock.Net/Admin/Mappings/WebProxyModel.cs
Normal file
23
src/WireMock.Net/Admin/Mappings/WebProxyModel.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
28
src/WireMock.Net/Compatibility/WebProxy.cs
Normal file
28
src/WireMock.Net/Compatibility/WebProxy.cs
Normal 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
|
||||
@@ -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;
|
||||
|
||||
48
src/WireMock.Net/ResponseBuilders/Response.WithProxy.cs
Normal file
48
src/WireMock.Net/ResponseBuilders/Response.WithProxy.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/WireMock.Net/Settings/IWebProxySettings.cs
Normal file
20
src/WireMock.Net/Settings/IWebProxySettings.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
19
src/WireMock.Net/Settings/WebProxySettings.cs
Normal file
19
src/WireMock.Net/Settings/WebProxySettings.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user