* #464 added code to request mapper to not automatically add content type and content length headers, .net will automatically try and add these headers in a few circumstances but this solves the 464 issue

* #464 use seek instead of read on body check for websockets tests

* #464 ran out of inodes on linux, looks like reloadonchange causes this
This commit is contained in:
Tom Pallister 2018-07-20 22:32:40 +01:00 committed by GitHub
parent 12ef3bc00f
commit 23c5fcbf91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1253 additions and 1034 deletions

View File

@ -22,7 +22,7 @@
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true) .AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json") .AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables(); .AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>

View File

@ -106,7 +106,7 @@ namespace OcelotGraphQL
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true) .AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json") .AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables(); .AddEnvironmentVariables();
}) })
.ConfigureServices(s => { .ConfigureServices(s => {

View File

@ -59,7 +59,6 @@ namespace OcelotApplicationApiGateway
{ {
this.webHost = new WebHostBuilder() this.webHost = new WebHostBuilder()
.UseKestrel() .UseKestrel()
//.UseStartup<Startup>()
.UseUrls(this.listeningAddress) .UseUrls(this.listeningAddress)
.ConfigureAppConfiguration((hostingContext, config) => .ConfigureAppConfiguration((hostingContext, config) =>
{ {
@ -67,7 +66,7 @@ namespace OcelotApplicationApiGateway
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true) .AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json") .AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables(); .AddEnvironmentVariables();
}) })
.ConfigureLogging((hostingContext, logging) => .ConfigureLogging((hostingContext, logging) =>

View File

@ -1,61 +1,61 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Infrastructure.Extensions; using Ocelot.Infrastructure.Extensions;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
namespace Ocelot.Authentication.Middleware namespace Ocelot.Authentication.Middleware
{ {
public class AuthenticationMiddleware : OcelotMiddleware public class AuthenticationMiddleware : OcelotMiddleware
{ {
private readonly OcelotRequestDelegate _next; private readonly OcelotRequestDelegate _next;
public AuthenticationMiddleware(OcelotRequestDelegate next, public AuthenticationMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory) IOcelotLoggerFactory loggerFactory)
: base(loggerFactory.CreateLogger<AuthenticationMiddleware>()) : base(loggerFactory.CreateLogger<AuthenticationMiddleware>())
{ {
_next = next; _next = next;
} }
public async Task Invoke(DownstreamContext context) public async Task Invoke(DownstreamContext context)
{ {
if (IsAuthenticatedRoute(context.DownstreamReRoute)) if (IsAuthenticatedRoute(context.DownstreamReRoute))
{ {
Logger.LogInformation($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated"); Logger.LogInformation($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey); var result = await context.HttpContext.AuthenticateAsync(context.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey);
context.HttpContext.User = result.Principal; context.HttpContext.User = result.Principal;
if (context.HttpContext.User.Identity.IsAuthenticated) if (context.HttpContext.User.Identity.IsAuthenticated)
{ {
Logger.LogInformation($"Client has been authenticated for {context.HttpContext.Request.Path}"); Logger.LogInformation($"Client has been authenticated for {context.HttpContext.Request.Path}");
await _next.Invoke(context); await _next.Invoke(context);
} }
else else
{ {
var error = new UnauthenticatedError( var error = new UnauthenticatedError(
$"Request for authenticated route {context.HttpContext.Request.Path} by {context.HttpContext.User.Identity.Name} was unauthenticated"); $"Request for authenticated route {context.HttpContext.Request.Path} by {context.HttpContext.User.Identity.Name} was unauthenticated");
Logger.LogWarning($"Client has NOT been authenticated for {context.HttpContext.Request.Path} and pipeline error set. {error}"); Logger.LogWarning($"Client has NOT been authenticated for {context.HttpContext.Request.Path} and pipeline error set. {error}");
SetPipelineError(context, error); SetPipelineError(context, error);
} }
} }
else else
{ {
Logger.LogInformation($"No authentication needed for {context.HttpContext.Request.Path}"); Logger.LogInformation($"No authentication needed for {context.HttpContext.Request.Path}");
await _next.Invoke(context); await _next.Invoke(context);
} }
} }
private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute) private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
{ {
return reRoute.IsAuthenticated; return reRoute.IsAuthenticated;
} }
} }
} }

View File

@ -71,7 +71,7 @@ namespace Ocelot.DependencyInjection
File.WriteAllText("ocelot.json", json); File.WriteAllText("ocelot.json", json);
builder.AddJsonFile("ocelot.json"); builder.AddJsonFile("ocelot.json", false, false);
return builder; return builder;
} }

View File

@ -1,12 +1,12 @@
namespace Ocelot.Request.Mapper namespace Ocelot.Request.Mapper
{ {
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Responses; using Ocelot.Responses;
public interface IRequestMapper public interface IRequestMapper
{ {
Task<Response<HttpRequestMessage>> Map(HttpRequest request); Response<HttpRequestMessage> Map(HttpRequest request);
} }
} }

View File

@ -1,108 +1,99 @@
namespace Ocelot.Request.Mapper namespace Ocelot.Request.Mapper
{ {
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Ocelot.Responses; using Ocelot.Responses;
public class RequestMapper : IRequestMapper public class RequestMapper : IRequestMapper
{ {
private readonly string[] _unsupportedHeaders = { "host" }; private readonly string[] _unsupportedHeaders = { "host" };
public async Task<Response<HttpRequestMessage>> Map(HttpRequest request) public Response<HttpRequestMessage> Map(HttpRequest request)
{ {
try try
{ {
var requestMessage = new HttpRequestMessage() var requestMessage = new HttpRequestMessage()
{ {
Content = await MapContent(request), Content = MapContent(request),
Method = MapMethod(request), Method = MapMethod(request),
RequestUri = MapUri(request) RequestUri = MapUri(request)
}; };
MapHeaders(request, requestMessage); MapHeaders(request, requestMessage);
return new OkResponse<HttpRequestMessage>(requestMessage); return new OkResponse<HttpRequestMessage>(requestMessage);
} }
catch (Exception ex) catch (Exception ex)
{ {
return new ErrorResponse<HttpRequestMessage>(new UnmappableRequestError(ex)); return new ErrorResponse<HttpRequestMessage>(new UnmappableRequestError(ex));
} }
} }
private async Task<HttpContent> MapContent(HttpRequest request) private HttpContent MapContent(HttpRequest request)
{ {
if (request.Body == null) if (request.Body == null || (request.Body.CanSeek && request.Body.Length <= 0))
{ {
return null; return null;
} }
var content = new ByteArrayContent(await ToByteArray(request.Body)); var content = new StreamContent(request.Body);
content.Headers if(!string.IsNullOrEmpty(request.ContentType))
.TryAddWithoutValidation("Content-Type", new[] {request.ContentType}); {
content.Headers
AddHeaderIfExistsOnRequest("Content-Language", content, request); .TryAddWithoutValidation("Content-Type", new[] {request.ContentType});
AddHeaderIfExistsOnRequest("Content-Location", content, request); }
AddHeaderIfExistsOnRequest("Content-Range", content, request);
AddHeaderIfExistsOnRequest("Content-MD5", content, request); AddHeaderIfExistsOnRequest("Content-Language", content, request);
AddHeaderIfExistsOnRequest("Content-Disposition", content, request); AddHeaderIfExistsOnRequest("Content-Location", content, request);
AddHeaderIfExistsOnRequest("Content-Encoding", content, request); AddHeaderIfExistsOnRequest("Content-Range", content, request);
AddHeaderIfExistsOnRequest("Content-MD5", content, request);
return content; AddHeaderIfExistsOnRequest("Content-Disposition", content, request);
} AddHeaderIfExistsOnRequest("Content-Encoding", content, request);
private void AddHeaderIfExistsOnRequest(string key, HttpContent content, HttpRequest request) return content;
{ }
if(request.Headers.ContainsKey(key))
{ private void AddHeaderIfExistsOnRequest(string key, HttpContent content, HttpRequest request)
content.Headers {
.TryAddWithoutValidation(key, request.Headers[key].ToList()); if(request.Headers.ContainsKey(key))
} {
} content.Headers
.TryAddWithoutValidation(key, request.Headers[key].ToList());
private HttpMethod MapMethod(HttpRequest request) }
{ }
return new HttpMethod(request.Method);
} private HttpMethod MapMethod(HttpRequest request)
{
private Uri MapUri(HttpRequest request) return new HttpMethod(request.Method);
{ }
return new Uri(request.GetEncodedUrl());
} private Uri MapUri(HttpRequest request)
{
private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage) return new Uri(request.GetEncodedUrl());
{ }
foreach (var header in request.Headers)
{ private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage)
if (IsSupportedHeader(header)) {
{ foreach (var header in request.Headers)
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); {
} if (IsSupportedHeader(header))
} {
} requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
private async Task<byte[]> ToByteArray(Stream stream) }
{ }
using (stream)
{ private bool IsSupportedHeader(KeyValuePair<string, StringValues> header)
using (var memStream = new MemoryStream()) {
{ return !_unsupportedHeaders.Contains(header.Key.ToLower());
await stream.CopyToAsync(memStream); }
return memStream.ToArray(); }
} }
}
}
private bool IsSupportedHeader(KeyValuePair<string, StringValues> header)
{
return !_unsupportedHeaders.Contains(header.Key.ToLower());
}
}
}

View File

@ -27,7 +27,7 @@ namespace Ocelot.Request.Middleware
public async Task Invoke(DownstreamContext context) public async Task Invoke(DownstreamContext context)
{ {
var downstreamRequest = await _requestMapper.Map(context.HttpContext.Request); var downstreamRequest = _requestMapper.Map(context.HttpContext.Request);
if (downstreamRequest.IsError) if (downstreamRequest.IsError)
{ {

View File

@ -0,0 +1,190 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
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 ContentTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
private string _contentType;
private long? _contentLength;
private bool _contentTypeHeaderExists;
public ContentTests()
{
_steps = new Steps();
}
[Fact]
public void should_not_add_content_type_or_content_length_headers()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51339,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51339", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => ThenTheContentTypeShouldBeEmpty())
.And(x => ThenTheContentLengthShouldBeEmpty())
.BDDfy();
}
[Fact]
public void should_add_content_type_and_content_length_headers()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51349,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
}
}
};
var contentType = "application/json";
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51349", "/", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent"))
.And(x => _steps.GivenThePostHasContentType(contentType))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.And(x => ThenTheContentLengthIs(11))
.And(x => ThenTheContentTypeIsIs(contentType))
.BDDfy();
}
[Fact]
public void should_add_default_content_type_header()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51359,
}
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51359", "/", 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.And(x => ThenTheContentLengthIs(11))
.And(x => ThenTheContentTypeIsIs("text/plain; charset=utf-8"))
.BDDfy();
}
private void ThenTheContentTypeIsIs(string expected)
{
_contentType.ShouldBe(expected);
}
private void ThenTheContentLengthShouldBeEmpty()
{
_contentLength.ShouldBeNull();
}
private void ThenTheContentLengthIs(int expected)
{
_contentLength.ShouldBe(expected);
}
private void ThenTheContentTypeShouldBeEmpty()
{
_contentType.ShouldBeNullOrEmpty();
_contentTypeHeaderExists.ShouldBe(false);
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
_contentType = context.Request.ContentType;
_contentLength = context.Request.ContentLength;
_contentTypeHeaderExists = context.Request.Headers.TryGetValue("Content-Type", out var value);
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
});
})
.Build();
_builder.Start();
}
public void Dispose()
{
_builder?.Dispose();
_steps.Dispose();
}
}
}

