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;