#263 map all content specific headers to downstream request content property, make sure we dont map them to request specific headers, added a gzip encoding acceptance test (#267)

This commit is contained in:
Tom Pallister 2018-03-08 07:32:06 +00:00 committed by GitHub
parent db05935b89
commit 5e9ee1f2ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 286 additions and 14 deletions

View File

@ -47,11 +47,28 @@
var content = new ByteArrayContent(await ToByteArray(request.Body)); var content = new ByteArrayContent(await ToByteArray(request.Body));
content.Headers.TryAddWithoutValidation("Content-Type", new[] {request.ContentType}); content.Headers
.TryAddWithoutValidation("Content-Type", new[] {request.ContentType});
AddHeaderIfExistsOnRequest("Content-Language", content, request);
AddHeaderIfExistsOnRequest("Content-Location", content, request);
AddHeaderIfExistsOnRequest("Content-Range", content, request);
AddHeaderIfExistsOnRequest("Content-MD5", content, request);
AddHeaderIfExistsOnRequest("Content-Disposition", content, request);
AddHeaderIfExistsOnRequest("Content-Encoding", content, request);
return content; return content;
} }
private void AddHeaderIfExistsOnRequest(string key, HttpContent content, HttpRequest request)
{
if(request.Headers.ContainsKey(key))
{
content.Headers
.TryAddWithoutValidation(key, request.Headers[key].ToList());
}
}
private HttpMethod MapMethod(HttpRequest request) private HttpMethod MapMethod(HttpRequest request)
{ {
return new HttpMethod(request.Method); return new HttpMethod(request.Method);

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class GzipTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
public GzipTests()
{
_steps = new Steps();
}
[Fact]
public void should_return_response_200_with_simple_url()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
}
}
};
var input = "people";
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura", "\"people\""))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasGzipContent(input))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody, string expected)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
if(context.Request.Headers.TryGetValue("Content-Encoding", out var contentEncoding))
{
contentEncoding.First().ShouldBe("gzip");
string text = null;
using (var decompress = new GZipStream(context.Request.Body, CompressionMode.Decompress))
{
using (var sr = new StreamReader(decompress)) {
text = sr.ReadToEnd();
}
}
if(text != expected)
{
throw new Exception("not gzipped");
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
});
})
.Build();
_builder.Start();
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -22,6 +22,8 @@ using Ocelot.Middleware;
using Shouldly; using Shouldly;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
using Ocelot.AcceptanceTests.Caching; using Ocelot.AcceptanceTests.Caching;
using System.IO.Compression;
using System.Text;
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
@ -582,6 +584,22 @@ namespace Ocelot.AcceptanceTests
_postContent = new StringContent(postcontent); _postContent = new StringContent(postcontent);
} }
public void GivenThePostHasGzipContent(object input)
{
string json = JsonConvert.SerializeObject(input);
byte[] jsonBytes = Encoding.UTF8.GetBytes(json);
MemoryStream ms = new MemoryStream();
using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress, true))
{
gzip.Write(jsonBytes, 0, jsonBytes.Length);
}
ms.Position = 0;
StreamContent content = new StreamContent(ms);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentEncoding.Add("gzip");
_postContent = content;
}
public void ThenTheResponseBodyShouldBe(string expectedBody) public void ThenTheResponseBodyShouldBe(string expectedBody)
{ {
_response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody); _response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody);

View File

@ -15,6 +15,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Security.Cryptography;
public class RequestMapperTests public class RequestMapperTests
{ {
@ -118,19 +119,151 @@
} }
[Fact] [Fact]
public void Should_map_content_type_header() public void Should_handle_no_content()
{ {
this.Given(_ => GivenTheInputRequestHasNoContent())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy();
}
[Fact]
public void Should_map_content_headers()
{
byte[] md5bytes = new byte[0];
using (var md5 = MD5.Create())
{
md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5"));
}
this.Given(_ => GivenTheInputRequestHasContent("This is my content")) this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheContentTypeIs("application/json")) .And(_ => GivenTheContentTypeIs("application/json"))
.And(_ => GivenTheContentEncodingIs("gzip, compress"))
.And(_ => GivenTheContentLanguageIs("english"))
.And(_ => GivenTheContentLocationIs("/my-receipts/38"))
.And(_ => GivenTheContentRangeIs("bytes 1-2/*"))
.And(_ => GivenTheContentDispositionIs("inline"))
.And(_ => GivenTheContentMD5Is(md5bytes))
.And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri()) .And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned()) .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress"))
.And(_ => ThenTheMappedRequestHasContentLanguageHeader("english"))
.And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38"))
.And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes))
.And(_ => ThenTheMappedRequestHasContentRangeHeader())
.And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders())
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_not_add_content_headers()
{
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheContentTypeIs("application/json"))
.And(_ => GivenTheInputRequestHasMethod("POST"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheOtherContentTypeHeadersAreNotMapped())
.BDDfy();
}
private void ThenTheContentHeadersAreNotAddedToNonContentHeaders()
{
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentEncoding");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLocation");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Length");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type");
}
private void ThenTheOtherContentTypeHeadersAreNotMapped()
{
_mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull();
}
private void ThenTheMappedRequestHasContentDispositionHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected);
}
private void GivenTheContentDispositionIs(string input)
{
_inputRequest.Headers.Add("Content-Disposition", input);
}
private void ThenTheMappedRequestHasContentMD5Header(byte[] expected)
{
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected);
}
private void GivenTheContentMD5Is(byte[] input)
{
var base64 = Convert.ToBase64String(input);
_inputRequest.Headers.Add("Content-MD5", base64);
}
private void ThenTheMappedRequestHasContentRangeHeader()
{
_mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1);
_mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2);
}
private void GivenTheContentRangeIs(string input)
{
_inputRequest.Headers.Add("Content-Range", input);
}
private void ThenTheMappedRequestHasContentLocationHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected);
}
private void GivenTheContentLocationIs(string input)
{
_inputRequest.Headers.Add("Content-Location", input);
}
private void ThenTheMappedRequestHasContentLanguageHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected);
}
private void GivenTheContentLanguageIs(string input)
{
_inputRequest.Headers.Add("Content-Language", input);
}
private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo)
{
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected);
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo);
}
private void GivenTheContentEncodingIs(string input)
{
_inputRequest.Headers.Add("Content-Encoding", input);
}
private void GivenTheContentTypeIs(string contentType) private void GivenTheContentTypeIs(string contentType)
{ {
_inputRequest.ContentType = contentType; _inputRequest.ContentType = contentType;
@ -146,18 +279,6 @@
_mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected); _mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected);
} }
[Fact]
public void Should_handle_no_content()
{
this.Given(_ => GivenTheInputRequestHasNoContent())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy();
}
private void GivenTheInputRequestHasMethod(string method) private void GivenTheInputRequestHasMethod(string method)
{ {
_inputRequest.Method = method; _inputRequest.Method = method;