mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 22:30:50 +08:00 
			
		
		
		
	Added request id functionality and general refactoring..also turned out i wasnt returning headers....sigh
This commit is contained in:
		@@ -20,6 +20,7 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
        private Dictionary<string, string> _routeClaimRequirement;
 | 
					        private Dictionary<string, string> _routeClaimRequirement;
 | 
				
			||||||
        private bool _isAuthorised;
 | 
					        private bool _isAuthorised;
 | 
				
			||||||
        private List<ClaimToThing> _claimToQueries;
 | 
					        private List<ClaimToThing> _claimToQueries;
 | 
				
			||||||
 | 
					        private string _requestIdHeaderKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ReRouteBuilder()
 | 
					        public ReRouteBuilder()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -96,6 +97,12 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
            return this;
 | 
					            return this;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public ReRouteBuilder WithRequestIdKey(string input)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _requestIdHeaderKey = input;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ReRouteBuilder WithClaimsToHeaders(List<ClaimToThing> input)
 | 
					        public ReRouteBuilder WithClaimsToHeaders(List<ClaimToThing> input)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _configHeaderExtractorProperties = input;
 | 
					            _configHeaderExtractorProperties = input;
 | 
				
			||||||
@@ -122,7 +129,7 @@ namespace Ocelot.Configuration.Builder
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public ReRoute Build()
 | 
					        public ReRoute Build()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, _isAuthorised, _claimToQueries);
 | 
					            return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret), _configHeaderExtractorProperties, _claimToClaims, _routeClaimRequirement, _isAuthorised, _claimToQueries, _requestIdHeaderKey);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -113,12 +113,17 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
 | 
					                return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
 | 
				
			||||||
                    reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
 | 
					                    reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated,
 | 
				
			||||||
                    authOptionsForRoute, claimsToHeaders, claimsToClaims, reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries
 | 
					                    authOptionsForRoute, claimsToHeaders, claimsToClaims, 
 | 
				
			||||||
 | 
					                    reRoute.RouteClaimsRequirement, isAuthorised, claimsToQueries,
 | 
				
			||||||
 | 
					                    reRoute.RequestIdKey
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod,
 | 
					            return new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, 
 | 
				
			||||||
                upstreamTemplate, isAuthenticated, null, new List<ClaimToThing>(), new List<ClaimToThing>(), reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>());
 | 
					                reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, 
 | 
				
			||||||
 | 
					                null, new List<ClaimToThing>(), new List<ClaimToThing>(), 
 | 
				
			||||||
 | 
					                reRoute.RouteClaimsRequirement, isAuthorised, new List<ClaimToThing>(),
 | 
				
			||||||
 | 
					                    reRoute.RequestIdKey);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
 | 
					        private List<ClaimToThing> GetAddThingsToRequest(Dictionary<string,string> thingBeingAdded)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class ReRoute
 | 
					    public class ReRoute
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, List<ClaimToThing> configurationHeaderExtractorProperties, List<ClaimToThing> claimsToClaims, Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, List<ClaimToThing> claimsToQueries)
 | 
					        public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions, List<ClaimToThing> configurationHeaderExtractorProperties, List<ClaimToThing> claimsToClaims, Dictionary<string, string> routeClaimsRequirement, bool isAuthorised, List<ClaimToThing> claimsToQueries, string requestIdKey)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            DownstreamTemplate = downstreamTemplate;
 | 
					            DownstreamTemplate = downstreamTemplate;
 | 
				
			||||||
            UpstreamTemplate = upstreamTemplate;
 | 
					            UpstreamTemplate = upstreamTemplate;
 | 
				
			||||||
@@ -14,6 +14,7 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
            AuthenticationOptions = authenticationOptions;
 | 
					            AuthenticationOptions = authenticationOptions;
 | 
				
			||||||
            RouteClaimsRequirement = routeClaimsRequirement;
 | 
					            RouteClaimsRequirement = routeClaimsRequirement;
 | 
				
			||||||
            IsAuthorised = isAuthorised;
 | 
					            IsAuthorised = isAuthorised;
 | 
				
			||||||
 | 
					            RequestIdKey = requestIdKey;
 | 
				
			||||||
            ClaimsToQueries = claimsToQueries
 | 
					            ClaimsToQueries = claimsToQueries
 | 
				
			||||||
                ?? new List<ClaimToThing>();
 | 
					                ?? new List<ClaimToThing>();
 | 
				
			||||||
            ClaimsToClaims = claimsToClaims 
 | 
					            ClaimsToClaims = claimsToClaims 
 | 
				
			||||||