View File

@ -68,9 +68,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureLogging((hostingContext, logging) => .ConfigureLogging((hostingContext, logging) =>
@ -126,9 +126,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -154,9 +154,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -197,9 +197,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -228,9 +228,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -261,9 +261,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -294,9 +294,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -326,9 +326,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -357,9 +357,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -396,9 +396,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -444,9 +444,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -481,9 +481,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -509,9 +509,9 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(s => .ConfigureServices(s =>
@ -570,8 +570,8 @@ namespace Ocelot.AcceptanceTests
{ {
var builder = new ConfigurationBuilder() var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()) .SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile("ocelot.json") .AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables(); .AddEnvironmentVariables();
var configuration = builder.Build(); var configuration = builder.Build();
@ -823,6 +823,11 @@ namespace Ocelot.AcceptanceTests
_postContent = new StringContent(postcontent); _postContent = new StringContent(postcontent);
} }
public void GivenThePostHasContentType(string postcontent)
{
_postContent.Headers.ContentType = new MediaTypeHeaderValue(postcontent);
}
public void GivenThePostHasGzipContent(object input) public void GivenThePostHasGzipContent(object input)
{ {
var json = JsonConvert.SerializeObject(input); var json = JsonConvert.SerializeObject(input);

View File

@ -341,8 +341,8 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureLogging((hostingContext, logging) => .ConfigureLogging((hostingContext, logging) =>
@ -387,8 +387,8 @@ namespace Ocelot.AcceptanceTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureLogging((hostingContext, logging) => .ConfigureLogging((hostingContext, logging) =>

View File

@ -1,157 +1,157 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns; using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Configs;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Middleware; using Ocelot.Middleware;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes.Jobs; using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Diagnosers; using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Validators; using BenchmarkDotNet.Validators;
namespace Ocelot.Benchmarks namespace Ocelot.Benchmarks
{ {
[Config(typeof(AllTheThingsBenchmarks))] [Config(typeof(AllTheThingsBenchmarks))]
public class AllTheThingsBenchmarks : ManualConfig public class AllTheThingsBenchmarks : ManualConfig
{ {
private IWebHost _service; private IWebHost _service;
private IWebHost _ocelot; private IWebHost _ocelot;
private HttpClient _httpClient; private HttpClient _httpClient;
public AllTheThingsBenchmarks() public AllTheThingsBenchmarks()
{ {
Add(StatisticColumn.AllStatistics); Add(StatisticColumn.AllStatistics);
Add(MemoryDiagnoser.Default); Add(MemoryDiagnoser.Default);
Add(BaselineValidator.FailOnError); Add(BaselineValidator.FailOnError);
} }
[GlobalSetup] [GlobalSetup]
public void SetUp() public void SetUp()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51879,
} }
}, },
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
} }
}; };
GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty); GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty);
GivenThereIsAConfiguration(configuration); GivenThereIsAConfiguration(configuration);
GivenOcelotIsRunning("http://localhost:5000"); GivenOcelotIsRunning("http://localhost:5000");
_httpClient = new HttpClient(); _httpClient = new HttpClient();
} }
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public async Task Baseline() public async Task Baseline()
{ {
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/"); var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/");
var response = await _httpClient.SendAsync(request); var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
} }
/* * Summary* /* * Summary*
BenchmarkDotNet = v0.10.13, OS = macOS 10.12.6 (16G1212) [Darwin 16.7.0] BenchmarkDotNet = v0.10.13, OS = macOS 10.12.6 (16G1212) [Darwin 16.7.0]
Intel Core i5-4278U CPU 2.60GHz(Haswell), 1 CPU, 4 logical cores and 2 physical cores Intel Core i5-4278U CPU 2.60GHz(Haswell), 1 CPU, 4 logical cores and 2 physical cores
.NET Core SDK = 2.1.4 .NET Core SDK = 2.1.4
[Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT [Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Scaled | Gen 0 | Gen 1 | Allocated | Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Scaled | Gen 0 | Gen 1 | Allocated |
--------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|--------:|-------:|----------:| --------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|--------:|-------:|----------:|
Baseline | 2.102 ms | 0.0292 ms | 0.0273 ms | 0.0070 ms | 2.063 ms | 2.080 ms | 2.093 ms | 2.122 ms | 2.152 ms | 475.8 | 1.00 | 31.2500 | 3.9063 | 1.63 KB |*/ Baseline | 2.102 ms | 0.0292 ms | 0.0273 ms | 0.0070 ms | 2.063 ms | 2.080 ms | 2.093 ms | 2.122 ms | 2.152 ms | 475.8 | 1.00 | 31.2500 | 3.9063 | 1.63 KB |*/
private void GivenOcelotIsRunning(string url) private void GivenOcelotIsRunning(string url)
{ {
_ocelot = new WebHostBuilder() _ocelot = new WebHostBuilder()
.UseKestrel() .UseKestrel()
.UseUrls(url) .UseUrls(url)
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) => .ConfigureAppConfiguration((hostingContext, config) =>
{ {
config config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true) .AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json") .AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables(); .AddEnvironmentVariables();
}) })
.ConfigureServices(s => { .ConfigureServices(s => {
s.AddOcelot(); s.AddOcelot();
}) })
.ConfigureLogging((hostingContext, logging) => .ConfigureLogging((hostingContext, logging) =>
{ {
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
}) })
.UseIISIntegration() .UseIISIntegration()
.Configure(app => .Configure(app =>
{ {
app.UseOcelot().Wait(); app.UseOcelot().Wait();
}) })
.Build(); .Build();
_ocelot.Start(); _ocelot.Start();
} }
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{ {
var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json"); var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json");
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath)) if (File.Exists(configurationPath))
{ {
File.Delete(configurationPath); File.Delete(configurationPath);
} }
File.WriteAllText(configurationPath, jsonConfiguration); File.WriteAllText(configurationPath, jsonConfiguration);
} }
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
{ {
_service = new WebHostBuilder() _service = new WebHostBuilder()
.UseUrls(baseUrl) .UseUrls(baseUrl)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.Configure(app => .Configure(app =>
{ {
app.UsePathBase(basePath); app.UsePathBase(basePath);
app.Run(async context => app.Run(async context =>
{ {
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody); await context.Response.WriteAsync(responseBody);
}); });
}) })
.Build(); .Build();
_service.Start(); _service.Start();
} }
} }
} }

