mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
* #280 can now add response headers inc trace id, now need to consolidate the header place holder stuff * #280 changed port for linux tests * #280 lots of hacking around to handle errors and consolidate placeholders into one class
This commit is contained in:
parent
978b0a43a0
commit
b51df71d7b
@ -3,8 +3,32 @@ Headers Transformation
|
|||||||
|
|
||||||
Ocelot allows the user to transform headers pre and post downstream request. At the moment Ocelot only supports find and replace. This feature was requested `GitHub #190 <https://github.com/TomPallister/Ocelot/issues/190>`_ and I decided that it was going to be useful in various ways.
|
Ocelot allows the user to transform headers pre and post downstream request. At the moment Ocelot only supports find and replace. This feature was requested `GitHub #190 <https://github.com/TomPallister/Ocelot/issues/190>`_ and I decided that it was going to be useful in various ways.
|
||||||
|
|
||||||
Syntax
|
Add to Response
|
||||||
^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
This feature was requested in `GitHub #280 <https://github.com/TomPallister/Ocelot/issues/280>`_. I have only implemented
|
||||||
|
for responses but could add for requests in the future.
|
||||||
|
|
||||||
|
If you want to add a header to your downstream response please add the following to a ReRoute in configuration.json..
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"DownstreamHeaderTransform": {
|
||||||
|
"Uncle": "Bob"
|
||||||
|
},
|
||||||
|
|
||||||
|
In the example above a header with the key Uncle and value Bob would be returned by Ocelot when requesting the specific ReRoute.
|
||||||
|
|
||||||
|
If you want to return the Butterfly APM trace id then do something like the following..
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"DownstreamHeaderTransform": {
|
||||||
|
"AnyKey": "{TraceId}"
|
||||||
|
},
|
||||||
|
|
||||||
|
Find and Replace
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
In order to transform a header first we specify the header key and then the type of transform we want e.g.
|
In order to transform a header first we specify the header key and then the type of transform we want e.g.
|
||||||
|
|
||||||
@ -43,6 +67,7 @@ Ocelot allows placeholders that can be used in header transformation.
|
|||||||
|
|
||||||
{BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value.
|
{BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value.
|
||||||
{DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment.
|
{DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment.
|
||||||
|
{TraceId} - This will use the Butterfly APM Trace Id. This only works for DownstreamHeaderTransform at the moment.
|
||||||
|
|
||||||
Handling 302 Redirects
|
Handling 302 Redirects
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Builder
|
namespace Ocelot.Configuration.Builder
|
||||||
{
|
{
|
||||||
@ -37,11 +38,13 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private string _upstreamHost;
|
private string _upstreamHost;
|
||||||
private string _key;
|
private string _key;
|
||||||
private List<string> _delegatingHandlers;
|
private List<string> _delegatingHandlers;
|
||||||
|
private List<AddHeader> _addHeadersToDownstream;
|
||||||
|
|
||||||
public DownstreamReRouteBuilder()
|
public DownstreamReRouteBuilder()
|
||||||
{
|
{
|
||||||
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
||||||
_delegatingHandlers = new List<string>();
|
_delegatingHandlers = new List<string>();
|
||||||
|
_addHeadersToDownstream = new List<AddHeader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
|
public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
|
||||||
@ -224,6 +227,12 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithAddHeadersToDownstream(List<AddHeader> addHeadersToDownstream)
|
||||||
|
{
|
||||||
|
_addHeadersToDownstream = addHeadersToDownstream;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public DownstreamReRoute Build()
|
public DownstreamReRoute Build()
|
||||||
{
|
{
|
||||||
return new DownstreamReRoute(
|
return new DownstreamReRoute(
|
||||||
@ -253,7 +262,8 @@ namespace Ocelot.Configuration.Builder
|
|||||||
_authenticationOptions,
|
_authenticationOptions,
|
||||||
new PathTemplate(_downstreamPathTemplate),
|
new PathTemplate(_downstreamPathTemplate),
|
||||||
_reRouteKey,
|
_reRouteKey,
|
||||||
_delegatingHandlers);
|
_delegatingHandlers,
|
||||||
|
_addHeadersToDownstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,6 +213,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
||||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||||
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
|
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
|
||||||
|
.WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
return reRoute;
|
return reRoute;
|
||||||
|
@ -1,20 +1,22 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
{
|
{
|
||||||
public class HeaderFindAndReplaceCreator : IHeaderFindAndReplaceCreator
|
public class HeaderFindAndReplaceCreator : IHeaderFindAndReplaceCreator
|
||||||
{
|
{
|
||||||
private IBaseUrlFinder _finder;
|
private IPlaceholders _placeholders;
|
||||||
private readonly Dictionary<string, Func<string>> _placeholders;
|
private IOcelotLogger _logger;
|
||||||
|
|
||||||
public HeaderFindAndReplaceCreator(IBaseUrlFinder finder)
|
public HeaderFindAndReplaceCreator(IPlaceholders placeholders, IOcelotLoggerFactory factory)
|
||||||
{
|
{
|
||||||
_finder = finder;
|
_logger = factory.CreateLogger<HeaderFindAndReplaceCreator>();;
|
||||||
_placeholders = new Dictionary<string, Func<string>>();
|
_placeholders = placeholders;
|
||||||
_placeholders.Add("{BaseUrl}", () => _finder.Find());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HeaderTransformations Create(FileReRoute fileReRoute)
|
public HeaderTransformations Create(FileReRoute fileReRoute)
|
||||||
@ -24,21 +26,43 @@ namespace Ocelot.Configuration.Creator
|
|||||||
foreach(var input in fileReRoute.UpstreamHeaderTransform)
|
foreach(var input in fileReRoute.UpstreamHeaderTransform)
|
||||||
{
|
{
|
||||||
var hAndr = Map(input);
|
var hAndr = Map(input);
|
||||||
upstream.Add(hAndr);
|
if(!hAndr.IsError)
|
||||||
|
{
|
||||||
|
upstream.Add(hAndr.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError($"Unable to add UpstreamHeaderTransform {input.Key}: {input.Value}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var downstream = new List<HeaderFindAndReplace>();
|
var downstream = new List<HeaderFindAndReplace>();
|
||||||
|
var addHeadersToDownstream = new List<AddHeader>();
|
||||||
|
|
||||||
foreach(var input in fileReRoute.DownstreamHeaderTransform)
|
foreach(var input in fileReRoute.DownstreamHeaderTransform)
|
||||||
{
|
{
|
||||||
var hAndr = Map(input);
|
if(input.Value.Contains(","))
|
||||||
downstream.Add(hAndr);
|
{
|
||||||
|
var hAndr = Map(input);
|
||||||
|
if(!hAndr.IsError)
|
||||||
|
{
|
||||||
|
downstream.Add(hAndr.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError($"Unable to add DownstreamHeaderTransform {input.Key}: {input.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addHeadersToDownstream.Add(new AddHeader(input.Key, input.Value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HeaderTransformations(upstream, downstream);
|
return new HeaderTransformations(upstream, downstream, addHeadersToDownstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HeaderFindAndReplace Map(KeyValuePair<string,string> input)
|
private Response<HeaderFindAndReplace> Map(KeyValuePair<string,string> input)
|
||||||
{
|
{
|
||||||
var findAndReplace = input.Value.Split(",");
|
var findAndReplace = input.Value.Split(",");
|
||||||
|
|
||||||
@ -51,16 +75,19 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
var placeholder = replace.Substring(startOfPlaceholder, startOfPlaceholder + (endOfPlaceholder + 1));
|
var placeholder = replace.Substring(startOfPlaceholder, startOfPlaceholder + (endOfPlaceholder + 1));
|
||||||
|
|
||||||
if(_placeholders.ContainsKey(placeholder))
|
var value = _placeholders.Get(placeholder);
|
||||||
|
|
||||||
|
if(value.IsError)
|
||||||
{
|
{
|
||||||
var value = _placeholders[placeholder].Invoke();
|
return new ErrorResponse<HeaderFindAndReplace>(value.Errors);
|
||||||
replace = replace.Replace(placeholder, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replace = replace.Replace(placeholder, value.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hAndr = new HeaderFindAndReplace(input.Key, findAndReplace[0], replace, 0);
|
var hAndr = new HeaderFindAndReplace(input.Key, findAndReplace[0], replace, 0);
|
||||||
|
|
||||||
return hAndr;
|
return new OkResponse<HeaderFindAndReplace>(hAndr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,14 +4,31 @@ namespace Ocelot.Configuration.Creator
|
|||||||
{
|
{
|
||||||
public class HeaderTransformations
|
public class HeaderTransformations
|
||||||
{
|
{
|
||||||
public HeaderTransformations(List<HeaderFindAndReplace> upstream, List<HeaderFindAndReplace> downstream)
|
public HeaderTransformations(
|
||||||
|
List<HeaderFindAndReplace> upstream,
|
||||||
|
List<HeaderFindAndReplace> downstream,
|
||||||
|
List<AddHeader> addHeader)
|
||||||
{
|
{
|
||||||
|
AddHeadersToDownstream = addHeader;
|
||||||
Upstream = upstream;
|
Upstream = upstream;
|
||||||
Downstream = downstream;
|
Downstream = downstream;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<HeaderFindAndReplace> Upstream {get;private set;}
|
public List<HeaderFindAndReplace> Upstream { get; private set; }
|
||||||
|
|
||||||
public List<HeaderFindAndReplace> Downstream {get;private set;}
|
public List<HeaderFindAndReplace> Downstream { get; private set; }
|
||||||
|
public List<AddHeader> AddHeadersToDownstream {get;private set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddHeader
|
||||||
|
{
|
||||||
|
public AddHeader(string key, string value)
|
||||||
|
{
|
||||||
|
this.Key = key;
|
||||||
|
this.Value = value;
|
||||||
|
|
||||||
|
}
|
||||||
|
public string Key { get; private set; }
|
||||||
|
public string Value { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
namespace Ocelot.Configuration
|
namespace Ocelot.Configuration
|
||||||
@ -32,8 +33,10 @@ namespace Ocelot.Configuration
|
|||||||
AuthenticationOptions authenticationOptions,
|
AuthenticationOptions authenticationOptions,
|
||||||
PathTemplate downstreamPathTemplate,
|
PathTemplate downstreamPathTemplate,
|
||||||
string reRouteKey,
|
string reRouteKey,
|
||||||
List<string> delegatingHandlers)
|
List<string> delegatingHandlers,
|
||||||
|
List<AddHeader> addHeadersToDownstream)
|
||||||
{
|
{
|
||||||
|
AddHeadersToDownstream = addHeadersToDownstream;
|
||||||
DelegatingHandlers = delegatingHandlers;
|
DelegatingHandlers = delegatingHandlers;
|
||||||
Key = key;
|
Key = key;
|
||||||
UpstreamPathTemplate = upstreamPathTemplate;
|
UpstreamPathTemplate = upstreamPathTemplate;
|
||||||
@ -90,5 +93,6 @@ namespace Ocelot.Configuration
|
|||||||
public PathTemplate DownstreamPathTemplate { get; private set; }
|
public PathTemplate DownstreamPathTemplate { get; private set; }
|
||||||
public string ReRouteKey { get; private set; }
|
public string ReRouteKey { get; private set; }
|
||||||
public List<string> DelegatingHandlers {get;private set;}
|
public List<string> DelegatingHandlers {get;private set;}
|
||||||
|
public List<AddHeader> AddHeadersToDownstream {get;private set;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ namespace Ocelot.DependencyInjection
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Butterfly.Client.AspNetCore;
|
using Butterfly.Client.AspNetCore;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
|
||||||
public class OcelotBuilder : IOcelotBuilder
|
public class OcelotBuilder : IOcelotBuilder
|
||||||
{
|
{
|
||||||
@ -149,6 +150,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
// We add this here so that we can always inject something into the factory for IoC..
|
// We add this here so that we can always inject something into the factory for IoC..
|
||||||
_services.AddSingleton<IServiceTracer, FakeServiceTracer>();
|
_services.AddSingleton<IServiceTracer, FakeServiceTracer>();
|
||||||
_services.TryAddSingleton<IConsulPollerConfiguration, InMemoryConsulPollerConfiguration>();
|
_services.TryAddSingleton<IConsulPollerConfiguration, InMemoryConsulPollerConfiguration>();
|
||||||
|
_services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
|
||||||
|
_services.TryAddSingleton<IPlaceholders, Placeholders>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
RateLimitOptionsError,
|
RateLimitOptionsError,
|
||||||
PathTemplateDoesntStartWithForwardSlash,
|
PathTemplateDoesntStartWithForwardSlash,
|
||||||
FileValidationFailedError,
|
FileValidationFailedError,
|
||||||
UnableToFindDelegatingHandlerProviderError
|
UnableToFindDelegatingHandlerProviderError,
|
||||||
|
CouldNotFindPlaceholderError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ using Ocelot.Configuration;
|
|||||||
using Ocelot.Infrastructure.Claims.Parser;
|
using Ocelot.Infrastructure.Claims.Parser;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
|
||||||
namespace Ocelot.Headers
|
namespace Ocelot.Headers
|
||||||
{
|
{
|
||||||
|
44
src/Ocelot/Headers/AddHeadersToResponse.cs
Normal file
44
src/Ocelot/Headers/AddHeadersToResponse.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
namespace Ocelot.Headers
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
|
||||||
|
public class AddHeadersToResponse : IAddHeadersToResponse
|
||||||
|
{
|
||||||
|
private IPlaceholders _placeholders;
|
||||||
|
private IOcelotLogger _logger;
|
||||||
|
|
||||||
|
public AddHeadersToResponse(IPlaceholders placeholders, IOcelotLoggerFactory factory)
|
||||||
|
{
|
||||||
|
_logger = factory.CreateLogger<AddHeadersToResponse>();
|
||||||
|
_placeholders = placeholders;
|
||||||
|
}
|
||||||
|
public void Add(List<AddHeader> addHeaders, HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
foreach(var add in addHeaders)
|
||||||
|
{
|
||||||
|
if(add.Value.StartsWith('{') && add.Value.EndsWith('}'))
|
||||||
|
{
|
||||||
|
var value = _placeholders.Get(add.Value);
|
||||||
|
|
||||||
|
if(value.IsError)
|
||||||
|
{
|
||||||
|
_logger.LogError($"Unable to add header to response {add.Key}: {add.Value}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Headers.TryAddWithoutValidation(add.Key, value.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Headers.TryAddWithoutValidation(add.Key, add.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
using Ocelot.Infrastructure.Extensions;
|
using Ocelot.Infrastructure.Extensions;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
@ -10,24 +11,14 @@ namespace Ocelot.Headers
|
|||||||
{
|
{
|
||||||
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
|
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
|
||||||
{
|
{
|
||||||
private Dictionary<string, Func<HttpRequestMessage, string>> _placeholders;
|
private IPlaceholders _placeholders;
|
||||||
|
|
||||||
public HttpResponseHeaderReplacer()
|
public HttpResponseHeaderReplacer(IPlaceholders placeholders)
|
||||||
{
|
{
|
||||||
_placeholders = new Dictionary<string, Func<HttpRequestMessage, string>>();
|
_placeholders = placeholders;
|
||||||
_placeholders.Add("{DownstreamBaseUrl}", x => {
|
|
||||||
var downstreamUrl = $"{x.RequestUri.Scheme}://{x.RequestUri.Host}";
|
|
||||||
|
|
||||||
if(x.RequestUri.Port != 80 && x.RequestUri.Port != 443)
|
|
||||||
{
|
|
||||||
downstreamUrl = $"{downstreamUrl}:{x.RequestUri.Port}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{downstreamUrl}/";
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs, HttpRequestMessage httpRequestMessage)
|
public Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs, HttpRequestMessage request)
|
||||||
{
|
{
|
||||||
foreach (var f in fAndRs)
|
foreach (var f in fAndRs)
|
||||||
{
|
{
|
||||||
@ -35,11 +26,13 @@ namespace Ocelot.Headers
|
|||||||
if(response.Headers.TryGetValues(f.Key, out var values))
|
if(response.Headers.TryGetValues(f.Key, out var values))
|
||||||
{
|
{
|
||||||
//check to see if it is a placeholder in the find...
|
//check to see if it is a placeholder in the find...
|
||||||
if(_placeholders.TryGetValue(f.Find, out var replacePlaceholder))
|
var placeholderValue = _placeholders.Get(f.Find, request);
|
||||||
|
|
||||||
|
if(!placeholderValue.IsError)
|
||||||
{
|
{
|
||||||
//if it is we need to get the value of the placeholder
|
//if it is we need to get the value of the placeholder
|
||||||
var find = replacePlaceholder(httpRequestMessage);
|
//var find = replacePlaceholder(httpRequestMessage);
|
||||||
var replaced = values.ToList()[f.Index].Replace(find, f.Replace.LastCharAsForwardSlash());
|
var replaced = values.ToList()[f.Index].Replace(placeholderValue.Data, f.Replace.LastCharAsForwardSlash());
|
||||||
response.Headers.Remove(f.Key);
|
response.Headers.Remove(f.Key);
|
||||||
response.Headers.Add(f.Key, replaced);
|
response.Headers.Add(f.Key, replaced);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
public interface IAddHeadersToRequest
|
public interface IAddHeadersToRequest
|
||||||
|
11
src/Ocelot/Headers/IAddHeadersToResponse.cs
Normal file
11
src/Ocelot/Headers/IAddHeadersToResponse.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Ocelot.Headers
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
|
||||||
|
public interface IAddHeadersToResponse
|
||||||
|
{
|
||||||
|
void Add(List<AddHeader> addHeaders, HttpResponseMessage response);
|
||||||
|
}
|
||||||
|
}
|
@ -13,12 +13,15 @@ namespace Ocelot.Headers.Middleware
|
|||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IHttpContextRequestHeaderReplacer _preReplacer;
|
private readonly IHttpContextRequestHeaderReplacer _preReplacer;
|
||||||
private readonly IHttpResponseHeaderReplacer _postReplacer;
|
private readonly IHttpResponseHeaderReplacer _postReplacer;
|
||||||
|
private readonly IAddHeadersToResponse _addHeaders;
|
||||||
|
|
||||||
public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next,
|
public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IHttpContextRequestHeaderReplacer preReplacer,
|
IHttpContextRequestHeaderReplacer preReplacer,
|
||||||
IHttpResponseHeaderReplacer postReplacer)
|
IHttpResponseHeaderReplacer postReplacer,
|
||||||
|
IAddHeadersToResponse addHeaders)
|
||||||
{
|
{
|
||||||
|
_addHeaders = addHeaders;
|
||||||
_next = next;
|
_next = next;
|
||||||
_postReplacer = postReplacer;
|
_postReplacer = postReplacer;
|
||||||
_preReplacer = preReplacer;
|
_preReplacer = preReplacer;
|
||||||
@ -37,6 +40,8 @@ namespace Ocelot.Headers.Middleware
|
|||||||
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
||||||
|
|
||||||
_postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
|
_postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
|
||||||
|
|
||||||
|
_addHeaders.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/Ocelot/Infrastructure/CouldNotFindPlaceholderError.cs
Normal file
12
src/Ocelot/Infrastructure/CouldNotFindPlaceholderError.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Ocelot.Errors;
|
||||||
|
|
||||||
|
namespace Ocelot.Infrastructure
|
||||||
|
{
|
||||||
|
public class CouldNotFindPlaceholderError : Error
|
||||||
|
{
|
||||||
|
public CouldNotFindPlaceholderError(string placeholder)
|
||||||
|
: base($"Unable to find placeholder called {placeholder}", OcelotErrorCode.CouldNotFindPlaceholderError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/Ocelot/Infrastructure/IPlaceholders.cs
Normal file
11
src/Ocelot/Infrastructure/IPlaceholders.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Infrastructure
|
||||||
|
{
|
||||||
|
public interface IPlaceholders
|
||||||
|
{
|
||||||
|
Response<string> Get(string key);
|
||||||
|
Response<string> Get(string key, HttpRequestMessage request);
|
||||||
|
}
|
||||||
|
}
|
70
src/Ocelot/Infrastructure/Placeholders.cs
Normal file
70
src/Ocelot/Infrastructure/Placeholders.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Infrastructure
|
||||||
|
{
|
||||||
|
public class Placeholders : IPlaceholders
|
||||||
|
{
|
||||||
|
private Dictionary<string, Func<Response<string>>> _placeholders;
|
||||||
|
private Dictionary<string, Func<HttpRequestMessage, string>> _requestPlaceholders;
|
||||||
|
private readonly IBaseUrlFinder _finder;
|
||||||
|
private readonly IRequestScopedDataRepository _repo;
|
||||||
|
|
||||||
|
public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo)
|
||||||
|
{
|
||||||
|
_repo = repo;
|
||||||
|
_finder = finder;
|
||||||
|
_placeholders = new Dictionary<string, Func<Response<string>>>();
|
||||||
|
_placeholders.Add("{BaseUrl}", () => new OkResponse<string>(_finder.Find()));
|
||||||
|
_placeholders.Add("{TraceId}", () => {
|
||||||
|
var traceId = _repo.Get<string>("TraceId");
|
||||||
|
if(traceId.IsError)
|
||||||
|
{
|
||||||
|
return new ErrorResponse<string>(traceId.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OkResponse<string>(traceId.Data);
|
||||||
|
});
|
||||||
|
|
||||||
|
_requestPlaceholders = new Dictionary<string, Func<HttpRequestMessage, string>>();
|
||||||
|
_requestPlaceholders.Add("{DownstreamBaseUrl}", x => {
|
||||||
|
var downstreamUrl = $"{x.RequestUri.Scheme}://{x.RequestUri.Host}";
|
||||||
|
|
||||||
|
if(x.RequestUri.Port != 80 && x.RequestUri.Port != 443)
|
||||||
|
{
|
||||||
|
downstreamUrl = $"{downstreamUrl}:{x.RequestUri.Port}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{downstreamUrl}/";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response<string> Get(string key)
|
||||||
|
{
|
||||||
|
if(_placeholders.ContainsKey(key))
|
||||||
|
{
|
||||||
|
var response = _placeholders[key].Invoke();
|
||||||
|
if(!response.IsError)
|
||||||
|
{
|
||||||
|
return new OkResponse<string>(response.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response<string> Get(string key, HttpRequestMessage request)
|
||||||
|
{
|
||||||
|
if(_requestPlaceholders.ContainsKey(key))
|
||||||
|
{
|
||||||
|
return new OkResponse<string>(_requestPlaceholders[key].Invoke(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,26 +5,37 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Butterfly.Client.Tracing;
|
using Butterfly.Client.Tracing;
|
||||||
using Butterfly.OpenTracing;
|
using Butterfly.OpenTracing;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
public class OcelotHttpTracingHandler : DelegatingHandler, ITracingHandler
|
public class OcelotHttpTracingHandler : DelegatingHandler, ITracingHandler
|
||||||
{
|
{
|
||||||
private readonly IServiceTracer _tracer;
|
private readonly IServiceTracer _tracer;
|
||||||
|
private readonly IRequestScopedDataRepository _repo;
|
||||||
private const string prefix_spanId = "ot-spanId";
|
private const string prefix_spanId = "ot-spanId";
|
||||||
|
|
||||||
public OcelotHttpTracingHandler(IServiceTracer tracer, HttpMessageHandler httpMessageHandler = null)
|
public OcelotHttpTracingHandler(
|
||||||
|
IServiceTracer tracer,
|
||||||
|
IRequestScopedDataRepository repo,
|
||||||
|
HttpMessageHandler httpMessageHandler = null)
|
||||||
{
|
{
|
||||||
|
_repo = repo;
|
||||||
_tracer = tracer ?? throw new ArgumentNullException(nameof(tracer));
|
_tracer = tracer ?? throw new ArgumentNullException(nameof(tracer));
|
||||||
InnerHandler = httpMessageHandler ?? new HttpClientHandler();
|
InnerHandler = httpMessageHandler ?? new HttpClientHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
protected override Task<HttpResponseMessage> SendAsync(
|
||||||
|
HttpRequestMessage request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return _tracer.ChildTraceAsync($"httpclient {request.Method}", DateTimeOffset.UtcNow, span => TracingSendAsync(span, request, cancellationToken));
|
return _tracer.ChildTraceAsync($"httpclient {request.Method}", DateTimeOffset.UtcNow, span => TracingSendAsync(span, request, cancellationToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual async Task<HttpResponseMessage> TracingSendAsync(ISpan span, HttpRequestMessage request, CancellationToken cancellationToken)
|
protected virtual async Task<HttpResponseMessage> TracingSendAsync(
|
||||||
|
ISpan span,
|
||||||
|
HttpRequestMessage request,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
IEnumerable<string> traceIdVals = null;
|
IEnumerable<string> traceIdVals = null;
|
||||||
if (request.Headers.TryGetValues(prefix_spanId, out traceIdVals))
|
if (request.Headers.TryGetValues(prefix_spanId, out traceIdVals))
|
||||||
@ -33,6 +44,8 @@ namespace Ocelot.Requester
|
|||||||
request.Headers.TryAddWithoutValidation(prefix_spanId, span.SpanContext.SpanId);
|
request.Headers.TryAddWithoutValidation(prefix_spanId, span.SpanContext.SpanId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_repo.Add("TraceId", span.SpanContext.TraceId);
|
||||||
|
|
||||||
span.Tags.Client().Component("HttpClient")
|
span.Tags.Client().Component("HttpClient")
|
||||||
.HttpMethod(request.Method.Method)
|
.HttpMethod(request.Method.Method)
|
||||||
.HttpUrl(request.RequestUri.OriginalString)
|
.HttpUrl(request.RequestUri.OriginalString)
|
||||||
|
@ -1,20 +1,25 @@
|
|||||||
using Butterfly.Client.Tracing;
|
using Butterfly.Client.Tracing;
|
||||||
using Butterfly.OpenTracing;
|
using Butterfly.OpenTracing;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
public class TracingHandlerFactory : ITracingHandlerFactory
|
public class TracingHandlerFactory : ITracingHandlerFactory
|
||||||
{
|
{
|
||||||
private readonly IServiceTracer _tracer;
|
private readonly IServiceTracer _tracer;
|
||||||
|
private readonly IRequestScopedDataRepository _repo;
|
||||||
|
|
||||||
public TracingHandlerFactory(IServiceTracer tracer)
|
public TracingHandlerFactory(
|
||||||
|
IServiceTracer tracer,
|
||||||
|
IRequestScopedDataRepository repo)
|
||||||
{
|
{
|
||||||
|
_repo = repo;
|
||||||
_tracer = tracer;
|
_tracer = tracer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ITracingHandler Get()
|
public ITracingHandler Get()
|
||||||
{
|
{
|
||||||
return new OcelotHttpTracingHandler(_tracer);
|
return new OcelotHttpTracingHandler(_tracer, _repo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
new FileHostAndPort
|
new FileHostAndPort
|
||||||
{
|
{
|
||||||
Host = "localhost",
|
Host = "localhost",
|
||||||
Port = 51888,
|
Port = 51388,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpstreamPathTemplate = "/api002/values",
|
UpstreamPathTemplate = "/api002/values",
|
||||||
@ -92,7 +92,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
var butterflyUrl = "http://localhost:9618";
|
var butterflyUrl = "http://localhost:9618";
|
||||||
|
|
||||||
this.Given(x => GivenServiceOneIsRunning("http://localhost:51887", "/api/values", 200, "Hello from Laura", butterflyUrl))
|
this.Given(x => GivenServiceOneIsRunning("http://localhost:51887", "/api/values", 200, "Hello from Laura", butterflyUrl))
|
||||||
.And(x => GivenServiceTwoIsRunning("http://localhost:51888", "/api/values", 200, "Hello from Tom", butterflyUrl))
|
.And(x => GivenServiceTwoIsRunning("http://localhost:51388", "/api/values", 200, "Hello from Tom", butterflyUrl))
|
||||||
.And(x => GivenFakeButterfly(butterflyUrl))
|
.And(x => GivenFakeButterfly(butterflyUrl))
|
||||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
.And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl))
|
.And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl))
|
||||||
@ -109,6 +109,60 @@ namespace Ocelot.AcceptanceTests
|
|||||||
commandOnAllStateMachines.ShouldBeTrue();
|
commandOnAllStateMachines.ShouldBeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_tracing_header()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/api/values",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 51387,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/api001/values",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||||
|
{
|
||||||
|
UseTracing = true
|
||||||
|
},
|
||||||
|
QoSOptions = new FileQoSOptions
|
||||||
|
{
|
||||||
|
ExceptionsAllowedBeforeBreaking = 3,
|
||||||
|
DurationOfBreak = 10,
|
||||||
|
TimeoutValue = 5000
|
||||||
|
},
|
||||||
|
DownstreamHeaderTransform = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{"Trace-Id", "{TraceId}"},
|
||||||
|
{"Tom", "Laura"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var butterflyUrl = "http://localhost:9618";
|
||||||
|
|
||||||
|
this.Given(x => GivenServiceOneIsRunning("http://localhost:51387", "/api/values", 200, "Hello from Laura", butterflyUrl))
|
||||||
|
.And(x => GivenFakeButterfly(butterflyUrl))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.And(x => _steps.ThenTheTraceHeaderIsSet("Trace-Id"))
|
||||||
|
.And(x => _steps.ThenTheResponseHeaderIs("Tom", "Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl)
|
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl)
|
||||||
{
|
{
|
||||||
_serviceOneBuilder = new WebHostBuilder()
|
_serviceOneBuilder = new WebHostBuilder()
|
||||||
|
@ -318,6 +318,12 @@ namespace Ocelot.AcceptanceTests
|
|||||||
header.First().ShouldBe(value);
|
header.First().ShouldBe(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ThenTheTraceHeaderIsSet(string key)
|
||||||
|
{
|
||||||
|
var header = _response.Headers.GetValues(key);
|
||||||
|
header.First().ShouldNotBeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public void GivenOcelotIsRunningUsingJsonSerializedCache()
|
public void GivenOcelotIsRunningUsingJsonSerializedCache()
|
||||||
{
|
{
|
||||||
_webHostBuilder = new WebHostBuilder();
|
_webHostBuilder = new WebHostBuilder();
|
||||||
|
@ -34,7 +34,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
_fileConfig = new FileConfiguration();
|
_fileConfig = new FileConfiguration();
|
||||||
_config = new Mock<IConsulPollerConfiguration>();
|
_config = new Mock<IConsulPollerConfiguration>();
|
||||||
_repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse<FileConfiguration>(_fileConfig));
|
_repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse<FileConfiguration>(_fileConfig));
|
||||||
_config.Setup(x => x.Delay).Returns(10);
|
_config.Setup(x => x.Delay).Returns(100);
|
||||||
_poller = new ConsulFileConfigurationPoller(_factory.Object, _repo.Object, _setter.Object, _config.Object);
|
_poller = new ConsulFileConfigurationPoller(_factory.Object, _repo.Object, _setter.Object, _config.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,6 +827,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
result.DownstreamReRoute[0].ClaimsToQueries.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToQueries.Count);
|
result.DownstreamReRoute[0].ClaimsToQueries.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToQueries.Count);
|
||||||
result.DownstreamReRoute[0].RequestIdKey.ShouldBe(expected.DownstreamReRoute[0].RequestIdKey);
|
result.DownstreamReRoute[0].RequestIdKey.ShouldBe(expected.DownstreamReRoute[0].RequestIdKey);
|
||||||
result.DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DownstreamReRoute[0].DelegatingHandlers);
|
result.DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DownstreamReRoute[0].DelegatingHandlers);
|
||||||
|
result.DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToDownstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -909,7 +910,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
private void GivenTheHeaderFindAndReplaceCreatorReturns()
|
private void GivenTheHeaderFindAndReplaceCreatorReturns()
|
||||||
{
|
{
|
||||||
_headerFindAndReplaceCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(new HeaderTransformations(new List<HeaderFindAndReplace>(), new List<HeaderFindAndReplace>()));
|
_headerFindAndReplaceCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(new HeaderTransformations(new List<HeaderFindAndReplace>(), new List<HeaderFindAndReplace>(), new List<AddHeader>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration)
|
private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration)
|
||||||
|
@ -5,7 +5,11 @@ using Ocelot.Configuration;
|
|||||||
using Ocelot.Configuration.Builder;
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Ocelot.UnitTests.Responder;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -17,12 +21,17 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
private HeaderFindAndReplaceCreator _creator;
|
private HeaderFindAndReplaceCreator _creator;
|
||||||
private FileReRoute _reRoute;
|
private FileReRoute _reRoute;
|
||||||
private HeaderTransformations _result;
|
private HeaderTransformations _result;
|
||||||
private Mock<IBaseUrlFinder> _finder;
|
private Mock<IPlaceholders> _placeholders;
|
||||||
|
private Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private Mock<IOcelotLogger> _logger;
|
||||||
|
|
||||||
public HeaderFindAndReplaceCreatorTests()
|
public HeaderFindAndReplaceCreatorTests()
|
||||||
{
|
{
|
||||||
_finder = new Mock<IBaseUrlFinder>();
|
_logger = new Mock<IOcelotLogger>();
|
||||||
_creator = new HeaderFindAndReplaceCreator(_finder.Object);
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_factory.Setup(x => x.CreateLogger<HeaderFindAndReplaceCreator>()).Returns(_logger.Object);
|
||||||
|
_placeholders = new Mock<IPlaceholders>();
|
||||||
|
_creator = new HeaderFindAndReplaceCreator(_placeholders.Object, _factory.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -84,6 +93,40 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_log_errors_and_not_add_headers()
|
||||||
|
{
|
||||||
|
var reRoute = new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamHeaderTransform = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{"Location", "http://www.bbc.co.uk/, {BaseUrl}"},
|
||||||
|
},
|
||||||
|
UpstreamHeaderTransform = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{"Location", "http://www.bbc.co.uk/, {BaseUrl}"},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = new List<HeaderFindAndReplace>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => GivenTheReRoute(reRoute))
|
||||||
|
.And(x => GivenTheBaseUrlErrors())
|
||||||
|
.When(x => WhenICreate())
|
||||||
|
.Then(x => ThenTheFollowingDownstreamIsReturned(expected))
|
||||||
|
.And(x => ThenTheFollowingUpstreamIsReturned(expected))
|
||||||
|
.And(x => ThenTheLoggerIsCalledCorrectly("Unable to add DownstreamHeaderTransform Location: http://www.bbc.co.uk/, {BaseUrl}"))
|
||||||
|
.And(x => ThenTheLoggerIsCalledCorrectly("Unable to add UpstreamHeaderTransform Location: http://www.bbc.co.uk/, {BaseUrl}"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheLoggerIsCalledCorrectly(string message)
|
||||||
|
{
|
||||||
|
_logger.Verify(x => x.LogError(message), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_use_base_url_partial_placeholder()
|
public void should_use_base_url_partial_placeholder()
|
||||||
{
|
{
|
||||||
@ -107,9 +150,41 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_add_trace_id_header()
|
||||||
|
{
|
||||||
|
var reRoute = new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamHeaderTransform = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{"Trace-Id", "{TraceId}"},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = new AddHeader("Trace-Id", "{TraceId}");
|
||||||
|
|
||||||
|
this.Given(x => GivenTheReRoute(reRoute))
|
||||||
|
.And(x => GivenTheBaseUrlIs("http://ocelot.com/"))
|
||||||
|
.When(x => WhenICreate())
|
||||||
|
.Then(x => ThenTheFollowingAddHeaderIsReturned(expected))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenTheBaseUrlIs(string baseUrl)
|
private void GivenTheBaseUrlIs(string baseUrl)
|
||||||
{
|
{
|
||||||
_finder.Setup(x => x.Find()).Returns(baseUrl);
|
_placeholders.Setup(x => x.Get(It.IsAny<string>())).Returns(new OkResponse<string>(baseUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheBaseUrlErrors()
|
||||||
|
{
|
||||||
|
_placeholders.Setup(x => x.Get(It.IsAny<string>())).Returns(new ErrorResponse<string>(new AnyError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheFollowingAddHeaderIsReturned(AddHeader addHeader)
|
||||||
|
{
|
||||||
|
_result.AddHeadersToDownstream[0].Key.ShouldBe(addHeader.Key);
|
||||||
|
_result.AddHeadersToDownstream[0].Value.ShouldBe(addHeader.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheFollowingDownstreamIsReturned(List<HeaderFindAndReplace> downstream)
|
private void ThenTheFollowingDownstreamIsReturned(List<HeaderFindAndReplace> downstream)
|
||||||
|
148
test/Ocelot.UnitTests/Headers/AddHeadersToResponseTests.cs
Normal file
148
test/Ocelot.UnitTests/Headers/AddHeadersToResponseTests.cs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
using Xunit;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Ocelot.Headers;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
using System.Linq;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.UnitTests.Responder;
|
||||||
|
using System;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Headers
|
||||||
|
{
|
||||||
|
public class AddHeadersToResponseTests
|
||||||
|
{
|
||||||
|
private IAddHeadersToResponse _adder;
|
||||||
|
private Mock<IPlaceholders> _placeholders;
|
||||||
|
private HttpResponseMessage _response;
|
||||||
|
private List<AddHeader> _addHeaders;
|
||||||
|
private Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private Mock<IOcelotLogger> _logger;
|
||||||
|
|
||||||
|
public AddHeadersToResponseTests()
|
||||||
|
{
|
||||||
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_logger = new Mock<IOcelotLogger>();
|
||||||
|
_factory.Setup(x => x.CreateLogger<AddHeadersToResponse>()).Returns(_logger.Object);
|
||||||
|
_placeholders = new Mock<IPlaceholders>();
|
||||||
|
_adder = new AddHeadersToResponse(_placeholders.Object, _factory.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_add_header()
|
||||||
|
{
|
||||||
|
var addHeaders = new List<AddHeader>
|
||||||
|
{
|
||||||
|
new AddHeader("Laura", "Tom")
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(_ => GivenAResponseMessage())
|
||||||
|
.And(_ => GivenTheAddHeaders(addHeaders))
|
||||||
|
.When(_ => WhenIAdd())
|
||||||
|
.And(_ => ThenTheHeaderIsReturned("Laura", "Tom"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_add_trace_id_placeholder()
|
||||||
|
{
|
||||||
|
var addHeaders = new List<AddHeader>
|
||||||
|
{
|
||||||
|
new AddHeader("Trace-Id", "{TraceId}")
|
||||||
|
};
|
||||||
|
|
||||||
|
var traceId = "123";
|
||||||
|
|
||||||
|
this.Given(_ => GivenAResponseMessage())
|
||||||
|
.And(_ => GivenTheTraceIdIs(traceId))
|
||||||
|
.And(_ => GivenTheAddHeaders(addHeaders))
|
||||||
|
.When(_ => WhenIAdd())
|
||||||
|
.Then(_ => ThenTheHeaderIsReturned("Trace-Id", traceId))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_add_trace_id_placeholder_and_normal()
|
||||||
|
{
|
||||||
|
var addHeaders = new List<AddHeader>
|
||||||
|
{
|
||||||
|
new AddHeader("Trace-Id", "{TraceId}"),
|
||||||
|
new AddHeader("Tom", "Laura")
|
||||||
|
};
|
||||||
|
|
||||||
|
var traceId = "123";
|
||||||
|
|
||||||
|
this.Given(_ => GivenAResponseMessage())
|
||||||
|
.And(_ => GivenTheTraceIdIs(traceId))
|
||||||
|
.And(_ => GivenTheAddHeaders(addHeaders))
|
||||||
|
.When(_ => WhenIAdd())
|
||||||
|
.Then(_ => ThenTheHeaderIsReturned("Trace-Id", traceId))
|
||||||
|
.Then(_ => ThenTheHeaderIsReturned("Tom", "Laura"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_do_nothing_and_log_error()
|
||||||
|
{
|
||||||
|
var addHeaders = new List<AddHeader>
|
||||||
|
{
|
||||||
|
new AddHeader("Trace-Id", "{TraceId}")
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(_ => GivenAResponseMessage())
|
||||||
|
.And(_ => GivenTheTraceIdErrors())
|
||||||
|
.And(_ => GivenTheAddHeaders(addHeaders))
|
||||||
|
.When(_ => WhenIAdd())
|
||||||
|
.Then(_ => ThenTheHeaderIsNotAdded("Trace-Id"))
|
||||||
|
.And(_ => ThenTheErrorIsLogged())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheErrorIsLogged()
|
||||||
|
{
|
||||||
|
_logger.Verify(x => x.LogError("Unable to add header to response Trace-Id: {TraceId}"), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheHeaderIsNotAdded(string key)
|
||||||
|
{
|
||||||
|
_response.Headers.TryGetValues(key, out var values).ShouldBeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheTraceIdIs(string traceId)
|
||||||
|
{
|
||||||
|
_placeholders.Setup(x => x.Get("{TraceId}")).Returns(new OkResponse<string>(traceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheTraceIdErrors()
|
||||||
|
{
|
||||||
|
_placeholders.Setup(x => x.Get("{TraceId}")).Returns(new ErrorResponse<string>(new AnyError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheHeaderIsReturned(string key, string value)
|
||||||
|
{
|
||||||
|
var values = _response.Headers.GetValues(key);
|
||||||
|
values.First().ShouldBe(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIAdd()
|
||||||
|
{
|
||||||
|
_adder.Add(_addHeaders, _response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenAResponseMessage()
|
||||||
|
{
|
||||||
|
_response = new HttpResponseMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheAddHeaders(List<AddHeader> addHeaders)
|
||||||
|
{
|
||||||
|
_addHeaders = addHeaders;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
private HttpHeadersTransformationMiddleware _middleware;
|
private HttpHeadersTransformationMiddleware _middleware;
|
||||||
private DownstreamContext _downstreamContext;
|
private DownstreamContext _downstreamContext;
|
||||||
private OcelotRequestDelegate _next;
|
private OcelotRequestDelegate _next;
|
||||||
|
private Mock<IAddHeadersToResponse> _addHeaders;
|
||||||
|
|
||||||
public HttpHeadersTransformationMiddlewareTests()
|
public HttpHeadersTransformationMiddlewareTests()
|
||||||
{
|
{
|
||||||
@ -36,7 +37,8 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
_logger = new Mock<IOcelotLogger>();
|
_logger = new Mock<IOcelotLogger>();
|
||||||
_loggerFactory.Setup(x => x.CreateLogger<AuthorisationMiddleware>()).Returns(_logger.Object);
|
_loggerFactory.Setup(x => x.CreateLogger<AuthorisationMiddleware>()).Returns(_logger.Object);
|
||||||
_next = context => Task.CompletedTask;
|
_next = context => Task.CompletedTask;
|
||||||
_middleware = new HttpHeadersTransformationMiddleware(_next, _loggerFactory.Object, _preReplacer.Object, _postReplacer.Object);
|
_addHeaders = new Mock<IAddHeadersToResponse>();
|
||||||
|
_middleware = new HttpHeadersTransformationMiddleware(_next, _loggerFactory.Object, _preReplacer.Object, _postReplacer.Object, _addHeaders.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -49,9 +51,16 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
.When(x => WhenICallTheMiddleware())
|
.When(x => WhenICallTheMiddleware())
|
||||||
.Then(x => ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly())
|
.Then(x => ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly())
|
||||||
.And(x => ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly())
|
.And(x => ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly())
|
||||||
|
.And(x => ThenAddHeadersIsCalledCorrectly())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenAddHeadersIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_addHeaders
|
||||||
|
.Verify(x => x.Add(_downstreamContext.DownstreamReRoute.AddHeadersToDownstream, _downstreamContext.DownstreamResponse), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
private void WhenICallTheMiddleware()
|
private void WhenICallTheMiddleware()
|
||||||
{
|
{
|
||||||
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
|
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
|
||||||
|
@ -7,20 +7,30 @@ using Ocelot.Configuration;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Headers
|
namespace Ocelot.UnitTests.Headers
|
||||||
{
|
{
|
||||||
public class HttpResponseHeaderReplacerTests
|
public class HttpResponseHeaderReplacerTests
|
||||||
{
|
{
|
||||||
private HttpResponseMessage _response;
|
private HttpResponseMessage _response;
|
||||||
|
private Placeholders _placeholders;
|
||||||
private HttpResponseHeaderReplacer _replacer;
|
private HttpResponseHeaderReplacer _replacer;
|
||||||
private List<HeaderFindAndReplace> _headerFindAndReplaces;
|
private List<HeaderFindAndReplace> _headerFindAndReplaces;
|
||||||
private Response _result;
|
private Response _result;
|
||||||
private HttpRequestMessage _request;
|
private HttpRequestMessage _request;
|
||||||
|
private Mock<IBaseUrlFinder> _finder;
|
||||||
|
private Mock<IRequestScopedDataRepository> _repo;
|
||||||
|
|
||||||
public HttpResponseHeaderReplacerTests()
|
public HttpResponseHeaderReplacerTests()
|
||||||
{
|
{
|
||||||
_replacer = new HttpResponseHeaderReplacer();
|
_repo = new Mock<IRequestScopedDataRepository>();
|
||||||
|
_finder = new Mock<IBaseUrlFinder>();
|
||||||
|
_placeholders = new Placeholders(_finder.Object, _repo.Object);
|
||||||
|
_replacer = new HttpResponseHeaderReplacer(_placeholders);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
79
test/Ocelot.UnitTests/Infrastructure/PlaceholdersTests.cs
Normal file
79
test/Ocelot.UnitTests/Infrastructure/PlaceholdersTests.cs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Infrastructure
|
||||||
|
{
|
||||||
|
public class PlaceholdersTests
|
||||||
|
{
|
||||||
|
private IPlaceholders _placeholders;
|
||||||
|
private Mock<IBaseUrlFinder> _finder;
|
||||||
|
private Mock<IRequestScopedDataRepository> _repo;
|
||||||
|
|
||||||
|
public PlaceholdersTests()
|
||||||
|
{
|
||||||
|
_repo = new Mock<IRequestScopedDataRepository>();
|
||||||
|
_finder = new Mock<IBaseUrlFinder>();
|
||||||
|
_placeholders = new Placeholders(_finder.Object, _repo.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_base_url()
|
||||||
|
{
|
||||||
|
var baseUrl = "http://www.bbc.co.uk";
|
||||||
|
_finder.Setup(x => x.Find()).Returns(baseUrl);
|
||||||
|
var result = _placeholders.Get("{BaseUrl}");
|
||||||
|
result.Data.ShouldBe(baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_key_does_not_exist()
|
||||||
|
{
|
||||||
|
var result = _placeholders.Get("{Test}");
|
||||||
|
result.IsError.ShouldBeTrue();
|
||||||
|
result.Errors[0].Message.ShouldBe("Unable to find placeholder called {Test}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_downstream_base_url_when_port_is_not_80_or_443()
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage();
|
||||||
|
request.RequestUri = new Uri("http://www.bbc.co.uk");
|
||||||
|
var result = _placeholders.Get("{DownstreamBaseUrl}", request);
|
||||||
|
result.Data.ShouldBe("http://www.bbc.co.uk/");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_downstream_base_url_when_port_is_80_or_443()
|
||||||
|
{
|
||||||
|
var request = new HttpRequestMessage();
|
||||||
|
request.RequestUri = new Uri("http://www.bbc.co.uk:123");
|
||||||
|
var result = _placeholders.Get("{DownstreamBaseUrl}", request);
|
||||||
|
result.Data.ShouldBe("http://www.bbc.co.uk:123/");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_key_does_not_exist_for_http_request_message()
|
||||||
|
{
|
||||||
|
var result = _placeholders.Get("{Test}", new System.Net.Http.HttpRequestMessage());
|
||||||
|
result.IsError.ShouldBeTrue();
|
||||||
|
result.Errors[0].Message.ShouldBe("Unable to find placeholder called {Test}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_trace_id()
|
||||||
|
{
|
||||||
|
var traceId = "123";
|
||||||
|
_repo.Setup(x => x.Get<string>("TraceId")).Returns(new OkResponse<string>(traceId));
|
||||||
|
var result = _placeholders.Get("{TraceId}");
|
||||||
|
result.Data.ShouldBe(traceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using Butterfly.Client.Tracing;
|
using Butterfly.Client.Tracing;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Requester;
|
using Ocelot.Requester;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -10,11 +11,13 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
{
|
{
|
||||||
private TracingHandlerFactory _factory;
|
private TracingHandlerFactory _factory;
|
||||||
private Mock<IServiceTracer> _tracer;
|
private Mock<IServiceTracer> _tracer;
|
||||||
|
private Mock<IRequestScopedDataRepository> _repo;
|
||||||
|
|
||||||
public TracingHandlerFactoryTests()
|
public TracingHandlerFactoryTests()
|
||||||
{
|
{
|
||||||
_tracer = new Mock<IServiceTracer>();
|
_tracer = new Mock<IServiceTracer>();
|
||||||
_factory = new TracingHandlerFactory(_tracer.Object);
|
_repo = new Mock<IRequestScopedDataRepository>();
|
||||||
|
_factory = new TracingHandlerFactory(_tracer.Object, _repo.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -120,7 +120,7 @@ namespace Ocelot.UnitTests.Responder
|
|||||||
// If this test fails then it's because the number of error codes has changed.
|
// If this test fails then it's because the number of error codes has changed.
|
||||||
// You should make the appropriate changes to the test cases here to ensure
|
// You should make the appropriate changes to the test cases here to ensure
|
||||||
// they cover all the error codes, and then modify this assertion.
|
// they cover all the error codes, and then modify this assertion.
|
||||||
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(33, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
|
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(34, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)
|
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user