@@ -33,6 +34,6 @@ namespace Ocelot.Configuration
 | 
				
			|||||||
        public List<ClaimToThing> ClaimsToHeaders { get; private set; }
 | 
					        public List<ClaimToThing> ClaimsToHeaders { get; private set; }
 | 
				
			||||||
        public List<ClaimToThing> ClaimsToClaims { get; private set; }
 | 
					        public List<ClaimToThing> ClaimsToClaims { get; private set; }
 | 
				
			||||||
        public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
 | 
					        public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
 | 
				
			||||||
 | 
					        public string RequestIdKey { get; private set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -20,5 +20,6 @@ namespace Ocelot.Configuration.Yaml
 | 
				
			|||||||
        public Dictionary<string, string> AddClaimsToRequest { get; set; }
 | 
					        public Dictionary<string, string> AddClaimsToRequest { get; set; }
 | 
				
			||||||
        public Dictionary<string, string> RouteClaimsRequirement { get; set; }
 | 
					        public Dictionary<string, string> RouteClaimsRequirement { get; set; }
 | 
				
			||||||
        public Dictionary<string, string> AddQueriesToRequest { get; set; }
 | 
					        public Dictionary<string, string> AddQueriesToRequest { get; set; }
 | 
				
			||||||
 | 
					        public string RequestIdKey { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
using Microsoft.AspNetCore.Authorization;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
using Microsoft.Extensions.DependencyInjection;
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
using Ocelot.Authentication.Handler.Creator;
 | 
					using Ocelot.Authentication.Handler.Creator;
 | 
				
			||||||
@@ -16,6 +15,7 @@ using Ocelot.DownstreamRouteFinder.Finder;
 | 
				
			|||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
					using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
				
			||||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
					using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
 | 
				
			||||||
using Ocelot.Headers;
 | 
					using Ocelot.Headers;
 | 
				
			||||||
 | 
					using Ocelot.Infrastructure.Claims.Parser;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.QueryStrings;
 | 
					using Ocelot.QueryStrings;
 | 
				
			||||||
using Ocelot.Request.Builder;
 | 
					using Ocelot.Request.Builder;
 | 
				
			||||||
@@ -24,8 +24,6 @@ using Ocelot.Responder;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.DependencyInjection
 | 
					namespace Ocelot.DependencyInjection
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using Infrastructure.Claims.Parser;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static class ServiceCollectionExtensions
 | 
					    public static class ServiceCollectionExtensions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static IServiceCollection AddOcelotYamlConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot)
 | 
					        public static IServiceCollection AddOcelotYamlConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot)
 | 
				
			||||||