View File

@ -536,9 +536,9 @@ namespace Ocelot.IntegrationTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(x => .ConfigureServices(x =>
@ -660,9 +660,9 @@ namespace Ocelot.IntegrationTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(x => { .ConfigureServices(x => {
@ -693,9 +693,9 @@ namespace Ocelot.IntegrationTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(x => .ConfigureServices(x =>
@ -733,9 +733,9 @@ namespace Ocelot.IntegrationTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(x => { .ConfigureServices(x => {

View File

@ -446,10 +446,10 @@ namespace Ocelot.IntegrationTests
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddJsonFile("peers.json", optional: true, reloadOnChange: true); config.AddJsonFile("peers.json", optional: true, reloadOnChange: false);
#pragma warning disable CS0618 #pragma warning disable CS0618
config.AddOcelotBaseUrl(url); config.AddOcelotBaseUrl(url);
#pragma warning restore CS0618 #pragma warning restore CS0618

View File

@ -1,218 +1,218 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using CacheManager.Core; using CacheManager.Core;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using Ocelot.Middleware; using Ocelot.Middleware;
namespace Ocelot.IntegrationTests namespace Ocelot.IntegrationTests
{ {
public class ThreadSafeHeadersTests : IDisposable public class ThreadSafeHeadersTests : IDisposable
{ {
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private IWebHost _builder; private IWebHost _builder;
private IWebHostBuilder _webHostBuilder; private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl; private readonly string _ocelotBaseUrl;
private IWebHost _downstreamBuilder; private IWebHost _downstreamBuilder;
private readonly Random _random; private readonly Random _random;
private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results; private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
public ThreadSafeHeadersTests() public ThreadSafeHeadersTests()
{ {
_results = new ConcurrentBag<ThreadSafeHeadersTestResult>(); _results = new ConcurrentBag<ThreadSafeHeadersTestResult>();
_random = new Random(); _random = new Random();
_httpClient = new HttpClient(); _httpClient = new HttpClient();
_ocelotBaseUrl = "http://localhost:5001"; _ocelotBaseUrl = "http://localhost:5001";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl); _httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
} }
[Fact] [Fact]
public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service() public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort> DownstreamHostAndPorts = new List<FileHostAndPort>
{ {
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 51879, Port = 51879,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
} }
} }
}; };
this.Given(x => GivenThereIsAConfiguration(configuration)) this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenThereIsAServiceRunningOn("http://localhost:51879")) .And(x => GivenThereIsAServiceRunningOn("http://localhost:51879"))
.And(x => GivenOcelotIsRunning()) .And(x => GivenOcelotIsRunning())
.When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300)) .When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300))
.Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()) .Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService())
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url) private void GivenThereIsAServiceRunningOn(string url)
{ {
_downstreamBuilder = new WebHostBuilder() _downstreamBuilder = new WebHostBuilder()
.UseUrls(url) .UseUrls(url)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration() .UseIISIntegration()
.UseUrls(url) .UseUrls(url)
.Configure(app => .Configure(app =>
{ {
app.Run(async context => app.Run(async context =>
{ {
var header = context.Request.Headers["ThreadSafeHeadersTest"]; var header = context.Request.Headers["ThreadSafeHeadersTest"];
context.Response.StatusCode = 200; context.Response.StatusCode = 200;
await context.Response.WriteAsync(header[0]); await context.Response.WriteAsync(header[0]);
}); });
}) })
.Build(); .Build();
_downstreamBuilder.Start(); _downstreamBuilder.Start();
} }
private void GivenOcelotIsRunning() private void GivenOcelotIsRunning()
{ {
_webHostBuilder = new WebHostBuilder() _webHostBuilder = new WebHostBuilder()
.UseUrls(_ocelotBaseUrl) .UseUrls(_ocelotBaseUrl)
.UseKestrel() .UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) => .ConfigureAppConfiguration((hostingContext, config) =>
{ {
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment; var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
config.AddJsonFile("ocelot.json"); config.AddJsonFile("ocelot.json", false, false);
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}) })
.ConfigureServices(x => .ConfigureServices(x =>
{ {
Action<ConfigurationBuilderCachePart> settings = (s) => Action<ConfigurationBuilderCachePart> settings = (s) =>
{ {
s.WithMicrosoftLogging(log => s.WithMicrosoftLogging(log =>
{ {
log.AddConsole(LogLevel.Debug); log.AddConsole(LogLevel.Debug);
}) })
.WithDictionaryHandle(); .WithDictionaryHandle();
}; };
x.AddOcelot() x.AddOcelot()
.AddCacheManager(settings) .AddCacheManager(settings)
.AddAdministration("/administration", "secret"); .AddAdministration("/administration", "secret");
}) })
.Configure(app => .Configure(app =>
{ {
app.UseOcelot().Wait(); app.UseOcelot().Wait();
}); });
_builder = _webHostBuilder.Build(); _builder = _webHostBuilder.Build();
_builder.Start(); _builder.Start();
} }
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{ {
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath)) if (File.Exists(configurationPath))
{ {
File.Delete(configurationPath); File.Delete(configurationPath);
} }
File.WriteAllText(configurationPath, jsonConfiguration); File.WriteAllText(configurationPath, jsonConfiguration);
var text = File.ReadAllText(configurationPath); var text = File.ReadAllText(configurationPath);
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
if (File.Exists(configurationPath)) if (File.Exists(configurationPath))
{ {
File.Delete(configurationPath); File.Delete(configurationPath);
} }
File.WriteAllText(configurationPath, jsonConfiguration); File.WriteAllText(configurationPath, jsonConfiguration);
text = File.ReadAllText(configurationPath); text = File.ReadAllText(configurationPath);
} }
private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times) private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
{ {
var tasks = new Task[times]; var tasks = new Task[times];
for (int i = 0; i < times; i++) for (int i = 0; i < times; i++)
{ {
var urlCopy = url; var urlCopy = url;
var random = _random.Next(0, 50); var random = _random.Next(0, 50);
tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random); tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random);
} }
Task.WaitAll(tasks); Task.WaitAll(tasks);
} }
private async Task GetForThreadSafeHeadersTest(string url, int random) private async Task GetForThreadSafeHeadersTest(string url, int random)
{ {
var request = new HttpRequestMessage(HttpMethod.Get, url); var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() }); request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() });
var response = await _httpClient.SendAsync(request); var response = await _httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync(); var content = await response.Content.ReadAsStringAsync();
int result = int.Parse(content); int result = int.Parse(content);
var tshtr = new ThreadSafeHeadersTestResult(result, random); var tshtr = new ThreadSafeHeadersTestResult(result, random);
_results.Add(tshtr); _results.Add(tshtr);
} }
private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService() private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()
{ {
foreach(var result in _results) foreach(var result in _results)
{ {
result.Result.ShouldBe(result.Random); result.Result.ShouldBe(result.Random);
} }
} }
public void Dispose() public void Dispose()
{ {
_builder?.Dispose(); _builder?.Dispose();
_httpClient?.Dispose(); _httpClient?.Dispose();
_downstreamBuilder?.Dispose(); _downstreamBuilder?.Dispose();
} }
class ThreadSafeHeadersTestResult class ThreadSafeHeadersTestResult
{ {
public ThreadSafeHeadersTestResult(int result, int random) public ThreadSafeHeadersTestResult(int result, int random)
{ {
Result = result; Result = result;
Random = random; Random = random;
} }
public int Result { get; private set; } public int Result { get; private set; }
public int Random { get; private set; } public int Random { get; private set; }
} }
} }
} }

