Fix proxy headers handling (#60)

* Fix proxy headers handling

* Small refactor and moved proxy tests to new test-file.
This commit is contained in:
Oleksandr Liakhevych
2017-10-28 19:59:34 +03:00
committed by Stef Heyenrath
parent d134684bcb
commit c8c9ab99c5
9 changed files with 248 additions and 134 deletions

View File

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

View File

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

View File

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