@@ -48,6 +46,7 @@ namespace Ocelot.DependencyInjection
 | 
				
			|||||||
            services.AddLogging();
 | 
					            services.AddLogging();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // ocelot services.
 | 
					            // ocelot services.
 | 
				
			||||||
 | 
					            services.AddSingleton<IRemoveHeaders, RemoveHeaders>();
 | 
				
			||||||
            services.AddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
 | 
					            services.AddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
 | 
				
			||||||
            services.AddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
 | 
					            services.AddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
 | 
				
			||||||
            services.AddSingleton<IAuthoriser, ClaimsAuthoriser>();
 | 
					            services.AddSingleton<IAuthoriser, ClaimsAuthoriser>();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,6 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly RequestDelegate _next;
 | 
					        private readonly RequestDelegate _next;
 | 
				
			||||||
        private readonly IDownstreamRouteFinder _downstreamRouteFinder;
 | 
					        private readonly IDownstreamRouteFinder _downstreamRouteFinder;
 | 
				
			||||||
        private readonly IRequestScopedDataRepository _requestScopedDataRepository;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DownstreamRouteFinderMiddleware(RequestDelegate next, 
 | 
					        public DownstreamRouteFinderMiddleware(RequestDelegate next, 
 | 
				
			||||||
            IDownstreamRouteFinder downstreamRouteFinder, 
 | 
					            IDownstreamRouteFinder downstreamRouteFinder, 
 | 
				
			||||||
@@ -19,7 +18,6 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            _next = next;
 | 
					            _next = next;
 | 
				
			||||||
            _downstreamRouteFinder = downstreamRouteFinder;
 | 
					            _downstreamRouteFinder = downstreamRouteFinder;
 | 
				
			||||||
            _requestScopedDataRepository = requestScopedDataRepository;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task Invoke(HttpContext context)
 | 
					        public async Task Invoke(HttpContext context)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								src/Ocelot/Headers/IRemoveHeaders.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Ocelot/Headers/IRemoveHeaders.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					using System.Net.Http.Headers;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Headers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public interface IRemoveHeaders
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Response Remove(HttpResponseHeaders headers);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								src/Ocelot/Headers/RemoveHeaders.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/Ocelot/Headers/RemoveHeaders.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Net.Http.Headers;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Headers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RemoveHeaders : IRemoveHeaders
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Some webservers return headers that cannot be forwarded to the client
 | 
				
			||||||
 | 
					        /// in a given context such as transfer encoding chunked when ASP.NET is not
 | 
				
			||||||
 | 
					        /// returning the response in this manner
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        private readonly List<string> _unsupportedHeaders = new List<string>
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "Transfer-Encoding"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Response Remove(HttpResponseHeaders headers)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            foreach (var unsupported in _unsupportedHeaders)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                headers.Remove(unsupported);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return new OkResponse();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,7 +10,8 @@ namespace Ocelot.Middleware
 | 
				
			|||||||
        private readonly RequestDelegate _next;
 | 
					        private readonly RequestDelegate _next;
 | 
				
			||||||
        private readonly ILogger _logger;
 | 
					        private readonly ILogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ExceptionHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
 | 
					        public ExceptionHandlerMiddleware(RequestDelegate next, 
 | 
				
			||||||
 | 
					            ILoggerFactory loggerFactory)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _next = next;
 | 
					            _next = next;
 | 
				
			||||||
            _logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
 | 
					            _logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
 | 
				
			||||||
@@ -24,20 +25,25 @@ namespace Ocelot.Middleware
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (Exception e)
 | 
					            catch (Exception e)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var message =
 | 
					                var message = CreateMessage(context, e);
 | 
				
			||||||
                    $"Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (e.InnerException != null)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    message = $"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}";
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                _logger.LogError(new EventId(1, "Ocelot Global Error"), message, e);
 | 
					                _logger.LogError(new EventId(1, "Ocelot Global Error"), message, e);
 | 
				
			||||||
 | 
					 | 
				
			||||||
                context.Response.StatusCode = 500;
 | 
					                context.Response.StatusCode = 500;
 | 
				
			||||||
                context.Response.ContentType = "application/json";
 | 
					                context.Response.ContentType = "application/json";
 | 
				
			||||||
                await context.Response.WriteAsync("Internal Server Error");
 | 
					                await context.Response.WriteAsync("Internal Server Error");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private static string CreateMessage(HttpContext context, Exception e)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var message =
 | 
				
			||||||
 | 
					                $"RequestId: {context.TraceIdentifier}, Exception caught in global error handler, exception message: {e.Message}, exception stack: {e.StackTrace}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (e.InnerException != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                message =
 | 
				
			||||||
 | 
					                    $"{message}, inner exception message {e.InnerException.Message}, inner exception stack {e.InnerException.StackTrace}";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return message;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ using Ocelot.Headers.Middleware;
 | 
				
			|||||||
using Ocelot.QueryStrings.Middleware;
 | 
					using Ocelot.QueryStrings.Middleware;
 | 
				
			||||||
using Ocelot.Request.Middleware;
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Ocelot.Requester.Middleware;
 | 
					using Ocelot.Requester.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.RequestId.Middleware;
 | 
				
			||||||
using Ocelot.Responder.Middleware;
 | 
					using Ocelot.Responder.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Middleware
 | 
					namespace Ocelot.Middleware
 | 
				
			||||||
@@ -49,6 +50,9 @@ namespace Ocelot.Middleware
 | 
				
			|||||||
            // Then we get the downstream route information
 | 
					            // Then we get the downstream route information
 | 
				
			||||||
            builder.UseDownstreamRouteFinderMiddleware();
 | 
					            builder.UseDownstreamRouteFinderMiddleware();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Now we can look for the requestId
 | 
				
			||||||
 | 
					            builder.UseRequestIdMiddleware();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
 | 
					            // Allow pre authentication logic. The idea being people might want to run something custom before what is built in.
 | 
				
			||||||
            builder.UseIfNotNull(middlewareConfiguration.PreAuthenticationMiddleware);
 | 
					            builder.UseIfNotNull(middlewareConfiguration.PreAuthenticationMiddleware);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,9 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Net;
 | 
					using System.Net;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using System.Net.Http.Headers;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
@@ -10,8 +12,15 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class HttpRequestBuilder : IRequestBuilder
 | 
					    public class HttpRequestBuilder : IRequestBuilder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public async Task<Response<Request>> Build(string httpMethod, string downstreamUrl, Stream content, IHeaderDictionary headers,
 | 
					        public async Task<Response<Request>> Build(
 | 
				
			||||||
            IRequestCookieCollection cookies, Microsoft.AspNetCore.Http.QueryString queryString, string contentType)
 | 
					            string httpMethod, 
 | 
				
			||||||
 | 
					            string downstreamUrl, 
 | 
				
			||||||
 | 
					            Stream content, 
 | 
				
			||||||
 | 
					            IHeaderDictionary headers,
 | 
				
			||||||
 | 
					            IRequestCookieCollection cookies, 
 | 
				
			||||||
 | 
					            QueryString queryString, 
 | 
				
			||||||
 | 
					            string contentType, 
 | 
				
			||||||
 | 
					            RequestId.RequestId requestId)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var method = new HttpMethod(httpMethod);
 | 
					            var method = new HttpMethod(httpMethod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,7 +30,7 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (content != null)
 | 
					            if (content != null)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                httpRequestMessage.Content = new ByteArrayContent(ToByteArray(content));
 | 
					                httpRequestMessage.Content = new ByteArrayContent(await ToByteArray(content));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!string.IsNullOrEmpty(contentType))
 | 
					            if (!string.IsNullOrEmpty(contentType))
 | 
				
			||||||
@@ -45,6 +54,11 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (RequestKeyIsNotNull(requestId) && !RequestIdInHeaders(requestId, httpRequestMessage.Headers))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ForwardRequestIdToDownstreamService(requestId, httpRequestMessage);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var cookieContainer = new CookieContainer();
 | 
					            var cookieContainer = new CookieContainer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            //todo get rid of if
 | 
					            //todo get rid of if
 | 
				
			||||||
@@ -59,13 +73,34 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
            return new OkResponse<Request>(new Request(httpRequestMessage, cookieContainer));
 | 
					            return new OkResponse<Request>(new Request(httpRequestMessage, cookieContainer));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private byte[] ToByteArray(Stream stream)
 | 
					        private void ForwardRequestIdToDownstreamService(RequestId.RequestId requestId, HttpRequestMessage httpRequestMessage)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private bool RequestIdInHeaders(RequestId.RequestId requestId, HttpRequestHeaders headers)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            IEnumerable<string> value;
 | 
				
			||||||
 | 
					            if (headers.TryGetValues(requestId.RequestIdKey, out value))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private bool RequestKeyIsNotNull(RequestId.RequestId requestId)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return !string.IsNullOrEmpty(requestId?.RequestIdKey) && !string.IsNullOrEmpty(requestId.RequestIdValue);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task<byte[]> ToByteArray(Stream stream)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            using (stream)
 | 
					            using (stream)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                using (MemoryStream memStream = new MemoryStream())
 | 
					                using (var memStream = new MemoryStream())
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    stream.CopyTo(memStream);
 | 
					                    await stream.CopyToAsync(memStream);
 | 
				
			||||||
                    return memStream.ToArray();
 | 
					                    return memStream.ToArray();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,8 @@ namespace Ocelot.Request.Builder
 | 
				
			|||||||
            Stream content,
 | 
					            Stream content,
 | 
				
			||||||
            IHeaderDictionary headers,
 | 
					            IHeaderDictionary headers,
 | 
				
			||||||
            IRequestCookieCollection cookies,
 | 
					            IRequestCookieCollection cookies,
 | 
				
			||||||
            Microsoft.AspNetCore.Http.QueryString queryString,
 | 
					            QueryString queryString,
 | 
				
			||||||
            string contentType);
 | 
					            string contentType, 
 | 
				
			||||||
 | 
					            RequestId.RequestId requestId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,17 +22,18 @@ namespace Ocelot.Request.Middleware
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public async Task Invoke(HttpContext context)
 | 
					        public async Task Invoke(HttpContext context)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var request = await _requestBuilder
 | 
					            var buildResult = await _requestBuilder
 | 
				
			||||||
                .Build(context.Request.Method, DownstreamUrl, context.Request.Body,
 | 
					                .Build(context.Request.Method, DownstreamUrl, context.Request.Body,
 | 
				
			||||||
              context.Request.Headers, context.Request.Cookies, context.Request.QueryString, context.Request.ContentType);
 | 
					                    context.Request.Headers, context.Request.Cookies, context.Request.QueryString,
 | 
				
			||||||
 | 
					                    context.Request.ContentType, new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (request.IsError)
 | 
					            if (buildResult.IsError)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                SetPipelineError(request.Errors);
 | 
					                SetPipelineError(buildResult.Errors);
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            SetUpstreamRequestForThisRequest(request.Data);
 | 
					            SetUpstreamRequestForThisRequest(buildResult.Data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await _next.Invoke(context);
 | 
					            await _next.Invoke(context);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9
									
								
								src/Ocelot/RequestId/DefaultRequestIdKey.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/Ocelot/RequestId/DefaultRequestIdKey.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.RequestId
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static class DefaultRequestIdKey
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // This is set incase anyone isnt doing this specifically with there requests. 
 | 
				
			||||||
 | 
					        // It will not be forwarded on to downstream services unless specfied in the config.
 | 
				
			||||||
 | 
					        public const string Value = "RequestId";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										45
									
								
								src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Primitives;
 | 
				
			||||||
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.RequestId.Middleware
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RequestIdMiddleware : OcelotMiddleware
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly RequestDelegate _next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RequestIdMiddleware(RequestDelegate next, 
 | 
				
			||||||
 | 
					            IRequestScopedDataRepository requestScopedDataRepository)
 | 
				
			||||||
 | 
					            :base(requestScopedDataRepository)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _next = next;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task Invoke(HttpContext context)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            SetTraceIdentifier(context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await _next.Invoke(context);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void SetTraceIdentifier(HttpContext context)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var key = DefaultRequestIdKey.Value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (DownstreamRoute.ReRoute.RequestIdKey != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                key = DownstreamRoute.ReRoute.RequestIdKey;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            StringValues requestId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (context.Request.Headers.TryGetValue(key, out requestId))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                context.TraceIdentifier = requestId;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.RequestId.Middleware
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static class RequestIdMiddlewareExtensions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static IApplicationBuilder UseRequestIdMiddleware(this IApplicationBuilder builder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return builder.UseMiddleware<RequestIdMiddleware>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										14
									
								
								src/Ocelot/RequestId/RequestId.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Ocelot/RequestId/RequestId.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.RequestId
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RequestId
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public RequestId(string requestIdKey, string requestIdValue)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            RequestIdKey = requestIdKey;
 | 
				
			||||||
 | 
					            RequestIdValue = requestIdValue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string RequestIdKey { get; private set; }
 | 
				
			||||||
 | 
					        public string RequestIdValue { get; private set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
 | 
					using System.Diagnostics;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
using Ocelot.Responder;
 | 
					using Ocelot.Responder;
 | 
				
			||||||
@@ -25,7 +27,6 @@ namespace Ocelot.Requester.Middleware
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public async Task Invoke(HttpContext context)
 | 
					        public async Task Invoke(HttpContext context)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					 | 
				
			||||||
            var response = await _requester.GetResponse(Request);
 | 
					            var response = await _requester.GetResponse(Request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (response.IsError)
 | 
					            if (response.IsError)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,10 @@
 | 
				
			|||||||
using System.Net.Http;
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Primitives;
 | 
				
			||||||
 | 
					using Ocelot.Headers;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Responder
 | 
					namespace Ocelot.Responder
 | 
				
			||||||
@@ -11,15 +15,42 @@ namespace Ocelot.Responder
 | 
				
			|||||||
    /// </summary>
 | 
					    /// </summary>
 | 
				
			||||||
    public class HttpContextResponder : IHttpResponder
 | 
					    public class HttpContextResponder : IHttpResponder
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly IRemoveHeaders _removeHeaders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public HttpContextResponder(IRemoveHeaders removeHeaders)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _removeHeaders = removeHeaders;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task<Response> SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response)
 | 
					        public async Task<Response> SetResponseOnHttpContext(HttpContext context, HttpResponseMessage response)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            context.Response.OnStarting(x =>
 | 
					            _removeHeaders.Remove(response.Headers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var httpResponseHeader in response.Headers)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                context.Response.StatusCode = (int)response.StatusCode;
 | 
					                context.Response.Headers.Add(httpResponseHeader.Key, new StringValues(httpResponseHeader.Value.ToArray()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var content = await response.Content.ReadAsStreamAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            context.Response.Headers.Add("Content-Length", new[] { content.Length.ToString() });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            context.Response.OnStarting(state =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var httpContext = (HttpContext)state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                httpContext.Response.StatusCode = (int)response.StatusCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return Task.CompletedTask;
 | 
					                return Task.CompletedTask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            }, context);
 | 
					            }, context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await context.Response.WriteAsync(await response.Content.ReadAsStringAsync());
 | 
					            using (var reader = new StreamReader(content))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var responseContent = reader.ReadToEnd();
 | 
				
			||||||
 | 
					                await context.Response.WriteAsync(responseContent);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new OkResponse();       
 | 
					            return new OkResponse();       
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										108
									
								
								test/Ocelot.AcceptanceTests/RequestIdTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								test/Ocelot.AcceptanceTests/RequestIdTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Net;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Primitives;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Yaml;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.AcceptanceTests
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RequestIdTests : IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private IWebHost _builder;
 | 
				
			||||||
 | 
					        private readonly Steps _steps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RequestIdTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _steps = new Steps();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_use_default_request_id_and_forward()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var yamlConfiguration = new YamlConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<YamlReRoute>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        new YamlReRoute
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            DownstreamTemplate = "http://localhost:51879/",
 | 
				
			||||||
 | 
					                            UpstreamTemplate = "/",
 | 
				
			||||||
 | 
					                            UpstreamHttpMethod = "Get",
 | 
				
			||||||
 | 
					                            RequestIdKey = _steps.RequestIdKey
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879"))
 | 
				
			||||||
 | 
					                .And(x => _steps.GivenThereIsAConfiguration(yamlConfiguration))
 | 
				
			||||||
 | 
					                .And(x => _steps.GivenOcelotIsRunning())
 | 
				
			||||||
 | 
					                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
 | 
				
			||||||
 | 
					                .Then(x => _steps.ThenTheRequestIdIsReturned())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_use_request_id_and_forward()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var yamlConfiguration = new YamlConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<YamlReRoute>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        new YamlReRoute
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            DownstreamTemplate = "http://localhost:51879/",
 | 
				
			||||||
 | 
					                            UpstreamTemplate = "/",
 | 
				
			||||||
 | 
					                            UpstreamHttpMethod = "Get",
 | 
				
			||||||
 | 
					                            RequestIdKey = _steps.RequestIdKey
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var requestId = Guid.NewGuid().ToString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879"))
 | 
				
			||||||
 | 
					                .And(x => _steps.GivenThereIsAConfiguration(yamlConfiguration))
 | 
				
			||||||
 | 
					                .And(x => _steps.GivenOcelotIsRunning())
 | 
				
			||||||
 | 
					                .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", requestId))
 | 
				
			||||||
 | 
					                .Then(x => _steps.ThenTheRequestIdIsReturned(requestId))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenThereIsAServiceRunningOn(string url)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _builder = new WebHostBuilder()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .UseKestrel()
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .UseIISIntegration()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.Run(context =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        StringValues requestId;
 | 
				
			||||||
 | 
					                        context.Request.Headers.TryGetValue(_steps.RequestIdKey, out requestId);
 | 
				
			||||||
 | 
					                        context.Response.Headers.Add(_steps.RequestIdKey, requestId.First());
 | 
				
			||||||
 | 
					                        return Task.CompletedTask;
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _builder.Start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _builder?.Dispose();
 | 
				
			||||||
 | 
					            _steps.Dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
using System.Net;
 | 
					using System.Net;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using System.Net.Http.Headers;
 | 
					using System.Net.Http.Headers;
 | 
				
			||||||
@@ -26,6 +27,7 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
        private HttpContent _postContent;
 | 
					        private HttpContent _postContent;
 | 
				
			||||||
        private BearerToken _token;
 | 
					        private BearerToken _token;
 | 
				
			||||||
        public HttpClient OcelotClient => _ocelotClient;
 | 
					        public HttpClient OcelotClient => _ocelotClient;
 | 
				
			||||||
 | 
					        public string RequestIdKey = "OcRequestId";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void GivenThereIsAConfiguration(YamlConfiguration yamlConfiguration)
 | 
					        public void GivenThereIsAConfiguration(YamlConfiguration yamlConfiguration)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -146,6 +148,13 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
            _response = _ocelotClient.GetAsync(url).Result;
 | 
					            _response = _ocelotClient.GetAsync(url).Result;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void WhenIGetUrlOnTheApiGateway(string url, string requestId)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation(RequestIdKey, requestId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _response = _ocelotClient.GetAsync(url).Result;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void WhenIPostUrlOnTheApiGateway(string url)
 | 
					        public void WhenIPostUrlOnTheApiGateway(string url)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _response = _ocelotClient.PostAsync(url, _postContent).Result;
 | 
					            _response = _ocelotClient.PostAsync(url, _postContent).Result;
 | 
				
			||||||
@@ -171,5 +180,15 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
            _ocelotClient?.Dispose();
 | 
					            _ocelotClient?.Dispose();
 | 
				
			||||||
            _ocelotServer?.Dispose();
 | 
					            _ocelotServer?.Dispose();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void ThenTheRequestIdIsReturned()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _response.Headers.GetValues(RequestIdKey).First().ShouldNotBeNullOrEmpty();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void ThenTheRequestIdIsReturned(string expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "Ocelot.ManualTest": {
 | 
					    "Ocelot.ManualTest": {
 | 
				
			||||||
      "commandName": "Project",
 | 
					      "commandName": "Project",
 | 
				
			||||||
      "launchBrowser": true,
 | 
					 | 
				
			||||||
      "launchUrl": "http://localhost:5000",
 | 
					      "launchUrl": "http://localhost:5000",
 | 
				
			||||||
      "environmentVariables": {
 | 
					      "environmentVariables": {
 | 
				
			||||||
        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
					        "ASPNETCORE_ENVIRONMENT": "Development"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,6 +50,10 @@ ReRoutes:
 | 
				
			|||||||
# the value must be registered
 | 
					# the value must be registered
 | 
				
			||||||
  RouteClaimsRequirement:
 | 
					  RouteClaimsRequirement:
 | 
				
			||||||
    UserType: registered
 | 
					    UserType: registered
 | 
				
			||||||
 | 
					# This tells Ocelot to look for a header and use its value as a request/correlation id. 
 | 
				
			||||||
 | 
					# If it is set here then the id will be forwarded to the downstream service. If it
 | 
				
			||||||
 | 
					# does not then it will not be forwarded
 | 
				
			||||||
 | 
					  RequestIdKey: OcRequestId
 | 
				
			||||||
# The next re route...
 | 
					# The next re route...
 | 
				
			||||||
- DownstreamTemplate: http://jsonplaceholder.typicode.com/posts
 | 
					- DownstreamTemplate: http://jsonplaceholder.typicode.com/posts
 | 
				
			||||||
  UpstreamTemplate: /posts
 | 
					  UpstreamTemplate: /posts
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										83
									
								
								test/Ocelot.UnitTests/Errors/GobalErrorHandlerTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								test/Ocelot.UnitTests/Errors/GobalErrorHandlerTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.TestHost;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using Moq;
 | 
				
			||||||
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.RequestId.Provider;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Errors
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class GobalErrorHandlerTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly Mock<ILoggerFactory> _loggerFactory;
 | 
				
			||||||
 | 
					        private readonly Mock<ILogger<ExceptionHandlerMiddleware>> _logger;
 | 
				
			||||||
 | 
					        private readonly Mock<IRequestIdProvider> _requestIdProvider;
 | 
				
			||||||
 | 
					        private readonly string _url;
 | 
				
			||||||
 | 
					        private readonly TestServer _server;
 | 
				
			||||||
 | 
					        private readonly HttpClient _client;
 | 
				
			||||||
 | 
					        private HttpResponseMessage _result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public GobalErrorHandlerTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _url = "http://localhost:51879";
 | 
				
			||||||
 | 
					            _logger = new Mock<ILogger<ExceptionHandlerMiddleware>>();
 | 
				
			||||||
 | 
					            _loggerFactory = new Mock<ILoggerFactory>();
 | 
				
			||||||
 | 
					            _requestIdProvider = new Mock<IRequestIdProvider>();
 | 
				
			||||||
 | 
					            var builder = new WebHostBuilder()
 | 
				
			||||||
 | 
					                .ConfigureServices(x =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    x.AddSingleton(_requestIdProvider.Object);
 | 
				
			||||||
 | 
					                    x.AddSingleton(_loggerFactory.Object);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .UseUrls(_url)
 | 
				
			||||||
 | 
					                .UseKestrel()
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .UseIISIntegration()
 | 
				
			||||||
 | 
					                .UseUrls(_url)
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.UseExceptionHandlerMiddleware();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    app.Run(x =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        throw new Exception("BLAM");
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _loggerFactory
 | 
				
			||||||
 | 
					                .Setup(x => x.CreateLogger<ExceptionHandlerMiddleware>())
 | 
				
			||||||
 | 
					                .Returns(_logger.Object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _server = new TestServer(builder);
 | 
				
			||||||
 | 
					            _client = _server.CreateClient();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_catch_exception_and_log()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            this.When(x => x.WhenICallTheMiddleware())
 | 
				
			||||||
 | 
					                .And(x => x.TheLoggerIsCalledCorrectly())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void TheLoggerIsCalledCorrectly()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _logger
 | 
				
			||||||
 | 
					                .Verify(x => x.LogError(It.IsAny<EventId>(), It.IsAny<string>(), It.IsAny<Exception>()), Times.Once);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICallTheMiddleware()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _client.GetAsync(_url).Result;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
							
								
								
									
										52
									
								
								test/Ocelot.UnitTests/Headers/RemoveHeaders.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								test/Ocelot.UnitTests/Headers/RemoveHeaders.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using System.Net.Http.Headers;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Headers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RemoveHeaders
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private HttpResponseHeaders _headers;
 | 
				
			||||||
 | 
					        private readonly Ocelot.Headers.RemoveHeaders _removeHeaders;
 | 
				
			||||||
 | 
					        private Response _result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RemoveHeaders()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _removeHeaders = new Ocelot.Headers.RemoveHeaders();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_remove_header()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var httpResponse = new HttpResponseMessage()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Headers = {{ "Transfer-Encoding", "chunked"}}
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenAHttpContext(httpResponse.Headers))
 | 
				
			||||||
 | 
					                .When(x => x.WhenIRemoveTheHeaders())
 | 
				
			||||||
 | 
					                .Then(x => x.TheHeaderIsNoLongerInTheContext())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenAHttpContext(HttpResponseHeaders headers)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _headers = headers;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenIRemoveTheHeaders()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _removeHeaders.Remove(_headers);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void TheHeaderIsNoLongerInTheContext()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.IsError.ShouldBeFalse();
 | 
				
			||||||
 | 
					            _headers.ShouldNotContain(x => x.Key == "Transfer-Encoding");
 | 
				
			||||||
 | 
					            _headers.ShouldNotContain(x => x.Key == "transfer-encoding");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,6 @@
 | 
				
			|||||||
namespace Ocelot.UnitTests.Infrastructure
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Infrastructure
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using System.Collections.Generic;
 | 
					    using System.Collections.Generic;
 | 
				
			||||||
    using System.Security.Claims;
 | 
					    using System.Security.Claims;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Net;
 | 
					using System.Net;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
@@ -7,6 +8,9 @@ using Microsoft.AspNetCore.Http;
 | 
				
			|||||||
using Microsoft.AspNetCore.TestHost;
 | 
					using Microsoft.AspNetCore.TestHost;
 | 
				
			||||||
using Microsoft.Extensions.DependencyInjection;
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
using Moq;
 | 
					using Moq;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamRouteFinder;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Request.Builder;
 | 
					using Ocelot.Request.Builder;
 | 
				
			||||||
using Ocelot.Request.Middleware;
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
@@ -26,6 +30,7 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
        private HttpResponseMessage _result;
 | 
					        private HttpResponseMessage _result;
 | 
				
			||||||
        private OkResponse<Ocelot.Request.Request> _request;
 | 
					        private OkResponse<Ocelot.Request.Request> _request;
 | 
				
			||||||
        private OkResponse<string> _downstreamUrl;
 | 
					        private OkResponse<string> _downstreamUrl;
 | 
				
			||||||
 | 
					        private OkResponse<DownstreamRoute> _downstreamRoute;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public HttpRequestBuilderMiddlewareTests()
 | 
					        public HttpRequestBuilderMiddlewareTests()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -56,19 +61,34 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void happy_path()
 | 
					        public void happy_path()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
 | 
				
			||||||
 | 
					                new ReRouteBuilder()
 | 
				
			||||||
 | 
					                    .WithRequestIdKey("LSRequestId").Build());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
 | 
					            this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
 | 
				
			||||||
                .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer())))
 | 
					                .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer())))
 | 
				
			||||||
                .When(x => x.WhenICallTheMiddleware())
 | 
					                .When(x => x.WhenICallTheMiddleware())
 | 
				
			||||||
                .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
 | 
					                .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
 | 
				
			||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
 | 
				
			||||||
 | 
					            _scopedRepository
 | 
				
			||||||
 | 
					                .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
 | 
				
			||||||
 | 
					                .Returns(_downstreamRoute);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheRequestBuilderReturns(Ocelot.Request.Request request)
 | 
					        private void GivenTheRequestBuilderReturns(Ocelot.Request.Request request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _request = new OkResponse<Ocelot.Request.Request>(request);
 | 
					            _request = new OkResponse<Ocelot.Request.Request>(request);
 | 
				
			||||||
            _requestBuilder
 | 
					            _requestBuilder
 | 
				
			||||||
                .Setup(x => x.Build(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Stream>(), It.IsAny<IHeaderDictionary>(),
 | 
					                .Setup(x => x.Build(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Stream>(), It.IsAny<IHeaderDictionary>(),
 | 
				
			||||||
                It.IsAny<IRequestCookieCollection>(), It.IsAny<QueryString>(), It.IsAny<string>()))
 | 
					                It.IsAny<IRequestCookieCollection>(), It.IsAny<QueryString>(), It.IsAny<string>(), It.IsAny<Ocelot.RequestId.RequestId>()))
 | 
				
			||||||
                .ReturnsAsync(_request);
 | 
					                .ReturnsAsync(_request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
        private string _contentType;
 | 
					        private string _contentType;
 | 
				
			||||||
        private readonly IRequestBuilder _requestBuilder;
 | 
					        private readonly IRequestBuilder _requestBuilder;
 | 
				
			||||||
        private Response<Ocelot.Request.Request> _result;
 | 
					        private Response<Ocelot.Request.Request> _result;
 | 
				
			||||||
 | 
					        private Ocelot.RequestId.RequestId _requestId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public RequestBuilderTests()
 | 
					        public RequestBuilderTests()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -114,6 +115,62 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_use_request_id()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var requestId = Guid.NewGuid().ToString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenIHaveHttpMethod("GET"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary()))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", requestId)))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateARequest())
 | 
				
			||||||
 | 
					                .And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    {"RequestId", requestId }
 | 
				
			||||||
 | 
					                }))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_not_use_request_if_if_already_in_headers()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenIHaveHttpMethod("GET"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    {"RequestId", "534534gv54gv45g" }
 | 
				
			||||||
 | 
					                }))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", Guid.NewGuid().ToString())))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateARequest())
 | 
				
			||||||
 | 
					                .And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    {"RequestId", "534534gv54gv45g" }
 | 
				
			||||||
 | 
					                }))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Theory]
 | 
				
			||||||
 | 
					        [InlineData(null, "blahh")]
 | 
				
			||||||
 | 
					        [InlineData("", "blahh")]
 | 
				
			||||||
 | 
					        [InlineData("RequestId", "")]
 | 
				
			||||||
 | 
					        [InlineData("RequestId", null)]
 | 
				
			||||||
 | 
					        public void should_not_use_request_id(string requestIdKey, string requestIdValue)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenIHaveHttpMethod("GET"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary()))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId(requestIdKey, requestIdValue)))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICreateARequest())
 | 
				
			||||||
 | 
					                .And(x => x.ThenTheRequestIdIsNotInTheHeaders())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheRequestIdIs(Ocelot.RequestId.RequestId requestId)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _requestId = requestId;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_use_cookies()
 | 
					        public void should_use_cookies()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -174,6 +231,11 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
            _cookies = cookies;
 | 
					            _cookies = cookies;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheRequestIdIsNotInTheHeaders()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.Data.HttpRequestMessage.Headers.ShouldNotContain(x => x.Key == "RequestId");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void ThenTheCorrectHeadersAreUsed(IHeaderDictionary expected)
 | 
					        private void ThenTheCorrectHeadersAreUsed(IHeaderDictionary expected)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var expectedHeaders = expected.Select(x => new KeyValuePair<string, string[]>(x.Key, x.Value));
 | 
					            var expectedHeaders = expected.Select(x => new KeyValuePair<string, string[]>(x.Key, x.Value));
 | 
				
			||||||
