mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:42:50 +08:00
#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:
parent
db05935b89
commit
5e9ee1f2ed
@ -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);
|
||||||
|
116
test/Ocelot.AcceptanceTests/GzipTests.cs
Normal file
116
test/Ocelot.AcceptanceTests/GzipTests.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user