View File

@ -26,7 +26,7 @@
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true) .AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json") .AddJsonFile("ocelot.json", false, false)
.AddEnvironmentVariables(); .AddEnvironmentVariables();
}) })
.ConfigureServices(s => { .ConfigureServices(s => {

View File

@ -8,7 +8,7 @@
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "localhost", "Host": "localhost",
"Port": 3000 "Port": 5001
} }
], ],
"QoSOptions": { "QoSOptions": {

View File

@ -95,7 +95,7 @@ namespace Ocelot.UnitTests.Request
_requestMapper _requestMapper
.Setup(rm => rm.Map(It.IsAny<HttpRequest>())) .Setup(rm => rm.Map(It.IsAny<HttpRequest>()))
.ReturnsAsync(_mappedRequest); .Returns(_mappedRequest);
} }
private void GivenTheMapperWillReturnAnError() private void GivenTheMapperWillReturnAnError()
@ -104,7 +104,7 @@ namespace Ocelot.UnitTests.Request
_requestMapper _requestMapper
.Setup(rm => rm.Map(It.IsAny<HttpRequest>())) .Setup(rm => rm.Map(It.IsAny<HttpRequest>()))
.ReturnsAsync(_mappedRequest); .Returns(_mappedRequest);
} }
private void WhenTheMiddlewareIsInvoked() private void WhenTheMiddlewareIsInvoked()

