mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 10:12:51 +08:00
* #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:
parent
12ef3bc00f
commit
23c5fcbf91
@ -22,7 +22,7 @@
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile("ocelot.json")
|
||||
.AddJsonFile("ocelot.json", false, false)
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
|
@ -106,7 +106,7 @@ namespace OcelotGraphQL
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile("ocelot.json")
|
||||
.AddJsonFile("ocelot.json", false, false)
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s => {
|
||||
|
@ -59,7 +59,6 @@ namespace OcelotApplicationApiGateway
|
||||
{
|
||||
this.webHost = new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
//.UseStartup<Startup>()
|
||||
.UseUrls(this.listeningAddress)
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
@ -67,7 +66,7 @@ namespace OcelotApplicationApiGateway
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile("ocelot.json")
|
||||
.AddJsonFile("ocelot.json", false, false)
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
|
@ -1,61 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Authentication.Middleware
|
||||
{
|
||||
public class AuthenticationMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
|
||||
public AuthenticationMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
: base(loggerFactory.CreateLogger<AuthenticationMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||
{
|
||||
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);
|
||||
|
||||
context.HttpContext.User = result.Principal;
|
||||
|
||||
if (context.HttpContext.User.Identity.IsAuthenticated)
|
||||
{
|
||||
Logger.LogInformation($"Client has been authenticated for {context.HttpContext.Request.Path}");
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = new UnauthenticatedError(
|
||||
$"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}");
|
||||
|
||||
SetPipelineError(context, error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation($"No authentication needed for {context.HttpContext.Request.Path}");
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
|
||||
{
|
||||
return reRoute.IsAuthenticated;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Authentication.Middleware
|
||||
{
|
||||
public class AuthenticationMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
|
||||
public AuthenticationMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
: base(loggerFactory.CreateLogger<AuthenticationMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||
{
|
||||
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);
|
||||
|
||||
context.HttpContext.User = result.Principal;
|
||||
|
||||
if (context.HttpContext.User.Identity.IsAuthenticated)
|
||||
{
|
||||
Logger.LogInformation($"Client has been authenticated for {context.HttpContext.Request.Path}");
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
var error = new UnauthenticatedError(
|
||||
$"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}");
|
||||
|
||||
SetPipelineError(context, error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation($"No authentication needed for {context.HttpContext.Request.Path}");
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute)
|
||||
{
|
||||
return reRoute.IsAuthenticated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ namespace Ocelot.DependencyInjection
|
||||
|
||||
File.WriteAllText("ocelot.json", json);
|
||||
|
||||
builder.AddJsonFile("ocelot.json");
|
||||
builder.AddJsonFile("ocelot.json", false, false);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
namespace Ocelot.Request.Mapper
|
||||
{
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public interface IRequestMapper
|
||||
{
|
||||
Task<Response<HttpRequestMessage>> Map(HttpRequest request);
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Request.Mapper
|
||||
{
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public interface IRequestMapper
|
||||
{
|
||||
Response<HttpRequestMessage> Map(HttpRequest request);
|
||||
}
|
||||
}
|
||||
|
@ -1,108 +1,99 @@
|
||||
namespace Ocelot.Request.Mapper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public class RequestMapper : IRequestMapper
|
||||
{
|
||||
private readonly string[] _unsupportedHeaders = { "host" };
|
||||
|
||||
public async Task<Response<HttpRequestMessage>> Map(HttpRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var requestMessage = new HttpRequestMessage()
|
||||
{
|
||||
Content = await MapContent(request),
|
||||
Method = MapMethod(request),
|
||||
RequestUri = MapUri(request)
|
||||
};
|
||||
|
||||
MapHeaders(request, requestMessage);
|
||||
|
||||
return new OkResponse<HttpRequestMessage>(requestMessage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ErrorResponse<HttpRequestMessage>(new UnmappableRequestError(ex));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpContent> MapContent(HttpRequest request)
|
||||
{
|
||||
if (request.Body == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var content = new ByteArrayContent(await ToByteArray(request.Body));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return new HttpMethod(request.Method);
|
||||
}
|
||||
|
||||
private Uri MapUri(HttpRequest request)
|
||||
{
|
||||
return new Uri(request.GetEncodedUrl());
|
||||
}
|
||||
|
||||
private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage)
|
||||
{
|
||||
foreach (var header in request.Headers)
|
||||
{
|
||||
if (IsSupportedHeader(header))
|
||||
{
|
||||
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<byte[]> ToByteArray(Stream stream)
|
||||
{
|
||||
using (stream)
|
||||
{
|
||||
using (var memStream = new MemoryStream())
|
||||
{
|
||||
await stream.CopyToAsync(memStream);
|
||||
return memStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsSupportedHeader(KeyValuePair<string, StringValues> header)
|
||||
{
|
||||
return !_unsupportedHeaders.Contains(header.Key.ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Request.Mapper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public class RequestMapper : IRequestMapper
|
||||
{
|
||||
private readonly string[] _unsupportedHeaders = { "host" };
|
||||
|
||||
public Response<HttpRequestMessage> Map(HttpRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
var requestMessage = new HttpRequestMessage()
|
||||
{
|
||||
Content = MapContent(request),
|
||||
Method = MapMethod(request),
|
||||
RequestUri = MapUri(request)
|
||||
};
|
||||
|
||||
MapHeaders(request, requestMessage);
|
||||
|
||||
return new OkResponse<HttpRequestMessage>(requestMessage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ErrorResponse<HttpRequestMessage>(new UnmappableRequestError(ex));
|
||||
}
|
||||
}
|
||||
|
||||
private HttpContent MapContent(HttpRequest request)
|
||||
{
|
||||
if (request.Body == null || (request.Body.CanSeek && request.Body.Length <= 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var content = new StreamContent(request.Body);
|
||||
|
||||
if(!string.IsNullOrEmpty(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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return new HttpMethod(request.Method);
|
||||
}
|
||||
|
||||
private Uri MapUri(HttpRequest request)
|
||||
{
|
||||
return new Uri(request.GetEncodedUrl());
|
||||
}
|
||||
|
||||
private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage)
|
||||
{
|
||||
foreach (var header in request.Headers)
|
||||
{
|
||||
if (IsSupportedHeader(header))
|
||||
{
|
||||
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsSupportedHeader(KeyValuePair<string, StringValues> header)
|
||||
{
|
||||
return !_unsupportedHeaders.Contains(header.Key.ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace Ocelot.Request.Middleware
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var downstreamRequest = await _requestMapper.Map(context.HttpContext.Request);
|
||||
var downstreamRequest = _requestMapper.Map(context.HttpContext.Request);
|
||||
|
||||
if (downstreamRequest.IsError)
|
||||
{
|
||||
|
190
test/Ocelot.AcceptanceTests/ContentTests.cs
Normal file
190
test/Ocelot.AcceptanceTests/ContentTests.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -68,9 +68,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
@ -126,9 +126,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -154,9 +154,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -197,9 +197,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -228,9 +228,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -261,9 +261,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -294,9 +294,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -326,9 +326,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -357,9 +357,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -396,9 +396,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -444,9 +444,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -481,9 +481,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -509,9 +509,9 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s =>
|
||||
@ -570,8 +570,8 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile("ocelot.json")
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile("ocelot.json", false, false)
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
var configuration = builder.Build();
|
||||
@ -823,6 +823,11 @@ namespace Ocelot.AcceptanceTests
|
||||
_postContent = new StringContent(postcontent);
|
||||
}
|
||||
|
||||
public void GivenThePostHasContentType(string postcontent)
|
||||
{
|
||||
_postContent.Headers.ContentType = new MediaTypeHeaderValue(postcontent);
|
||||
}
|
||||
|
||||
public void GivenThePostHasGzipContent(object input)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(input);
|
||||
|
@ -341,8 +341,8 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
@ -387,8 +387,8 @@ namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
|
@ -1,157 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Columns;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.DependencyInjection;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes.Jobs;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using BenchmarkDotNet.Validators;
|
||||
|
||||
namespace Ocelot.Benchmarks
|
||||
{
|
||||
[Config(typeof(AllTheThingsBenchmarks))]
|
||||
public class AllTheThingsBenchmarks : ManualConfig
|
||||
{
|
||||
private IWebHost _service;
|
||||
private IWebHost _ocelot;
|
||||
private HttpClient _httpClient;
|
||||
|
||||
public AllTheThingsBenchmarks()
|
||||
{
|
||||
Add(StatisticColumn.AllStatistics);
|
||||
Add(MemoryDiagnoser.Default);
|
||||
Add(BaselineValidator.FailOnError);
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void SetUp()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 51879,
|
||||
}
|
||||
},
|
||||
DownstreamScheme = "http",
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty);
|
||||
GivenThereIsAConfiguration(configuration);
|
||||
GivenOcelotIsRunning("http://localhost:5000");
|
||||
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public async Task Baseline()
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
/* * Summary*
|
||||
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
|
||||
.NET Core SDK = 2.1.4
|
||||
|
||||
[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
|
||||
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 |*/
|
||||
|
||||
private void GivenOcelotIsRunning(string url)
|
||||
{
|
||||
_ocelot = new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseUrls(url)
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile("ocelot.json")
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s => {
|
||||
s.AddOcelot();
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
{
|
||||
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
||||
})
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
})
|
||||
.Build();
|
||||
|
||||
_ocelot.Start();
|
||||
}
|
||||
|
||||
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json");
|
||||
|
||||
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
File.Delete(configurationPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_service = new WebHostBuilder()
|
||||
.UseUrls(baseUrl)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UsePathBase(basePath);
|
||||
app.Run(async context =>
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_service.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Columns;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.DependencyInjection;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes.Jobs;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using BenchmarkDotNet.Validators;
|
||||
|
||||
namespace Ocelot.Benchmarks
|
||||
{
|
||||
[Config(typeof(AllTheThingsBenchmarks))]
|
||||
public class AllTheThingsBenchmarks : ManualConfig
|
||||
{
|
||||
private IWebHost _service;
|
||||
private IWebHost _ocelot;
|
||||
private HttpClient _httpClient;
|
||||
|
||||
public AllTheThingsBenchmarks()
|
||||
{
|
||||
Add(StatisticColumn.AllStatistics);
|
||||
Add(MemoryDiagnoser.Default);
|
||||
Add(BaselineValidator.FailOnError);
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void SetUp()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 51879,
|
||||
}
|
||||
},
|
||||
DownstreamScheme = "http",
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty);
|
||||
GivenThereIsAConfiguration(configuration);
|
||||
GivenOcelotIsRunning("http://localhost:5000");
|
||||
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public async Task Baseline()
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/");
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
/* * Summary*
|
||||
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
|
||||
.NET Core SDK = 2.1.4
|
||||
|
||||
[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
|
||||
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 |*/
|
||||
|
||||
private void GivenOcelotIsRunning(string url)
|
||||
{
|
||||
_ocelot = new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseUrls(url)
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile("ocelot.json", false, false)
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s => {
|
||||
s.AddOcelot();
|
||||
})
|
||||
.ConfigureLogging((hostingContext, logging) =>
|
||||
{
|
||||
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
|
||||
})
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
})
|
||||
.Build();
|
||||
|
||||
_ocelot.Start();
|
||||
}
|
||||
|
||||
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json");
|
||||
|
||||
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
File.Delete(configurationPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_service = new WebHostBuilder()
|
||||
.UseUrls(baseUrl)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UsePathBase(basePath);
|
||||
app.Run(async context =>
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_service.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -536,9 +536,9 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(x =>
|
||||
@ -660,9 +660,9 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(x => {
|
||||
@ -693,9 +693,9 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(x =>
|
||||
@ -733,9 +733,9 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(x => {
|
||||
|
@ -446,10 +446,10 @@ namespace Ocelot.IntegrationTests
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddJsonFile("peers.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddJsonFile("peers.json", optional: true, reloadOnChange: false);
|
||||
#pragma warning disable CS0618
|
||||
config.AddOcelotBaseUrl(url);
|
||||
#pragma warning restore CS0618
|
||||
|
@ -1,218 +1,218 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using CacheManager.Core;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.IntegrationTests
|
||||
{
|
||||
public class ThreadSafeHeadersTests : IDisposable
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private IWebHost _builder;
|
||||
private IWebHostBuilder _webHostBuilder;
|
||||
private readonly string _ocelotBaseUrl;
|
||||
private IWebHost _downstreamBuilder;
|
||||
private readonly Random _random;
|
||||
private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
|
||||
|
||||
public ThreadSafeHeadersTests()
|
||||
{
|
||||
_results = new ConcurrentBag<ThreadSafeHeadersTestResult>();
|
||||
_random = new Random();
|
||||
_httpClient = new HttpClient();
|
||||
_ocelotBaseUrl = "http://localhost:5001";
|
||||
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service()
|
||||
{
|
||||
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> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => GivenThereIsAConfiguration(configuration))
|
||||
.And(x => GivenThereIsAServiceRunningOn("http://localhost:51879"))
|
||||
.And(x => GivenOcelotIsRunning())
|
||||
.When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300))
|
||||
.Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string url)
|
||||
{
|
||||
_downstreamBuilder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Run(async context =>
|
||||
{
|
||||
var header = context.Request.Headers["ThreadSafeHeadersTest"];
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
await context.Response.WriteAsync(header[0]);
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_downstreamBuilder.Start();
|
||||
}
|
||||
|
||||
private void GivenOcelotIsRunning()
|
||||
{
|
||||
_webHostBuilder = new WebHostBuilder()
|
||||
.UseUrls(_ocelotBaseUrl)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
config.AddJsonFile("ocelot.json");
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(x =>
|
||||
{
|
||||
Action<ConfigurationBuilderCachePart> settings = (s) =>
|
||||
{
|
||||
s.WithMicrosoftLogging(log =>
|
||||
{
|
||||
log.AddConsole(LogLevel.Debug);
|
||||
})
|
||||
.WithDictionaryHandle();
|
||||
};
|
||||
|
||||
x.AddOcelot()
|
||||
.AddCacheManager(settings)
|
||||
.AddAdministration("/administration", "secret");
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
});
|
||||
|
||||
_builder = _webHostBuilder.Build();
|
||||
|
||||
_builder.Start();
|
||||
}
|
||||
|
||||
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
|
||||
|
||||
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
File.Delete(configurationPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||
|
||||
var text = File.ReadAllText(configurationPath);
|
||||
|
||||
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
|
||||
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
File.Delete(configurationPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||
|
||||
text = File.ReadAllText(configurationPath);
|
||||
}
|
||||
|
||||
private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
|
||||
{
|
||||
var tasks = new Task[times];
|
||||
|
||||
for (int i = 0; i < times; i++)
|
||||
{
|
||||
var urlCopy = url;
|
||||
var random = _random.Next(0, 50);
|
||||
tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random);
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
|
||||
private async Task GetForThreadSafeHeadersTest(string url, int random)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() });
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
int result = int.Parse(content);
|
||||
var tshtr = new ThreadSafeHeadersTestResult(result, random);
|
||||
_results.Add(tshtr);
|
||||
}
|
||||
|
||||
private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()
|
||||
{
|
||||
foreach(var result in _results)
|
||||
{
|
||||
result.Result.ShouldBe(result.Random);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_builder?.Dispose();
|
||||
_httpClient?.Dispose();
|
||||
_downstreamBuilder?.Dispose();
|
||||
}
|
||||
|
||||
class ThreadSafeHeadersTestResult
|
||||
{
|
||||
public ThreadSafeHeadersTestResult(int result, int random)
|
||||
{
|
||||
Result = result;
|
||||
Random = random;
|
||||
}
|
||||
|
||||
public int Result { get; private set; }
|
||||
public int Random { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using CacheManager.Core;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.IntegrationTests
|
||||
{
|
||||
public class ThreadSafeHeadersTests : IDisposable
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private IWebHost _builder;
|
||||
private IWebHostBuilder _webHostBuilder;
|
||||
private readonly string _ocelotBaseUrl;
|
||||
private IWebHost _downstreamBuilder;
|
||||
private readonly Random _random;
|
||||
private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
|
||||
|
||||
public ThreadSafeHeadersTests()
|
||||
{
|
||||
_results = new ConcurrentBag<ThreadSafeHeadersTestResult>();
|
||||
_random = new Random();
|
||||
_httpClient = new HttpClient();
|
||||
_ocelotBaseUrl = "http://localhost:5001";
|
||||
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_same_response_for_each_different_header_under_load_to_downsteam_service()
|
||||
{
|
||||
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> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => GivenThereIsAConfiguration(configuration))
|
||||
.And(x => GivenThereIsAServiceRunningOn("http://localhost:51879"))
|
||||
.And(x => GivenOcelotIsRunning())
|
||||
.When(x => WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues("/", 300))
|
||||
.Then(x => ThenTheSameHeaderValuesAreReturnedByTheDownstreamService())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string url)
|
||||
{
|
||||
_downstreamBuilder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Run(async context =>
|
||||
{
|
||||
var header = context.Request.Headers["ThreadSafeHeadersTest"];
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
await context.Response.WriteAsync(header[0]);
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_downstreamBuilder.Start();
|
||||
}
|
||||
|
||||
private void GivenOcelotIsRunning()
|
||||
{
|
||||
_webHostBuilder = new WebHostBuilder()
|
||||
.UseUrls(_ocelotBaseUrl)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(x =>
|
||||
{
|
||||
Action<ConfigurationBuilderCachePart> settings = (s) =>
|
||||
{
|
||||
s.WithMicrosoftLogging(log =>
|
||||
{
|
||||
log.AddConsole(LogLevel.Debug);
|
||||
})
|
||||
.WithDictionaryHandle();
|
||||
};
|
||||
|
||||
x.AddOcelot()
|
||||
.AddCacheManager(settings)
|
||||
.AddAdministration("/administration", "secret");
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
});
|
||||
|
||||
_builder = _webHostBuilder.Build();
|
||||
|
||||
_builder.Start();
|
||||
}
|
||||
|
||||
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
|
||||
|
||||
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
File.Delete(configurationPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||
|
||||
var text = File.ReadAllText(configurationPath);
|
||||
|
||||
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
|
||||
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
File.Delete(configurationPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||
|
||||
text = File.ReadAllText(configurationPath);
|
||||
}
|
||||
|
||||
private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
|
||||
{
|
||||
var tasks = new Task[times];
|
||||
|
||||
for (int i = 0; i < times; i++)
|
||||
{
|
||||
var urlCopy = url;
|
||||
var random = _random.Next(0, 50);
|
||||
tasks[i] = GetForThreadSafeHeadersTest(urlCopy, random);
|
||||
}
|
||||
|
||||
Task.WaitAll(tasks);
|
||||
}
|
||||
|
||||
private async Task GetForThreadSafeHeadersTest(string url, int random)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
request.Headers.Add("ThreadSafeHeadersTest", new List<string> { random.ToString() });
|
||||
var response = await _httpClient.SendAsync(request);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
int result = int.Parse(content);
|
||||
var tshtr = new ThreadSafeHeadersTestResult(result, random);
|
||||
_results.Add(tshtr);
|
||||
}
|
||||
|
||||
private void ThenTheSameHeaderValuesAreReturnedByTheDownstreamService()
|
||||
{
|
||||
foreach(var result in _results)
|
||||
{
|
||||
result.Result.ShouldBe(result.Random);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_builder?.Dispose();
|
||||
_httpClient?.Dispose();
|
||||
_downstreamBuilder?.Dispose();
|
||||
}
|
||||
|
||||
class ThreadSafeHeadersTestResult
|
||||
{
|
||||
public ThreadSafeHeadersTestResult(int result, int random)
|
||||
{
|
||||
Result = result;
|
||||
Random = random;
|
||||
}
|
||||
|
||||
public int Result { get; private set; }
|
||||
public int Random { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddJsonFile("ocelot.json")
|
||||
.AddJsonFile("ocelot.json", false, false)
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(s => {
|
||||
|
@ -8,7 +8,7 @@
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 3000
|
||||
"Port": 5001
|
||||
}
|
||||
],
|
||||
"QoSOptions": {
|
||||
|
@ -95,7 +95,7 @@ namespace Ocelot.UnitTests.Request
|
||||
|
||||
_requestMapper
|
||||
.Setup(rm => rm.Map(It.IsAny<HttpRequest>()))
|
||||
.ReturnsAsync(_mappedRequest);
|
||||
.Returns(_mappedRequest);
|
||||
}
|
||||
|
||||
private void GivenTheMapperWillReturnAnError()
|
||||
@ -104,7 +104,7 @@ namespace Ocelot.UnitTests.Request
|
||||
|
||||
_requestMapper
|
||||
.Setup(rm => rm.Map(It.IsAny<HttpRequest>()))
|
||||
.ReturnsAsync(_mappedRequest);
|
||||
.Returns(_mappedRequest);
|
||||
}
|
||||
|
||||
private void WhenTheMiddlewareIsInvoked()
|
||||
|
@ -1,407 +1,441 @@
|
||||
namespace Ocelot.UnitTests.Request.Mapper
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Request.Mapper;
|
||||
using Ocelot.Responses;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
public class RequestMapperTests
|
||||
{
|
||||
readonly HttpRequest _inputRequest;
|
||||
|
||||
readonly RequestMapper _requestMapper;
|
||||
|
||||
Response<HttpRequestMessage> _mappedRequest;
|
||||
|
||||
List<KeyValuePair<string, StringValues>> _inputHeaders = null;
|
||||
|
||||
public RequestMapperTests()
|
||||
{
|
||||
_inputRequest = new DefaultHttpRequest(new DefaultHttpContext());
|
||||
|
||||
_requestMapper = new RequestMapper();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[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", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")]
|
||||
[InlineData("http", "點看.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)
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasScheme(scheme))
|
||||
.And(_ => GivenTheInputRequestHasHost(host))
|
||||
.And(_ => GivenTheInputRequestHasPath(path))
|
||||
.And(_ => GivenTheInputRequestHasQueryString(queryString))
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasUri(expectedUri))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[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)
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasScheme(scheme))
|
||||
.And(_ => GivenTheInputRequestHasHost(host))
|
||||
.And(_ => GivenTheInputRequestHasPath(path))
|
||||
.And(_ => GivenTheInputRequestHasQueryString(queryString))
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenAnErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestIsNull())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET")]
|
||||
[InlineData("POST")]
|
||||
[InlineData("WHATEVER")]
|
||||
public void Should_map_method(string method)
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasMethod(method))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasMethod(method))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_map_all_headers()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasHeaders())
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasEachHeader())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_handle_no_headers()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasNoHeaders())
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasNoHeaders())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_map_content()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasContent("This is my content"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
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"))
|
||||
.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(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.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(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders())
|
||||
.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)
|
||||
{
|
||||
_inputRequest.ContentType = contentType;
|
||||
}
|
||||
|
||||
private void ThenTheMappedRequestHasContentTypeHeader(string expected)
|
||||
{
|
||||
_mappedRequest.Data.Content.Headers.ContentType.MediaType.ShouldBe(expected);
|
||||
}
|
||||
|
||||
private void ThenTheMappedRequestHasContentSize(long expected)
|
||||
{
|
||||
_mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected);
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasMethod(string method)
|
||||
{
|
||||
_inputRequest.Method = method;
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasScheme(string scheme)
|
||||
{
|
||||
_inputRequest.Scheme = scheme;
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasHost(string host)
|
||||
{
|
||||
_inputRequest.Host = new HostString(host);
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasPath(string path)
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
_inputRequest.Path = path;
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasQueryString(string querystring)
|
||||
{
|
||||
if (querystring != null)
|
||||
{
|
||||
_inputRequest.QueryString = new QueryString(querystring);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasAValidUri()
|
||||
{
|
||||
GivenTheInputRequestHasScheme("http");
|
||||
GivenTheInputRequestHasHost("www.google.com");
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasHeaders()
|
||||
{
|
||||
_inputHeaders = new List<KeyValuePair<string, StringValues>>()
|
||||
{
|
||||
new KeyValuePair<string, StringValues>("abc", new StringValues(new string[]{"123","456" })),
|
||||
new KeyValuePair<string, StringValues>("def", new StringValues(new string[]{"789","012" })),
|
||||
};
|
||||
|
||||
foreach (var inputHeader in _inputHeaders)
|
||||
{
|
||||
_inputRequest.Headers.Add(inputHeader);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasNoHeaders()
|
||||
{
|
||||
_inputRequest.Headers.Clear();
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasContent(string content)
|
||||
{
|
||||
_inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content));
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasNoContent()
|
||||
{
|
||||
_inputRequest.Body = null;
|
||||
}
|
||||
|
||||
private void WhenMapped()
|
||||
{
|
||||
_mappedRequest = _requestMapper.Map(_inputRequest).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private void ThenNoErrorIsReturned()
|
||||
{
|
||||
_mappedRequest.IsError.ShouldBeFalse();
|
||||
}
|
||||
|
||||
private void ThenAnErrorIsReturned()
|
||||
{
|
||||
_mappedRequest.IsError.ShouldBeTrue();
|
||||
}
|
||||
|
||||
private void ThenTheMappedRequestHasUri(string expectedUri)
|
||||
{
|
||||
_mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri);
|
||||
}
|
||||
|
||||
private void ThenTheMappedRequestHasMethod(string expectedMethod)
|
||||
{
|
||||
_mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Ocelot.UnitTests.Request.Mapper
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Request.Mapper;
|
||||
using Ocelot.Responses;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
public class RequestMapperTests
|
||||
{
|
||||
readonly HttpRequest _inputRequest;
|
||||
|
||||
readonly RequestMapper _requestMapper;
|
||||
|
||||
Response<HttpRequestMessage> _mappedRequest;
|
||||
|
||||
List<KeyValuePair<string, StringValues>> _inputHeaders = null;
|
||||
|
||||
public RequestMapperTests()
|
||||
{
|
||||
_inputRequest = new DefaultHttpRequest(new DefaultHttpContext());
|
||||
|
||||
_requestMapper = new RequestMapper();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[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", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")]
|
||||
[InlineData("http", "點看.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)
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasScheme(scheme))
|
||||
.And(_ => GivenTheInputRequestHasHost(host))
|
||||
.And(_ => GivenTheInputRequestHasPath(path))
|
||||
.And(_ => GivenTheInputRequestHasQueryString(queryString))
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasUri(expectedUri))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[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)
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasScheme(scheme))
|
||||
.And(_ => GivenTheInputRequestHasHost(host))
|
||||
.And(_ => GivenTheInputRequestHasPath(path))
|
||||
.And(_ => GivenTheInputRequestHasQueryString(queryString))
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenAnErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestIsNull())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GET")]
|
||||
[InlineData("POST")]
|
||||
[InlineData("WHATEVER")]
|
||||
public void Should_map_method(string method)
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasMethod(method))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasMethod(method))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_map_all_headers()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasHeaders())
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasEachHeader())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_handle_no_headers()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasNoHeaders())
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasNoHeaders())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_map_content()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasContent("This is my content"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_handle_no_content()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasNullContent())
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasNoContent())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_handle_no_content_type()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasNoContentType())
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasNoContent())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_handle_no_content_length()
|
||||
{
|
||||
this.Given(_ => GivenTheInputRequestHasNoContentLength())
|
||||
.And(_ => GivenTheInputRequestHasMethod("GET"))
|
||||
.And(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.And(_ => ThenTheMappedRequestHasNoContent())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasNoContentLength()
|
||||
{
|
||||
_inputRequest.ContentLength = null;
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasNoContentType()
|
||||
{
|
||||
_inputRequest.ContentType = null;
|
||||
}
|
||||
|
||||
[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"))
|
||||
.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(_ => GivenTheInputRequestHasAValidUri())
|
||||
.When(_ => WhenMapped())
|
||||
.Then(_ => ThenNoErrorIsReturned())
|
||||
.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(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders())
|
||||
.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)
|
||||
{
|
||||
_inputRequest.ContentType = contentType;
|
||||
}
|
||||
|
||||
private void ThenTheMappedRequestHasContentTypeHeader(string expected)
|
||||
{
|
||||
_mappedRequest.Data.Content.Headers.ContentType.MediaType.ShouldBe(expected);
|
||||
}
|
||||
|
||||
private void ThenTheMappedRequestHasContentSize(long expected)
|
||||
{
|
||||
_mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected);
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasMethod(string method)
|
||||
{
|
||||
_inputRequest.Method = method;
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasScheme(string scheme)
|
||||
{
|
||||
_inputRequest.Scheme = scheme;
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasHost(string host)
|
||||
{
|
||||
_inputRequest.Host = new HostString(host);
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasPath(string path)
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
_inputRequest.Path = path;
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasQueryString(string querystring)
|
||||
{
|
||||
if (querystring != null)
|
||||
{
|
||||
_inputRequest.QueryString = new QueryString(querystring);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasAValidUri()
|
||||
{
|
||||
GivenTheInputRequestHasScheme("http");
|
||||
GivenTheInputRequestHasHost("www.google.com");
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasHeaders()
|
||||
{
|
||||
_inputHeaders = new List<KeyValuePair<string, StringValues>>()
|
||||
{
|
||||
new KeyValuePair<string, StringValues>("abc", new StringValues(new string[]{"123","456" })),
|
||||
new KeyValuePair<string, StringValues>("def", new StringValues(new string[]{"789","012" })),
|
||||
};
|
||||
|
||||
foreach (var inputHeader in _inputHeaders)
|
||||
{
|
||||
_inputRequest.Headers.Add(inputHeader);
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasNoHeaders()
|
||||
{
|
||||
_inputRequest.Headers.Clear();
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasContent(string content)
|
||||
{
|
||||
_inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content));
|
||||
}
|
||||
|
||||
private void GivenTheInputRequestHasNullContent()
|
||||
{
|
||||
_inputRequest.Body = null;
|
||||
}
|
||||
|
||||
private void WhenMapped()
|
||||
{
|
||||
_mappedRequest = _requestMapper.Map(_inputRequest);
|
||||
}
|
||||
|
||||
private void ThenNoErrorIsReturned()
|
||||
{
|
||||
_mappedRequest.IsError.ShouldBeFalse();
|
||||
}
|
||||
|
||||
private void ThenAnErrorIsReturned()
|
||||
{
|
||||
_mappedRequest.IsError.ShouldBeTrue();
|
||||
}
|
||||
|
||||
private void ThenTheMappedRequestHasUri(string expectedUri)
|
||||
{
|
||||
_mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri);
|
||||
}
|
||||
|
||||
private void ThenTheMappedRequestHasMethod(string expectedMethod)
|
||||
{
|
||||
_mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user