mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-10-31 21:15:28 +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:
		| @@ -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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Tom Pallister
					Tom Pallister