View File

@ -1,407 +1,441 @@
namespace Ocelot.UnitTests.Request.Mapper namespace Ocelot.UnitTests.Request.Mapper
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal; using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Ocelot.Request.Mapper; using Ocelot.Request.Mapper;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Shouldly; using Shouldly;
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Security.Cryptography; using System.Security.Cryptography;
public class RequestMapperTests public class RequestMapperTests
{ {
readonly HttpRequest _inputRequest; readonly HttpRequest _inputRequest;
readonly RequestMapper _requestMapper; readonly RequestMapper _requestMapper;
Response<HttpRequestMessage> _mappedRequest; Response<HttpRequestMessage> _mappedRequest;
List<KeyValuePair<string, StringValues>> _inputHeaders = null; List<KeyValuePair<string, StringValues>> _inputHeaders = null;
public RequestMapperTests() public RequestMapperTests()
{ {
_inputRequest = new DefaultHttpRequest(new DefaultHttpContext()); _inputRequest = new DefaultHttpRequest(new DefaultHttpContext());
_requestMapper = new RequestMapper(); _requestMapper = new RequestMapper();
} }
[Theory] [Theory]
[InlineData("https", "my.url:123", "/abc/DEF", "?a=1&b=2", "https://my.url:123/abc/DEF?a=1&b=2")] [InlineData("https", "my.url:123", "/abc/DEF", "?a=1&b=2", "https://my.url:123/abc/DEF?a=1&b=2")]
[InlineData("http", "blah.com", "/d ef", "?abc=123", "http://blah.com/d%20ef?abc=123")] // note! the input is encoded when building the input request [InlineData("http", "blah.com", "/d ef", "?abc=123", "http://blah.com/d%20ef?abc=123")] // note! the input is encoded when building the input request
[InlineData("http", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")] [InlineData("http", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")]
[InlineData("http", "點看.com", null, null, "http://xn--c1yn36f.com/")] [InlineData("http", "點看.com", null, null, "http://xn--c1yn36f.com/")]
[InlineData("http", "xn--c1yn36f.com", null, null, "http://xn--c1yn36f.com/")] [InlineData("http", "xn--c1yn36f.com", null, null, "http://xn--c1yn36f.com/")]
public void Should_map_valid_request_uri(string scheme, string host, string path, string queryString, string expectedUri) public void Should_map_valid_request_uri(string scheme, string host, string path, string queryString, string expectedUri)
{ {
this.Given(_ => GivenTheInputRequestHasMethod("GET")) this.Given(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasScheme(scheme)) .And(_ => GivenTheInputRequestHasScheme(scheme))
.And(_ => GivenTheInputRequestHasHost(host)) .And(_ => GivenTheInputRequestHasHost(host))
.And(_ => GivenTheInputRequestHasPath(path)) .And(_ => GivenTheInputRequestHasPath(path))
.And(_ => GivenTheInputRequestHasQueryString(queryString)) .And(_ => GivenTheInputRequestHasQueryString(queryString))
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned()) .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasUri(expectedUri)) .And(_ => ThenTheMappedRequestHasUri(expectedUri))
.BDDfy(); .BDDfy();
} }
[Theory] [Theory]
[InlineData("ftp", "google.com", "/abc/DEF", "?a=1&b=2")] [InlineData("ftp", "google.com", "/abc/DEF", "?a=1&b=2")]
public void Should_error_on_unsupported_request_uri(string scheme, string host, string path, string queryString) public void Should_error_on_unsupported_request_uri(string scheme, string host, string path, string queryString)
{ {
this.Given(_ => GivenTheInputRequestHasMethod("GET")) this.Given(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasScheme(scheme)) .And(_ => GivenTheInputRequestHasScheme(scheme))
.And(_ => GivenTheInputRequestHasHost(host)) .And(_ => GivenTheInputRequestHasHost(host))
.And(_ => GivenTheInputRequestHasPath(path)) .And(_ => GivenTheInputRequestHasPath(path))
.And(_ => GivenTheInputRequestHasQueryString(queryString)) .And(_ => GivenTheInputRequestHasQueryString(queryString))
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenAnErrorIsReturned()) .Then(_ => ThenAnErrorIsReturned())
.And(_ => ThenTheMappedRequestIsNull()) .And(_ => ThenTheMappedRequestIsNull())
.BDDfy(); .BDDfy();
} }
[Theory] [Theory]
[InlineData("GET")] [InlineData("GET")]
[InlineData("POST")] [InlineData("POST")]
[InlineData("WHATEVER")] [InlineData("WHATEVER")]
public void Should_map_method(string method) public void Should_map_method(string method)
{ {
this.Given(_ => GivenTheInputRequestHasMethod(method)) this.Given(_ => GivenTheInputRequestHasMethod(method))
.And(_ => GivenTheInputRequestHasAValidUri()) .And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned()) .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasMethod(method)) .And(_ => ThenTheMappedRequestHasMethod(method))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void Should_map_all_headers() public void Should_map_all_headers()
{ {
this.Given(_ => GivenTheInputRequestHasHeaders()) this.Given(_ => GivenTheInputRequestHasHeaders())
.And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri()) .And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned()) .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasEachHeader()) .And(_ => ThenTheMappedRequestHasEachHeader())
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void Should_handle_no_headers() public void Should_handle_no_headers()
{ {
this.Given(_ => GivenTheInputRequestHasNoHeaders()) this.Given(_ => GivenTheInputRequestHasNoHeaders())
.And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri()) .And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned()) .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoHeaders()) .And(_ => ThenTheMappedRequestHasNoHeaders())
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void Should_map_content() public void Should_map_content()
{ {
this.Given(_ => GivenTheInputRequestHasContent("This is my content")) this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri()) .And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned()) .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContent("This is my content")) .And(_ => ThenTheMappedRequestHasContent("This is my content"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void Should_handle_no_content() public void Should_handle_no_content()
{ {
this.Given(_ => GivenTheInputRequestHasNoContent()) this.Given(_ => GivenTheInputRequestHasNullContent())
.And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri()) .And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned()) .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent()) .And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void Should_map_content_headers() public void Should_handle_no_content_type()
{ {
byte[] md5bytes = new byte[0]; this.Given(_ => GivenTheInputRequestHasNoContentType())
using (var md5 = MD5.Create()) .And(_ => GivenTheInputRequestHasMethod("GET"))
{ .And(_ => GivenTheInputRequestHasAValidUri())
md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5")); .When(_ => WhenMapped())
} .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
this.Given(_ => GivenTheInputRequestHasContent("This is my content")) .BDDfy();
.And(_ => GivenTheContentTypeIs("application/json")) }
.And(_ => GivenTheContentEncodingIs("gzip, compress"))
.And(_ => GivenTheContentLanguageIs("english")) [Fact]
.And(_ => GivenTheContentLocationIs("/my-receipts/38")) public void Should_handle_no_content_length()
.And(_ => GivenTheContentRangeIs("bytes 1-2/*")) {
.And(_ => GivenTheContentDispositionIs("inline")) this.Given(_ => GivenTheInputRequestHasNoContentLength())
.And(_ => GivenTheContentMD5Is(md5bytes)) .And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasAValidUri())
.And(_ => GivenTheInputRequestHasAValidUri()) .When(_ => WhenMapped())
.When(_ => WhenMapped()) .Then(_ => ThenNoErrorIsReturned())
.Then(_ => ThenNoErrorIsReturned()) .And(_ => ThenTheMappedRequestHasNoContent())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) .BDDfy();
.And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress")) }
.And(_ => ThenTheMappedRequestHasContentLanguageHeader("english"))
.And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38")) private void GivenTheInputRequestHasNoContentLength()
.And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes)) {
.And(_ => ThenTheMappedRequestHasContentRangeHeader()) _inputRequest.ContentLength = null;
.And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline")) }
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders()) private void GivenTheInputRequestHasNoContentType()
.BDDfy(); {
} _inputRequest.ContentType = null;
}
[Fact]
public void should_not_add_content_headers() [Fact]
{ public void Should_map_content_headers()
this.Given(_ => GivenTheInputRequestHasContent("This is my content")) {
.And(_ => GivenTheContentTypeIs("application/json")) byte[] md5bytes = new byte[0];
.And(_ => GivenTheInputRequestHasMethod("POST")) using (var md5 = MD5.Create())
.And(_ => GivenTheInputRequestHasAValidUri()) {
.When(_ => WhenMapped()) md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5"));
.Then(_ => ThenNoErrorIsReturned()) }
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => ThenTheOtherContentTypeHeadersAreNotMapped()) .And(_ => GivenTheContentTypeIs("application/json"))
.BDDfy(); .And(_ => GivenTheContentEncodingIs("gzip, compress"))
} .And(_ => GivenTheContentLanguageIs("english"))
.And(_ => GivenTheContentLocationIs("/my-receipts/38"))
private void ThenTheContentHeadersAreNotAddedToNonContentHeaders() .And(_ => GivenTheContentRangeIs("bytes 1-2/*"))
{ .And(_ => GivenTheContentDispositionIs("inline"))
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition"); .And(_ => GivenTheContentMD5Is(md5bytes))
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5"); .And(_ => GivenTheInputRequestHasMethod("GET"))
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange"); .And(_ => GivenTheInputRequestHasAValidUri())
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage"); .When(_ => WhenMapped())
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentEncoding"); .Then(_ => ThenNoErrorIsReturned())
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLocation"); .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Length"); .And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress"))
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type"); .And(_ => ThenTheMappedRequestHasContentLanguageHeader("english"))
} .And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38"))
.And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes))
private void ThenTheOtherContentTypeHeadersAreNotMapped() .And(_ => ThenTheMappedRequestHasContentRangeHeader())
{ .And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline"))
_mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull(); .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull(); .And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders())
_mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull(); .BDDfy();
_mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty(); }
_mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull(); [Fact]
} public void should_not_add_content_headers()
{
private void ThenTheMappedRequestHasContentDispositionHeader(string expected) this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
{ .And(_ => GivenTheContentTypeIs("application/json"))
_mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected); .And(_ => GivenTheInputRequestHasMethod("POST"))
} .And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
private void GivenTheContentDispositionIs(string input) .Then(_ => ThenNoErrorIsReturned())
{ .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
_inputRequest.Headers.Add("Content-Disposition", input); .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
} .And(_ => ThenTheOtherContentTypeHeadersAreNotMapped())
.BDDfy();
private void ThenTheMappedRequestHasContentMD5Header(byte[] expected) }
{
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected); private void ThenTheContentHeadersAreNotAddedToNonContentHeaders()
} {
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition");
private void GivenTheContentMD5Is(byte[] input) _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5");
{ _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange");
var base64 = Convert.ToBase64String(input); _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage");
_inputRequest.Headers.Add("Content-MD5", base64); _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");
private void ThenTheMappedRequestHasContentRangeHeader() _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type");
{ }
_mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1);
_mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2); private void ThenTheOtherContentTypeHeadersAreNotMapped()
} {
_mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull();
private void GivenTheContentRangeIs(string input) _mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull();
{ _mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull();
_inputRequest.Headers.Add("Content-Range", input); _mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty();
} _mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull();
private void ThenTheMappedRequestHasContentLocationHeader(string expected) }
{
_mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected); private void ThenTheMappedRequestHasContentDispositionHeader(string expected)
} {
_mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected);
private void GivenTheContentLocationIs(string input) }
{
_inputRequest.Headers.Add("Content-Location", input); private void GivenTheContentDispositionIs(string input)
} {
_inputRequest.Headers.Add("Content-Disposition", input);
private void ThenTheMappedRequestHasContentLanguageHeader(string expected) }
{
_mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected); private void ThenTheMappedRequestHasContentMD5Header(byte[] expected)
} {
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected);
private void GivenTheContentLanguageIs(string input) }
{
_inputRequest.Headers.Add("Content-Language", input); private void GivenTheContentMD5Is(byte[] input)
} {
var base64 = Convert.ToBase64String(input);
private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo) _inputRequest.Headers.Add("Content-MD5", base64);
{ }
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected);
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo); private void ThenTheMappedRequestHasContentRangeHeader()
} {
_mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1);
private void GivenTheContentEncodingIs(string input) _mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2);
{ }
_inputRequest.Headers.Add("Content-Encoding", input);
} private void GivenTheContentRangeIs(string input)
{
private void GivenTheContentTypeIs(string contentType) _inputRequest.Headers.Add("Content-Range", input);
{ }
_inputRequest.ContentType = contentType;
} private void ThenTheMappedRequestHasContentLocationHeader(string expected)
{
private void ThenTheMappedRequestHasContentTypeHeader(string expected) _mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected);
{ }
_mappedRequest.Data.Content.Headers.ContentType.MediaType.ShouldBe(expected);
} private void GivenTheContentLocationIs(string input)
{
private void ThenTheMappedRequestHasContentSize(long expected) _inputRequest.Headers.Add("Content-Location", input);
{ }
_mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected);
} private void ThenTheMappedRequestHasContentLanguageHeader(string expected)
{
private void GivenTheInputRequestHasMethod(string method) _mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected);
{ }
_inputRequest.Method = method;
} private void GivenTheContentLanguageIs(string input)
{
private void GivenTheInputRequestHasScheme(string scheme) _inputRequest.Headers.Add("Content-Language", input);
{ }
_inputRequest.Scheme = scheme;
} private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo)
{
private void GivenTheInputRequestHasHost(string host) _mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected);
{ _mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo);
_inputRequest.Host = new HostString(host); }
}
private void GivenTheContentEncodingIs(string input)
private void GivenTheInputRequestHasPath(string path) {
{ _inputRequest.Headers.Add("Content-Encoding", input);
if (path != null) }
{
_inputRequest.Path = path; private void GivenTheContentTypeIs(string contentType)
} {
} _inputRequest.ContentType = contentType;
}
private void GivenTheInputRequestHasQueryString(string querystring)
{ private void ThenTheMappedRequestHasContentTypeHeader(string expected)
if (querystring != null) {
{ _mappedRequest.Data.Content.Headers.ContentType.MediaType.ShouldBe(expected);
_inputRequest.QueryString = new QueryString(querystring); }
}
} private void ThenTheMappedRequestHasContentSize(long expected)
{
private void GivenTheInputRequestHasAValidUri() _mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected);
{ }
GivenTheInputRequestHasScheme("http");
GivenTheInputRequestHasHost("www.google.com"); private void GivenTheInputRequestHasMethod(string method)
} {
_inputRequest.Method = method;
private void GivenTheInputRequestHasHeaders() }
{
_inputHeaders = new List<KeyValuePair<string, StringValues>>() private void GivenTheInputRequestHasScheme(string scheme)
{ {
new KeyValuePair<string, StringValues>("abc", new StringValues(new string[]{"123","456" })), _inputRequest.Scheme = scheme;
new KeyValuePair<string, StringValues>("def", new StringValues(new string[]{"789","012" })), }
};
private void GivenTheInputRequestHasHost(string host)
foreach (var inputHeader in _inputHeaders) {
{ _inputRequest.Host = new HostString(host);
_inputRequest.Headers.Add(inputHeader); }
}
} private void GivenTheInputRequestHasPath(string path)
{
private void GivenTheInputRequestHasNoHeaders() if (path != null)
{ {
_inputRequest.Headers.Clear(); _inputRequest.Path = path;
} }
}
private void GivenTheInputRequestHasContent(string content)
{ private void GivenTheInputRequestHasQueryString(string querystring)
_inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content)); {
} if (querystring != null)
{
private void GivenTheInputRequestHasNoContent() _inputRequest.QueryString = new QueryString(querystring);
{ }
_inputRequest.Body = null; }
}
private void GivenTheInputRequestHasAValidUri()
private void WhenMapped() {
{ GivenTheInputRequestHasScheme("http");
_mappedRequest = _requestMapper.Map(_inputRequest).GetAwaiter().GetResult(); GivenTheInputRequestHasHost("www.google.com");
} }
private void ThenNoErrorIsReturned() private void GivenTheInputRequestHasHeaders()
{ {
_mappedRequest.IsError.ShouldBeFalse(); _inputHeaders = new List<KeyValuePair<string, StringValues>>()
} {
new KeyValuePair<string, StringValues>("abc", new StringValues(new string[]{"123","456" })),
private void ThenAnErrorIsReturned() new KeyValuePair<string, StringValues>("def", new StringValues(new string[]{"789","012" })),
{ };
_mappedRequest.IsError.ShouldBeTrue();
} foreach (var inputHeader in _inputHeaders)
{
private void ThenTheMappedRequestHasUri(string expectedUri) _inputRequest.Headers.Add(inputHeader);
{ }
_mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri); }
}
private void GivenTheInputRequestHasNoHeaders()
private void ThenTheMappedRequestHasMethod(string expectedMethod) {
{ _inputRequest.Headers.Clear();
_mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod); }
}
private void GivenTheInputRequestHasContent(string content)
private void ThenTheMappedRequestHasEachHeader() {
{ _inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content));
_mappedRequest.Data.Headers.Count().ShouldBe(_inputHeaders.Count); }
foreach(var header in _mappedRequest.Data.Headers)
{ private void GivenTheInputRequestHasNullContent()
var inputHeader = _inputHeaders.First(h => h.Key == header.Key); {
inputHeader.ShouldNotBeNull(); _inputRequest.Body = null;
inputHeader.Value.Count().ShouldBe(header.Value.Count()); }
foreach(var inputHeaderValue in inputHeader.Value)
{ private void WhenMapped()
header.Value.Any(v => v == inputHeaderValue); {
} _mappedRequest = _requestMapper.Map(_inputRequest);
} }
}
private void ThenNoErrorIsReturned()
private void ThenTheMappedRequestHasNoHeaders() {
{ _mappedRequest.IsError.ShouldBeFalse();
_mappedRequest.Data.Headers.Count().ShouldBe(0); }
}
private void ThenAnErrorIsReturned()
private void ThenTheMappedRequestHasContent(string expectedContent) {
{ _mappedRequest.IsError.ShouldBeTrue();
_mappedRequest.Data.Content.ReadAsStringAsync().GetAwaiter().GetResult().ShouldBe(expectedContent); }
}
private void ThenTheMappedRequestHasUri(string expectedUri)
private void ThenTheMappedRequestHasNoContent() {
{ _mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri);
_mappedRequest.Data.Content.ShouldBeNull(); }
}
private void ThenTheMappedRequestHasMethod(string expectedMethod)
private void ThenTheMappedRequestIsNull() {
{ _mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod);
_mappedRequest.Data.ShouldBeNull(); }
}
} private void ThenTheMappedRequestHasEachHeader()
} {
_mappedRequest.Data.Headers.Count().ShouldBe(_inputHeaders.Count);
foreach(var header in _mappedRequest.Data.Headers)
{
var inputHeader = _inputHeaders.First(h => h.Key == header.Key);
inputHeader.ShouldNotBeNull();
inputHeader.Value.Count().ShouldBe(header.Value.Count());
foreach(var inputHeaderValue in inputHeader.Value)
{
header.Value.Any(v => v == inputHeaderValue);
}
}
}
private void ThenTheMappedRequestHasNoHeaders()
{
_mappedRequest.Data.Headers.Count().ShouldBe(0);
}
private void ThenTheMappedRequestHasContent(string expectedContent)
{
_mappedRequest.Data.Content.ReadAsStringAsync().GetAwaiter().GetResult().ShouldBe(expectedContent);
}
private void ThenTheMappedRequestHasNoContent()
{
_mappedRequest.Data.Content.ShouldBeNull();
}
private void ThenTheMappedRequestIsNull()
{
_mappedRequest.Data.ShouldBeNull();
}
}
}