@@ -219,7 +281,7 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
        private void WhenICreateARequest()
 | 
					        private void WhenICreateARequest()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _result = _requestBuilder.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers,
 | 
					            _result = _requestBuilder.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers,
 | 
				
			||||||
                _cookies, _query, _contentType).Result;
 | 
					                _cookies, _query, _contentType, _requestId).Result;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										132
									
								
								test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.TestHost;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Moq;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamRouteFinder;
 | 
				
			||||||
 | 
					using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
				
			||||||
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
 | 
					using Ocelot.RequestId.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.RequestId
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class RequestIdMiddlewareTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
 | 
				
			||||||
 | 
					        private readonly string _url;
 | 
				
			||||||
 | 
					        private readonly TestServer _server;
 | 
				
			||||||
 | 
					        private readonly HttpClient _client;
 | 
				
			||||||
 | 
					        private Response<DownstreamRoute> _downstreamRoute;
 | 
				
			||||||
 | 
					        private HttpResponseMessage _result;
 | 
				
			||||||
 | 
					        private string _value;
 | 
				
			||||||
 | 
					        private string _key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public RequestIdMiddlewareTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _url = "http://localhost:51879";
 | 
				
			||||||
 | 
					            _scopedRepository = new Mock<IRequestScopedDataRepository>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var builder = new WebHostBuilder()
 | 
				
			||||||
 | 
					              .ConfigureServices(x =>
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                  x.AddSingleton(_scopedRepository.Object);
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					              .UseUrls(_url)
 | 
				
			||||||
 | 
					              .UseKestrel()
 | 
				
			||||||
 | 
					              .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					              .UseIISIntegration()
 | 
				
			||||||
 | 
					              .UseUrls(_url)
 | 
				
			||||||
 | 
					              .Configure(app =>
 | 
				
			||||||
 | 
					              {
 | 
				
			||||||
 | 
					                  app.UseRequestIdMiddleware();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  app.Run(x =>
 | 
				
			||||||
 | 
					                  {
 | 
				
			||||||
 | 
					                      x.Response.Headers.Add("LSRequestId", x.TraceIdentifier);
 | 
				
			||||||
 | 
					                      return Task.CompletedTask;
 | 
				
			||||||
 | 
					                  });
 | 
				
			||||||
 | 
					              });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _server = new TestServer(builder);
 | 
				
			||||||
 | 
					            _client = _server.CreateClient();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_add_request_id_to_repository()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
 | 
				
			||||||
 | 
					                new ReRouteBuilder()
 | 
				
			||||||
 | 
					                .WithDownstreamTemplate("any old string")
 | 
				
			||||||
 | 
					                .WithRequestIdKey("LSRequestId").Build());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var requestId = Guid.NewGuid().ToString();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
 | 
				
			||||||
 | 
					                .And(x => x.GivenTheRequestIdIsAddedToTheRequest("LSRequestId", requestId))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICallTheMiddleware())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheTraceIdIs(requestId))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_add_trace_indentifier_to_repository()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(),
 | 
				
			||||||
 | 
					                new ReRouteBuilder()
 | 
				
			||||||
 | 
					                .WithDownstreamTemplate("any old string")
 | 
				
			||||||
 | 
					                .WithRequestIdKey("LSRequestId").Build());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
 | 
				
			||||||
 | 
					                .When(x => x.WhenICallTheMiddleware())
 | 
				
			||||||
 | 
					                .Then(x => x.ThenTheTraceIdIsAnything())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheTraceIdIsAnything()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.Headers.GetValues("LSRequestId").First().ShouldNotBeNullOrEmpty();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenTheTraceIdIs(string expected)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result.Headers.GetValues("LSRequestId").First().ShouldBe(expected);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheRequestIdIsAddedToTheRequest(string key, string value)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _key = key;
 | 
				
			||||||
 | 
					            _value = value;
 | 
				
			||||||
 | 
					            _client.DefaultRequestHeaders.TryAddWithoutValidation(_key, _value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void WhenICallTheMiddleware()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _result = _client.GetAsync(_url).Result;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
 | 
				
			||||||
 | 
					            _scopedRepository
 | 
				
			||||||
 | 
					                .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
 | 
				
			||||||
 | 
					                .Returns(_downstreamRoute);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _client.Dispose();
 | 
				
			||||||
 | 
					            _server.Dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user