mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-18 07:13:46 +01:00
Fix proxy headers handling (#60)
* Fix proxy headers handling * Small refactor and moved proxy tests to new test-file.
This commit is contained in:
committed by
Stef Heyenrath
parent
d134684bcb
commit
c8c9ab99c5
@@ -2,17 +2,24 @@
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Http
|
||||
{
|
||||
internal static class HttpClientHelper
|
||||
{
|
||||
private static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
|
||||
public static HttpClient CreateHttpClient(string clientX509Certificate2ThumbprintOrSubjectName = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
|
||||
HttpClientHandler handler;
|
||||
|
||||
if (string.IsNullOrEmpty(clientX509Certificate2ThumbprintOrSubjectName))
|
||||
{
|
||||
handler = new HttpClientHandler();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETSTANDARD || NET46
|
||||
var handler = new HttpClientHandler
|
||||
handler = new HttpClientHandler
|
||||
{
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual,
|
||||
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
|
||||
@@ -22,7 +29,8 @@ namespace WireMock.Http
|
||||
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
handler.ClientCertificates.Add(x509Certificate2);
|
||||
#else
|
||||
var handler = new WebRequestHandler
|
||||
|
||||
var webRequestHandler = new WebRequestHandler
|
||||
{
|
||||
ClientCertificateOptions = ClientCertificateOption.Manual,
|
||||
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
|
||||
@@ -30,38 +38,51 @@ namespace WireMock.Http
|
||||
};
|
||||
|
||||
var x509Certificate2 = CertificateUtil.GetCertificate(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
handler.ClientCertificates.Add(x509Certificate2);
|
||||
return new HttpClient(handler);
|
||||
webRequestHandler.ClientCertificates.Add(x509Certificate2);
|
||||
handler = webRequestHandler;
|
||||
#endif
|
||||
}
|
||||
|
||||
return new HttpClient();
|
||||
// For proxy we shouldn't follow auto redirects
|
||||
handler.AllowAutoRedirect = false;
|
||||
|
||||
// If UseCookies enabled, httpClient ignores Cookie header
|
||||
handler.UseCookies = false;
|
||||
|
||||
return new HttpClient(handler);
|
||||
}
|
||||
|
||||
public static async Task<ResponseMessage> SendAsync(RequestMessage requestMessage, string url, string clientX509Certificate2ThumbprintOrSubjectName = null)
|
||||
public static async Task<ResponseMessage> SendAsync(HttpClient client, RequestMessage requestMessage, string url)
|
||||
{
|
||||
var client = CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
Check.NotNull(client, nameof(client));
|
||||
|
||||
var originalUri = new Uri(requestMessage.Url);
|
||||
var requiredUri = new Uri(url);
|
||||
|
||||
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
|
||||
|
||||
// Overwrite the host header
|
||||
httpRequestMessage.Headers.Host = new Uri(url).Authority;
|
||||
|
||||
// Set headers if present
|
||||
if (requestMessage.Headers != null)
|
||||
{
|
||||
foreach (var headerName in requestMessage.Headers.Keys.Where(k => k.ToUpper() != "HOST"))
|
||||
{
|
||||
httpRequestMessage.Headers.TryAddWithoutValidation(headerName, requestMessage.Headers[headerName]);
|
||||
}
|
||||
}
|
||||
|
||||
// Set Body if present
|
||||
if (requestMessage.BodyAsBytes != null && requestMessage.BodyAsBytes.Length > 0)
|
||||
{
|
||||
httpRequestMessage.Content = new ByteArrayContent(requestMessage.BodyAsBytes);
|
||||
}
|
||||
|
||||
// Overwrite the host header
|
||||
httpRequestMessage.Headers.Host = requiredUri.Authority;
|
||||
|
||||
// Set headers if present
|
||||
if (requestMessage.Headers != null)
|
||||
{
|
||||
foreach (var header in requestMessage.Headers.Where(header => !string.Equals(header.Key, "HOST", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// Try to add to request headers. If failed - try to add to content headers
|
||||
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
|
||||
{
|
||||
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the URL
|
||||
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);
|
||||
|
||||
@@ -74,9 +95,24 @@ namespace WireMock.Http
|
||||
Body = await httpResponseMessage.Content.ReadAsStringAsync()
|
||||
};
|
||||
|
||||
foreach (var header in httpResponseMessage.Headers)
|
||||
// Set both content and response headers, replacing URLs in values
|
||||
var headers = httpResponseMessage.Content?.Headers.Union(httpResponseMessage.Headers);
|
||||
|
||||
foreach (var header in headers)
|
||||
{
|
||||
responseMessage.AddHeader(header.Key, header.Value.FirstOrDefault());
|
||||
// if Location header contains absolute redirect URL, and base URL is one that we proxy to,
|
||||
// we need to replace it to original one.
|
||||
if (string.Equals(header.Key, "Location", StringComparison.OrdinalIgnoreCase)
|
||||
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out Uri absoluteLocationUri)
|
||||
&& string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var replacedLocationUri = new Uri(originalUri, absoluteLocationUri.PathAndQuery);
|
||||
responseMessage.AddHeader(header.Key, replacedLocationUri.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
responseMessage.AddHeader(header.Key, header.Value.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
return responseMessage;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
@@ -19,6 +20,8 @@ namespace WireMock.ResponseBuilders
|
||||
/// </summary>
|
||||
public class Response : IResponseBuilder
|
||||
{
|
||||
private HttpClient httpClientForProxy;
|
||||
|
||||
/// <summary>
|
||||
/// The delay
|
||||
/// </summary>
|
||||
@@ -317,6 +320,7 @@ namespace WireMock.ResponseBuilders
|
||||
|
||||
ProxyUrl = proxyUrl;
|
||||
X509Certificate2ThumbprintOrSubjectName = clientX509Certificate2ThumbprintOrSubjectName;
|
||||
httpClientForProxy = HttpClientHelper.CreateHttpClient(clientX509Certificate2ThumbprintOrSubjectName);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -332,12 +336,12 @@ namespace WireMock.ResponseBuilders
|
||||
if (Delay != null)
|
||||
await Task.Delay(Delay.Value);
|
||||
|
||||
if (ProxyUrl != null)
|
||||
if (ProxyUrl != null && httpClientForProxy != null)
|
||||
{
|
||||
var requestUri = new Uri(requestMessage.Url);
|
||||
var proxyUri = new Uri(ProxyUrl);
|
||||
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
|
||||
return await HttpClientHelper.SendAsync(requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri, X509Certificate2ThumbprintOrSubjectName);
|
||||
return await HttpClientHelper.SendAsync(httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri);
|
||||
}
|
||||
|
||||
if (UseTransformer)
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
@@ -129,8 +130,11 @@ namespace WireMock.Server
|
||||
}
|
||||
|
||||
#region Proxy and Record
|
||||
private HttpClient httpClientForProxy;
|
||||
|
||||
private void InitProxyAndRecord(ProxyAndRecordSettings settings)
|
||||
{
|
||||
httpClientForProxy = HttpClientHelper.CreateHttpClient(settings.X509Certificate2ThumbprintOrSubjectName);
|
||||
Given(Request.Create().WithPath("/*").UsingAnyVerb()).RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
|
||||
}
|
||||
|
||||
@@ -140,7 +144,7 @@ namespace WireMock.Server
|
||||
var proxyUri = new Uri(settings.Url);
|
||||
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
|
||||
|
||||
var responseMessage = await HttpClientHelper.SendAsync(requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri, settings.X509Certificate2ThumbprintOrSubjectName);
|
||||
var responseMessage = await HttpClientHelper.SendAsync(httpClientForProxy, requestMessage, proxyUriWithRequestPathAndQuery.AbsoluteUri);
|
||||
|
||||
if (settings.SaveMapping)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user