mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 09:55:28 +08:00 
			
		
		
		
	Merge branch 'release-5.3.0'
This commit is contained in:
		@@ -5,7 +5,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?branch=develop)
 | 
					[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb/history?branch=develop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[](https://coveralls.io/github/TomPallister/Ocelot?branch=develop)
 | 
					[](https://coveralls.io/github/ThreeMammals/Ocelot?branch=develop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Ocelot
 | 
					# Ocelot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -41,6 +41,7 @@ A quick list of Ocelot's capabilities for more information see the [documentatio
 | 
				
			|||||||
* Request Aggregation
 | 
					* Request Aggregation
 | 
				
			||||||
* Service Discovery with Consul
 | 
					* Service Discovery with Consul
 | 
				
			||||||
* Service Fabric
 | 
					* Service Fabric
 | 
				
			||||||
 | 
					* WebSockets
 | 
				
			||||||
* Authentication
 | 
					* Authentication
 | 
				
			||||||
* Authorisation
 | 
					* Authorisation
 | 
				
			||||||
* Rate Limiting
 | 
					* Rate Limiting
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,3 +12,9 @@ Finally if logging is set to trace level Ocelot will log starting, finishing and
 | 
				
			|||||||
The reason for not just using bog standard framework logging is that I could not 
 | 
					The reason for not just using bog standard framework logging is that I could not 
 | 
				
			||||||
work out how to override the request id that get's logged when setting IncludeScopes 
 | 
					work out how to override the request id that get's logged when setting IncludeScopes 
 | 
				
			||||||
to true for logging settings. Nicely onto the next feature.
 | 
					to true for logging settings. Nicely onto the next feature.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Warning
 | 
				
			||||||
 | 
					^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you are logging to Console you will get terrible performance. I have had so many issues about performance issues with Ocelot
 | 
				
			||||||
 | 
					and it is always logging level Debug, logging to Console :) Make sure you are logging to something proper in production :)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										68
									
								
								docs/features/websockets.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								docs/features/websockets.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					Websockets
 | 
				
			||||||
 | 
					==========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ocelot supports proxying websockets with some extra bits. This functionality was requested in `Issue 212 <https://github.com/ThreeMammals/Ocelot/issues/212>`_. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In order to get websocket proxying working with Ocelot you need to do the following.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In your Configure method you need to tell your application to use WebSockets.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: csharp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     Configure(app =>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        app.UseWebSockets();
 | 
				
			||||||
 | 
					        app.UseOcelot().Wait();
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then in your configuration.json add the following to proxy a ReRoute using websockets.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code-block:: json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       {
 | 
				
			||||||
 | 
					            "DownstreamPathTemplate": "/ws",
 | 
				
			||||||
 | 
					            "UpstreamPathTemplate": "/",
 | 
				
			||||||
 | 
					            "DownstreamScheme": "ws",
 | 
				
			||||||
 | 
					            "DownstreamHostAndPorts": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "Host": "localhost",
 | 
				
			||||||
 | 
					                    "Port": 5001
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With this configuration set Ocelot will match any websocket traffic that comes in on / and proxy it to localhost:5001/ws. To make this clearer
 | 
				
			||||||
 | 
					Ocelot will receive messages from the upstream client, proxy these to the downstream service, receive messages from the downstream service and 
 | 
				
			||||||
 | 
					proxy these to the upstream client.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Supported
 | 
				
			||||||
 | 
					^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Load Balancer
 | 
				
			||||||
 | 
					2. Routing
 | 
				
			||||||
 | 
					3. Service Discovery
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This means that you can set up your downstream services running websockets and either have multiple DownstreamHostAndPorts in your ReRoute 
 | 
				
			||||||
 | 
					config or hook your ReRoute into a service discovery provider and then load balance requests...Which I think is pretty cool :)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Not Supported
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unfortunately a lot of Ocelot's features are non websocket specific such as header and http client stuff. I've listed what won't work below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Tracing
 | 
				
			||||||
 | 
					2. RequestId
 | 
				
			||||||
 | 
					3. Request Aggregation
 | 
				
			||||||
 | 
					4. Rate Limiting
 | 
				
			||||||
 | 
					5. Quality of Service
 | 
				
			||||||
 | 
					6. Middleware Injection
 | 
				
			||||||
 | 
					7. Header Transformation
 | 
				
			||||||
 | 
					8. Delegating Handlers
 | 
				
			||||||
 | 
					9. Claims Transformation
 | 
				
			||||||
 | 
					10. Caching
 | 
				
			||||||
 | 
					11. Authentication - If anyone requests it we might be able to do something with basic authentication.
 | 
				
			||||||
 | 
					12. Authorisation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I'm not 100% sure what will happen with this feature when it get's into the wild so please make sure you test thoroughly! 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,6 +25,7 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
 | 
				
			|||||||
   features/servicefabric
 | 
					   features/servicefabric
 | 
				
			||||||
   features/authentication
 | 
					   features/authentication
 | 
				
			||||||
   features/authorisation
 | 
					   features/authorisation
 | 
				
			||||||
 | 
					   features/websockets
 | 
				
			||||||
   features/administration
 | 
					   features/administration
 | 
				
			||||||
   features/ratelimiting
 | 
					   features/ratelimiting
 | 
				
			||||||
   features/caching
 | 
					   features/caching
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
Big Picture
 | 
					Big Picture
 | 
				
			||||||
===========
 | 
					===========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Ocleot is aimed at people using .NET running 
 | 
					Ocelot is aimed at people using .NET running 
 | 
				
			||||||
a micro services / service orientated architecture 
 | 
					a micro services / service orientated architecture 
 | 
				
			||||||
that need a unified point of entry into their system.
 | 
					that need a unified point of entry into their system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ built to netcoreapp2.0 `this <https://docs.microsoft.com/en-us/dotnet/articles/s
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
**Install NuGet package**
 | 
					**Install NuGet package**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Install Ocelot and it's dependecies using nuget. You will need to create a netcoreapp2.0 projct and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections
 | 
					Install Ocelot and it's dependecies using nuget. You will need to create a netcoreapp2.0 project and bring the package into it. Then follow the Startup below and :doc:`../features/configuration` sections
 | 
				
			||||||
to get up and running.
 | 
					to get up and running.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   ``Install-Package Ocelot``
 | 
					   ``Install-Package Ocelot``
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,7 +37,7 @@ namespace Ocelot.Cache.Middleware
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var downstreamUrlKey = $"{context.DownstreamRequest.Method.Method}-{context.DownstreamRequest.RequestUri.OriginalString}";
 | 
					            var downstreamUrlKey = $"{context.DownstreamRequest.Method}-{context.DownstreamRequest.OriginalString}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey);
 | 
					            _logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								src/Ocelot/Configuration/Creator/AddHeader.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Ocelot/Configuration/Creator/AddHeader.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Configuration.Creator
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class AddHeader
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public AddHeader(string key, string value)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Key = key;
 | 
				
			||||||
 | 
					            Value = value;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Key { get; }
 | 
				
			||||||
 | 
					        public string Value { get; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -10,12 +10,12 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public class HeaderFindAndReplaceCreator : IHeaderFindAndReplaceCreator
 | 
					    public class HeaderFindAndReplaceCreator : IHeaderFindAndReplaceCreator
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private IPlaceholders _placeholders;
 | 
					        private readonly IPlaceholders _placeholders;
 | 
				
			||||||
        private IOcelotLogger _logger;
 | 
					        private readonly IOcelotLogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public HeaderFindAndReplaceCreator(IPlaceholders placeholders, IOcelotLoggerFactory factory)
 | 
					        public HeaderFindAndReplaceCreator(IPlaceholders placeholders, IOcelotLoggerFactory factory)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _logger = factory.CreateLogger<HeaderFindAndReplaceCreator>();;
 | 
					            _logger = factory.CreateLogger<HeaderFindAndReplaceCreator>();
 | 
				
			||||||
            _placeholders = placeholders;
 | 
					            _placeholders = placeholders;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,21 +14,10 @@ namespace Ocelot.Configuration.Creator
 | 
				
			|||||||
            Downstream = downstream;
 | 
					            Downstream = downstream;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public List<HeaderFindAndReplace> Upstream { get; private set; }
 | 
					        public List<HeaderFindAndReplace> Upstream { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public List<HeaderFindAndReplace> Downstream { get; private set; }
 | 
					        public List<HeaderFindAndReplace> Downstream { get; }
 | 
				
			||||||
        public List<AddHeader> AddHeadersToDownstream {get;private set;}
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class AddHeader
 | 
					        public List<AddHeader> AddHeadersToDownstream { get; }
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public AddHeader(string key, string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            this.Key = key;
 | 
					 | 
				
			||||||
            this.Value = value;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        public string Key { get; private set; }
 | 
					 | 
				
			||||||
        public string Value { get; private set; }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ using Newtonsoft.Json;
 | 
				
			|||||||
using Ocelot.Configuration.File;
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
using Ocelot.ServiceDiscovery;
 | 
					using Ocelot.ServiceDiscovery;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Configuration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Configuration.Repository
 | 
					namespace Ocelot.Configuration.Repository
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,5 +4,4 @@
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        int Delay { get; }
 | 
					        int Delay { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
using Ocelot.Errors;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Ocelot.DownstreamUrlCreator
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class DownstreamHostNullOrEmptyError : Error
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public DownstreamHostNullOrEmptyError()
 | 
					 | 
				
			||||||
            : base("downstream host was null or empty", OcelotErrorCode.DownstreamHostNullOrEmptyError)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
using Ocelot.Errors;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Ocelot.DownstreamUrlCreator
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class DownstreamPathNullOrEmptyError : Error
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public DownstreamPathNullOrEmptyError() 
 | 
					 | 
				
			||||||
            : base("downstream path was null or empty", OcelotErrorCode.DownstreamPathNullOrEmptyError)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
using Ocelot.Errors;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Ocelot.DownstreamUrlCreator
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class DownstreamSchemeNullOrEmptyError : Error
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public DownstreamSchemeNullOrEmptyError()
 | 
					 | 
				
			||||||
            : base("downstream scheme was null or empty", OcelotErrorCode.DownstreamSchemeNullOrEmptyError)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -40,49 +40,36 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            UriBuilder uriBuilder;
 | 
					            context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (ServiceFabricRequest(context))
 | 
					            if (ServiceFabricRequest(context))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                uriBuilder = CreateServiceFabricUri(context, dsPath);
 | 
					                var pathAndQuery = CreateServiceFabricUri(context, dsPath);
 | 
				
			||||||
 | 
					                context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
 | 
				
			||||||
 | 
					                context.DownstreamRequest.Query = pathAndQuery.query;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri)
 | 
					                context.DownstreamRequest.AbsolutePath = dsPath.Data.Value;
 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    Path = dsPath.Data.Value,
 | 
					 | 
				
			||||||
                    Scheme = context.DownstreamReRoute.DownstreamScheme
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            context.DownstreamRequest.RequestUri = uriBuilder.Uri;
 | 
					            _logger.LogDebug("downstream url is {context.DownstreamRequest}", context.DownstreamRequest);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", context.DownstreamRequest.RequestUri);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await _next.Invoke(context);
 | 
					            await _next.Invoke(context);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private UriBuilder CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
 | 
					        private (string path, string query) CreateServiceFabricUri(DownstreamContext context, Response<DownstreamPath> dsPath)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var query = context.DownstreamRequest.RequestUri.Query;
 | 
					            var query = context.DownstreamRequest.Query;           
 | 
				
			||||||
            var scheme = context.DownstreamReRoute.DownstreamScheme;
 | 
					 | 
				
			||||||
            var host = context.DownstreamRequest.RequestUri.Host;
 | 
					 | 
				
			||||||
            var port = context.DownstreamRequest.RequestUri.Port;
 | 
					 | 
				
			||||||
            var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
 | 
					            var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Uri uri;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (RequestForStatefullService(query))
 | 
					            if (RequestForStatefullService(query))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}{query}");
 | 
					                return (serviceFabricPath, query);
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var split = string.IsNullOrEmpty(query) ? "?" : "&";
 | 
					 | 
				
			||||||
                uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}{query}{split}cmd=instance");
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new UriBuilder(uri);
 | 
					            var split = string.IsNullOrEmpty(query) ? "?" : "&";
 | 
				
			||||||
 | 
					            return (serviceFabricPath, $"{query}{split}cmd=instance");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static bool ServiceFabricRequest(DownstreamContext context)
 | 
					        private static bool ServiceFabricRequest(DownstreamContext context)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ using Ocelot.Infrastructure.Claims.Parser;
 | 
				
			|||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using Ocelot.Configuration.Creator;
 | 
					using Ocelot.Configuration.Creator;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Headers
 | 
					namespace Ocelot.Headers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -17,7 +18,7 @@ namespace Ocelot.Headers
 | 
				
			|||||||
            _claimsParser = claimsParser;
 | 
					            _claimsParser = claimsParser;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, HttpRequestMessage downstreamRequest)
 | 
					        public Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var config in claimsToThings)
 | 
					            foreach (var config in claimsToThings)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,23 +1,22 @@
 | 
				
			|||||||
namespace Ocelot.Headers
 | 
					namespace Ocelot.Headers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using System;
 | 
					 | 
				
			||||||
    using System.Collections.Generic;
 | 
					    using System.Collections.Generic;
 | 
				
			||||||
    using System.Net.Http;
 | 
					    using System.Net.Http;
 | 
				
			||||||
    using Ocelot.Configuration.Creator;
 | 
					    using Ocelot.Configuration.Creator;
 | 
				
			||||||
    using Ocelot.Infrastructure;
 | 
					    using Ocelot.Infrastructure;
 | 
				
			||||||
    using Ocelot.Infrastructure.RequestData;
 | 
					 | 
				
			||||||
    using Ocelot.Logging;
 | 
					    using Ocelot.Logging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class AddHeadersToResponse : IAddHeadersToResponse
 | 
					    public class AddHeadersToResponse : IAddHeadersToResponse
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private IPlaceholders _placeholders;
 | 
					        private readonly IPlaceholders _placeholders;
 | 
				
			||||||
        private IOcelotLogger _logger;
 | 
					        private readonly IOcelotLogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public AddHeadersToResponse(IPlaceholders placeholders, IOcelotLoggerFactory factory)
 | 
					        public AddHeadersToResponse(IPlaceholders placeholders, IOcelotLoggerFactory factory)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _logger = factory.CreateLogger<AddHeadersToResponse>();
 | 
					            _logger = factory.CreateLogger<AddHeadersToResponse>();
 | 
				
			||||||
            _placeholders = placeholders;
 | 
					            _placeholders = placeholders;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void Add(List<AddHeader> addHeaders, HttpResponseMessage response)
 | 
					        public void Add(List<AddHeader> addHeaders, HttpResponseMessage response)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach(var add in addHeaders)
 | 
					            foreach(var add in addHeaders)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ using System.Net.Http;
 | 
				
			|||||||
using Ocelot.Configuration;
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
using Ocelot.Infrastructure;
 | 
					using Ocelot.Infrastructure;
 | 
				
			||||||
using Ocelot.Infrastructure.Extensions;
 | 
					using Ocelot.Infrastructure.Extensions;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Headers
 | 
					namespace Ocelot.Headers
 | 
				
			||||||
@@ -18,7 +19,7 @@ namespace Ocelot.Headers
 | 
				
			|||||||
            _placeholders = placeholders;
 | 
					            _placeholders = placeholders;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs, HttpRequestMessage request)
 | 
					        public Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs, DownstreamRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            foreach (var f in fAndRs)
 | 
					            foreach (var f in fAndRs)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,10 +6,11 @@
 | 
				
			|||||||
    using Ocelot.Configuration;
 | 
					    using Ocelot.Configuration;
 | 
				
			||||||
    using Ocelot.Configuration.Creator;
 | 
					    using Ocelot.Configuration.Creator;
 | 
				
			||||||
    using Ocelot.Infrastructure.RequestData;
 | 
					    using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
 | 
					    using Ocelot.Request.Middleware;
 | 
				
			||||||
    using Ocelot.Responses;
 | 
					    using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public interface IAddHeadersToRequest
 | 
					    public interface IAddHeadersToRequest
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, HttpRequestMessage downstreamRequest);
 | 
					        Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,13 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using Ocelot.Configuration;
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Headers
 | 
					namespace Ocelot.Headers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IHttpResponseHeaderReplacer
 | 
					    public interface IHttpResponseHeaderReplacer
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs, HttpRequestMessage httpRequestMessage);
 | 
					        Response Replace(HttpResponseMessage response, List<HeaderFindAndReplace> fAndRs, DownstreamRequest httpRequestMessage);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Infrastructure
 | 
					namespace Ocelot.Infrastructure
 | 
				
			||||||
@@ -6,6 +7,6 @@ namespace Ocelot.Infrastructure
 | 
				
			|||||||
    public interface IPlaceholders
 | 
					    public interface IPlaceholders
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Response<string> Get(string key);
 | 
					        Response<string> Get(string key);
 | 
				
			||||||
        Response<string> Get(string key, HttpRequestMessage request);
 | 
					        Response<string> Get(string key, DownstreamRequest request);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -3,14 +3,15 @@ using System.Collections.Generic;
 | 
				
			|||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Infrastructure
 | 
					namespace Ocelot.Infrastructure
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class Placeholders : IPlaceholders
 | 
					    public class Placeholders : IPlaceholders
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private Dictionary<string, Func<Response<string>>> _placeholders;
 | 
					        private readonly Dictionary<string, Func<Response<string>>> _placeholders;
 | 
				
			||||||
        private Dictionary<string, Func<HttpRequestMessage, string>> _requestPlaceholders;
 | 
					        private readonly Dictionary<string, Func<DownstreamRequest, string>> _requestPlaceholders;
 | 
				
			||||||
        private readonly IBaseUrlFinder _finder;
 | 
					        private readonly IBaseUrlFinder _finder;
 | 
				
			||||||
        private readonly IRequestScopedDataRepository _repo;
 | 
					        private readonly IRequestScopedDataRepository _repo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,13 +31,13 @@ namespace Ocelot.Infrastructure
 | 
				
			|||||||
                return new OkResponse<string>(traceId.Data);
 | 
					                return new OkResponse<string>(traceId.Data);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _requestPlaceholders = new Dictionary<string, Func<HttpRequestMessage, string>>();
 | 
					            _requestPlaceholders = new Dictionary<string, Func<DownstreamRequest, string>>();
 | 
				
			||||||
            _requestPlaceholders.Add("{DownstreamBaseUrl}", x => {
 | 
					            _requestPlaceholders.Add("{DownstreamBaseUrl}", x => {
 | 
				
			||||||
                var downstreamUrl = $"{x.RequestUri.Scheme}://{x.RequestUri.Host}";
 | 
					                var downstreamUrl = $"{x.Scheme}://{x.Host}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if(x.RequestUri.Port != 80 && x.RequestUri.Port != 443)
 | 
					                if(x.Port != 80 && x.Port != 443)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    downstreamUrl = $"{downstreamUrl}:{x.RequestUri.Port}";
 | 
					                    downstreamUrl = $"{downstreamUrl}:{x.Port}";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return $"{downstreamUrl}/";
 | 
					                return $"{downstreamUrl}/";
 | 
				
			||||||
@@ -57,7 +58,7 @@ namespace Ocelot.Infrastructure
 | 
				
			|||||||
            return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
 | 
					            return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Response<string> Get(string key, HttpRequestMessage request)
 | 
					        public Response<string> Get(string key, DownstreamRequest request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if(_requestPlaceholders.ContainsKey(key))
 | 
					            if(_requestPlaceholders.ContainsKey(key))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +1,8 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.Middleware;
 | 
					 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					 | 
				
			||||||
using Ocelot.LoadBalancer.LoadBalancers;
 | 
					using Ocelot.LoadBalancer.LoadBalancers;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
using Ocelot.QueryStrings.Middleware;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.LoadBalancer.Middleware
 | 
					namespace Ocelot.LoadBalancer.Middleware
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -43,17 +39,13 @@ namespace Ocelot.LoadBalancer.Middleware
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri);
 | 
					            context.DownstreamRequest.Host = hostAndPort.Data.DownstreamHost;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            uriBuilder.Host = hostAndPort.Data.DownstreamHost;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (hostAndPort.Data.DownstreamPort > 0)
 | 
					            if (hostAndPort.Data.DownstreamPort > 0)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                uriBuilder.Port = hostAndPort.Data.DownstreamPort;
 | 
					                context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            context.DownstreamRequest.RequestUri = uriBuilder.Uri;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                await _next.Invoke(context);
 | 
					                await _next.Invoke(context);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
using Ocelot.Configuration;
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
					using Ocelot.DownstreamRouteFinder.UrlMatcher;
 | 
				
			||||||
using Ocelot.Errors;
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Middleware
 | 
					namespace Ocelot.Middleware
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -19,7 +21,7 @@ namespace Ocelot.Middleware
 | 
				
			|||||||
        public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;}
 | 
					        public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;}
 | 
				
			||||||
        public HttpContext HttpContext { get; private set; }
 | 
					        public HttpContext HttpContext { get; private set; }
 | 
				
			||||||
        public DownstreamReRoute DownstreamReRoute { get; set; }
 | 
					        public DownstreamReRoute DownstreamReRoute { get; set; }
 | 
				
			||||||
        public HttpRequestMessage DownstreamRequest { get; set; }
 | 
					        public DownstreamRequest DownstreamRequest { get; set; }
 | 
				
			||||||
        public HttpResponseMessage DownstreamResponse { get; set; }
 | 
					        public HttpResponseMessage DownstreamResponse { get; set; }
 | 
				
			||||||
        public List<Error> Errors { get;set; }
 | 
					        public List<Error> Errors { get;set; }
 | 
				
			||||||
        public bool IsError => Errors.Count > 0;
 | 
					        public bool IsError => Errors.Count > 0;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,5 +11,6 @@ namespace Ocelot.Middleware.Pipeline
 | 
				
			|||||||
        IServiceProvider ApplicationServices { get; }
 | 
					        IServiceProvider ApplicationServices { get; }
 | 
				
			||||||
        OcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware);
 | 
					        OcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware);
 | 
				
			||||||
        OcelotRequestDelegate Build();
 | 
					        OcelotRequestDelegate Build();
 | 
				
			||||||
 | 
					        IOcelotPipelineBuilder New();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										44
									
								
								src/Ocelot/Middleware/Pipeline/MapWhenMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/Ocelot/Middleware/Pipeline/MapWhenMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Middleware.Pipeline
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MapWhenMiddleware
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly OcelotRequestDelegate _next;
 | 
				
			||||||
 | 
					        private readonly MapWhenOptions _options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public MapWhenMiddleware(OcelotRequestDelegate next, MapWhenOptions options)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (next == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException(nameof(next));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (options == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException(nameof(options));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _next = next;
 | 
				
			||||||
 | 
					            _options = options;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task Invoke(DownstreamContext context)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (context == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException(nameof(context));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (_options.Predicate(context))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await _options.Branch(context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                await _next(context);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								src/Ocelot/Middleware/Pipeline/MapWhenOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Ocelot/Middleware/Pipeline/MapWhenOptions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.Middleware.Pipeline
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class MapWhenOptions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private Func<DownstreamContext, bool> _predicate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public Func<DownstreamContext, bool> Predicate
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            get
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return _predicate;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            set
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (value == null)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    throw new ArgumentNullException(nameof(value));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                _predicate = value;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public OcelotRequestDelegate Branch { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -19,6 +19,12 @@ namespace Ocelot.Middleware.Pipeline
 | 
				
			|||||||
            _middlewares = new List<Func<OcelotRequestDelegate, OcelotRequestDelegate>>();
 | 
					            _middlewares = new List<Func<OcelotRequestDelegate, OcelotRequestDelegate>>();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         public OcelotPipelineBuilder(IOcelotPipelineBuilder builder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            ApplicationServices = builder.ApplicationServices;
 | 
				
			||||||
 | 
					            _middlewares = new List<Func<OcelotRequestDelegate, OcelotRequestDelegate>>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IServiceProvider ApplicationServices { get; }
 | 
					        public IServiceProvider ApplicationServices { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public OcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware)
 | 
					        public OcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware)
 | 
				
			||||||
@@ -42,5 +48,10 @@ namespace Ocelot.Middleware.Pipeline
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            return app;
 | 
					            return app;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public IOcelotPipelineBuilder New()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return new OcelotPipelineBuilder(this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,8 @@ using Microsoft.Extensions.DependencyInjection;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Middleware.Pipeline
 | 
					namespace Ocelot.Middleware.Pipeline
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    using Predicate = Func<DownstreamContext, bool>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static class OcelotPipelineBuilderExtensions
 | 
					    public static class OcelotPipelineBuilderExtensions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        internal const string InvokeMethodName = "Invoke";
 | 
					        internal const string InvokeMethodName = "Invoke";
 | 
				
			||||||
@@ -91,6 +93,35 @@ namespace Ocelot.Middleware.Pipeline
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public static IOcelotPipelineBuilder MapWhen(this IOcelotPipelineBuilder app, Predicate predicate, Action<IOcelotPipelineBuilder> configuration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (app == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException(nameof(app));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (predicate == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException(nameof(predicate));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (configuration == null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new ArgumentNullException(nameof(configuration));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var branchBuilder = app.New();
 | 
				
			||||||
 | 
					            configuration(branchBuilder);
 | 
				
			||||||
 | 
					            var branch = branchBuilder.Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var options = new MapWhenOptions
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Predicate = predicate,
 | 
				
			||||||
 | 
					                Branch = branch,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            return app.Use(next => new MapWhenMiddleware(next, options).Invoke);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static Func<T, DownstreamContext, IServiceProvider, Task> Compile<T>(MethodInfo methodinfo, ParameterInfo[] parameters)
 | 
					        private static Func<T, DownstreamContext, IServiceProvider, Task> Compile<T>(MethodInfo methodinfo, ParameterInfo[] parameters)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var middleware = typeof(T);
 | 
					            var middleware = typeof(T);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,18 +15,30 @@ using Ocelot.Request.Middleware;
 | 
				
			|||||||
using Ocelot.Requester.Middleware;
 | 
					using Ocelot.Requester.Middleware;
 | 
				
			||||||
using Ocelot.RequestId.Middleware;
 | 
					using Ocelot.RequestId.Middleware;
 | 
				
			||||||
using Ocelot.Responder.Middleware;
 | 
					using Ocelot.Responder.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.WebSockets.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.Middleware.Pipeline
 | 
					namespace Ocelot.Middleware.Pipeline
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public static class OcelotPipelineExtensions
 | 
					    public static class OcelotPipelineExtensions
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuilder builder,
 | 
					        public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuilder builder,
 | 
				
			||||||
            OcelotPipelineConfiguration pipelineConfiguration = null)
 | 
					            OcelotPipelineConfiguration pipelineConfiguration)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // This is registered to catch any global exceptions that are not handled
 | 
					            // This is registered to catch any global exceptions that are not handled
 | 
				
			||||||
            // It also sets the Request Id if anything is set globally
 | 
					            // It also sets the Request Id if anything is set globally
 | 
				
			||||||
            builder.UseExceptionHandlerMiddleware();
 | 
					            builder.UseExceptionHandlerMiddleware();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // If the request is for websockets upgrade we fork into a different pipeline
 | 
				
			||||||
 | 
					            builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest,
 | 
				
			||||||
 | 
					                app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.UseDownstreamRouteFinderMiddleware();
 | 
				
			||||||
 | 
					                    app.UseDownstreamRequestInitialiser();
 | 
				
			||||||
 | 
					                    app.UseLoadBalancingMiddleware();
 | 
				
			||||||
 | 
					                    app.UseDownstreamUrlCreatorMiddleware();
 | 
				
			||||||
 | 
					                    app.UseWebSocketsProxyMiddleware();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Allow the user to respond with absolutely anything they want.
 | 
					            // Allow the user to respond with absolutely anything they want.
 | 
				
			||||||
            builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);
 | 
					            builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ using Ocelot.Responses;
 | 
				
			|||||||
using System.Security.Claims;
 | 
					using System.Security.Claims;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Microsoft.Extensions.Primitives;
 | 
					using Microsoft.Extensions.Primitives;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,9 +22,9 @@ namespace Ocelot.QueryStrings
 | 
				
			|||||||
            _claimsParser = claimsParser;
 | 
					            _claimsParser = claimsParser;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Response SetQueriesOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<Claim> claims, HttpRequestMessage downstreamRequest)
 | 
					        public Response SetQueriesOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<Claim> claims, DownstreamRequest downstreamRequest)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var queryDictionary = ConvertQueryStringToDictionary(downstreamRequest.RequestUri.Query);
 | 
					            var queryDictionary = ConvertQueryStringToDictionary(downstreamRequest.Query);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            foreach (var config in claimsToThings)
 | 
					            foreach (var config in claimsToThings)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@@ -46,11 +47,7 @@ namespace Ocelot.QueryStrings
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var uriBuilder = new UriBuilder(downstreamRequest.RequestUri);
 | 
					            downstreamRequest.Query = ConvertDictionaryToQueryString(queryDictionary);
 | 
				
			||||||
 | 
					 | 
				
			||||||
            uriBuilder.Query = ConvertDictionaryToQueryString(queryDictionary);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            downstreamRequest.RequestUri = uriBuilder.Uri;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new OkResponse();
 | 
					            return new OkResponse();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,11 +4,12 @@ using Ocelot.Configuration;
 | 
				
			|||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using System.Security.Claims;
 | 
					using System.Security.Claims;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.QueryStrings
 | 
					namespace Ocelot.QueryStrings
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IAddQueriesToRequest
 | 
					    public interface IAddQueriesToRequest
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        Response SetQueriesOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<Claim> claims, HttpRequestMessage downstreamRequest);
 | 
					        Response SetQueriesOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<Claim> claims, DownstreamRequest downstreamRequest);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								src/Ocelot/Request/Middleware/DownstreamRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/Ocelot/Request/Middleware/DownstreamRequest.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					namespace Ocelot.Request.Middleware
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    using System;
 | 
				
			||||||
 | 
					    using System.Net.Http;
 | 
				
			||||||
 | 
					    using System.Net.Http.Headers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public class DownstreamRequest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private readonly HttpRequestMessage _request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public DownstreamRequest(HttpRequestMessage request)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _request = request;
 | 
				
			||||||
 | 
					            Method = _request.Method.Method;
 | 
				
			||||||
 | 
					            OriginalString = _request.RequestUri.OriginalString;
 | 
				
			||||||
 | 
					            Scheme = _request.RequestUri.Scheme;
 | 
				
			||||||
 | 
					            Host = _request.RequestUri.Host;
 | 
				
			||||||
 | 
					            Port = _request.RequestUri.Port;
 | 
				
			||||||
 | 
					            Headers = _request.Headers;
 | 
				
			||||||
 | 
					            AbsolutePath = _request.RequestUri.AbsolutePath;
 | 
				
			||||||
 | 
					            Query = _request.RequestUri.Query;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public HttpRequestHeaders Headers { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Method { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string OriginalString { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Scheme { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Host { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int Port { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string AbsolutePath { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string Query { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public HttpRequestMessage ToHttpRequestMessage()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var uriBuilder = new UriBuilder
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Port = Port,
 | 
				
			||||||
 | 
					                Host = Host,
 | 
				
			||||||
 | 
					                Path = AbsolutePath,
 | 
				
			||||||
 | 
					                Query = Query,
 | 
				
			||||||
 | 
					                Scheme = Scheme
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _request.RequestUri = uriBuilder.Uri;
 | 
				
			||||||
 | 
					            return _request;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public string ToUri()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var uriBuilder = new UriBuilder
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Port = Port,
 | 
				
			||||||
 | 
					                Host = Host,
 | 
				
			||||||
 | 
					                Path = AbsolutePath,
 | 
				
			||||||
 | 
					                Query = Query,
 | 
				
			||||||
 | 
					                Scheme = Scheme
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return uriBuilder.Uri.AbsoluteUri;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
namespace Ocelot.Request.Middleware
 | 
					namespace Ocelot.Request.Middleware
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    using System.Net.Http;
 | 
				
			||||||
    using System.Threading.Tasks;
 | 
					    using System.Threading.Tasks;
 | 
				
			||||||
    using Microsoft.AspNetCore.Http;
 | 
					    using Microsoft.AspNetCore.Http;
 | 
				
			||||||
    using Ocelot.DownstreamRouteFinder.Middleware;
 | 
					    using Ocelot.DownstreamRouteFinder.Middleware;
 | 
				
			||||||
@@ -31,7 +32,7 @@ namespace Ocelot.Request.Middleware
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            context.DownstreamRequest = downstreamRequest.Data;
 | 
					            context.DownstreamRequest = new DownstreamRequest(downstreamRequest.Data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await _next.Invoke(context);
 | 
					            await _next.Invoke(context);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ using System.Net.Http;
 | 
				
			|||||||
using System.Net.Http.Headers;
 | 
					using System.Net.Http.Headers;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using Ocelot.DownstreamRouteFinder.Middleware;
 | 
					using Ocelot.DownstreamRouteFinder.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.RequestId.Middleware
 | 
					namespace Ocelot.RequestId.Middleware
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -82,7 +83,7 @@ namespace Ocelot.RequestId.Middleware
 | 
				
			|||||||
            return headers.TryGetValues(requestId.RequestIdKey, out value);
 | 
					            return headers.TryGetValues(requestId.RequestIdKey, out value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void AddRequestIdHeader(RequestId requestId, HttpRequestMessage httpRequestMessage)
 | 
					        private void AddRequestIdHeader(RequestId requestId, DownstreamRequest httpRequestMessage)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue);
 | 
					            httpRequestMessage.Headers.Add(requestId.RequestIdKey, requestId.RequestIdValue);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,9 +78,7 @@ namespace Ocelot.Requester
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private string GetCacheKey(DownstreamContext request)
 | 
					        private string GetCacheKey(DownstreamContext request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var baseUrl = $"{request.DownstreamRequest.RequestUri.Scheme}://{request.DownstreamRequest.RequestUri.Authority}{request.DownstreamRequest.RequestUri.AbsolutePath}";
 | 
					            return request.DownstreamRequest.OriginalString;
 | 
				
			||||||
 | 
					 | 
				
			||||||
            return baseUrl;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,7 +32,7 @@ namespace Ocelot.Requester
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            try
 | 
					            try
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                var response = await httpClient.SendAsync(context.DownstreamRequest);
 | 
					                var response = await httpClient.SendAsync(context.DownstreamRequest.ToHttpRequestMessage());
 | 
				
			||||||
                return new OkResponse<HttpResponseMessage>(response);
 | 
					                return new OkResponse<HttpResponseMessage>(response);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            catch (TimeoutRejectedException exception)
 | 
					            catch (TimeoutRejectedException exception)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery.Configuration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ConsulRegistryConfiguration
 | 
					    public class ConsulRegistryConfiguration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery.Configuration
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ServiceFabricConfiguration
 | 
					    public class ServiceFabricConfiguration
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -9,8 +9,10 @@
 | 
				
			|||||||
            ServiceName = serviceName;
 | 
					            ServiceName = serviceName;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string ServiceName { get; private set; }
 | 
					        public string ServiceName { get; }
 | 
				
			||||||
        public string HostName { get; private set; }
 | 
					
 | 
				
			||||||
        public int Port { get; private set; }
 | 
					        public string HostName { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public int Port { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
using Ocelot.Errors;
 | 
					using Ocelot.Errors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery.Errors
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class UnableToFindServiceDiscoveryProviderError : Error
 | 
					    public class UnableToFindServiceDiscoveryProviderError : Error
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using Ocelot.Configuration;
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Providers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ using System.Collections.Generic;
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Ocelot.Values;
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery.Providers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ConfigurationServiceProvider : IServiceDiscoveryProvider
 | 
					    public class ConfigurationServiceProvider : IServiceDiscoveryProvider
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -5,9 +5,10 @@ using System.Threading.Tasks;
 | 
				
			|||||||
using Consul;
 | 
					using Consul;
 | 
				
			||||||
using Ocelot.Infrastructure.Extensions;
 | 
					using Ocelot.Infrastructure.Extensions;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Configuration;
 | 
				
			||||||
using Ocelot.Values;
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery.Providers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
 | 
					    public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -2,7 +2,7 @@ using System.Collections.Generic;
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
using Ocelot.Values;
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery.Providers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public interface IServiceDiscoveryProvider
 | 
					    public interface IServiceDiscoveryProvider
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -1,8 +1,9 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Configuration;
 | 
				
			||||||
using Ocelot.Values;
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery.Providers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class ServiceFabricServiceDiscoveryProvider : IServiceDiscoveryProvider
 | 
					    public class ServiceFabricServiceDiscoveryProvider : IServiceDiscoveryProvider
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -1,6 +1,8 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using Ocelot.Configuration;
 | 
					using Ocelot.Configuration;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Providers;
 | 
				
			||||||
using Ocelot.Values;
 | 
					using Ocelot.Values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.ServiceDiscovery
 | 
					namespace Ocelot.ServiceDiscovery
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,6 @@
 | 
				
			|||||||
            Value = value;
 | 
					            Value = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Value { get; private set; }
 | 
					        public string Value { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
namespace Ocelot.Values
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    public class DownstreamUrl
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        public DownstreamUrl(string value)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Value = value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        public string Value { get; private set; }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -7,6 +7,6 @@
 | 
				
			|||||||
            Value = value;
 | 
					            Value = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Value { get; private set; }
 | 
					        public string Value { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,14 +17,14 @@ namespace Ocelot.Values
 | 
				
			|||||||
            Tags = tags;
 | 
					            Tags = tags;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Id { get; private set; }
 | 
					        public string Id { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Name { get; private set; }
 | 
					        public string Name { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Version { get; private set; }
 | 
					        public string Version { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public IEnumerable<string> Tags { get; private set; }
 | 
					        public IEnumerable<string> Tags { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public ServiceHostAndPort HostAndPort { get; private set; }
 | 
					        public ServiceHostAndPort HostAndPort { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,8 @@
 | 
				
			|||||||
            DownstreamPort = downstreamPort;
 | 
					            DownstreamPort = downstreamPort;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string DownstreamHost { get; private set; }
 | 
					        public string DownstreamHost { get; }
 | 
				
			||||||
        public int DownstreamPort { get; private set; }
 | 
					
 | 
				
			||||||
 | 
					        public int DownstreamPort { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ namespace Ocelot.Values
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public string Template { get; }
 | 
					        public string Template { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public int Priority { get; }
 | 
					        public int Priority { get; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										103
									
								
								src/Ocelot/WebSockets/Middleware/WebSocketsProxyMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								src/Ocelot/WebSockets/Middleware/WebSocketsProxyMiddleware.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Net.WebSockets;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Ocelot.Logging;
 | 
				
			||||||
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.WebSockets.Middleware
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class WebSocketsProxyMiddleware : OcelotMiddleware
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private OcelotRequestDelegate _next;
 | 
				
			||||||
 | 
					        private IOcelotLogger _logger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public WebSocketsProxyMiddleware(OcelotRequestDelegate next,
 | 
				
			||||||
 | 
					            IOcelotLoggerFactory loggerFactory)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _next = next;
 | 
				
			||||||
 | 
					            _logger = loggerFactory.CreateLogger<WebSocketsProxyMiddleware>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task Invoke(DownstreamContext context)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await Proxy(context.HttpContext, context.DownstreamRequest.ToUri());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task Proxy(HttpContext context, string serverEndpoint)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var wsToUpstreamClient = await context.WebSockets.AcceptWebSocketAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var wsToDownstreamService = new ClientWebSocket();
 | 
				
			||||||
 | 
					            var uri = new Uri(serverEndpoint);
 | 
				
			||||||
 | 
					            await wsToDownstreamService.ConnectAsync(uri, CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var receiveFromUpstreamSendToDownstream = Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var buffer = new byte[1024 * 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var receiveSegment = new ArraySegment<byte>(buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (wsToUpstreamClient.State == WebSocketState.Open || wsToUpstreamClient.State == WebSocketState.CloseSent)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var result = await wsToUpstreamClient.ReceiveAsync(receiveSegment, CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var sendSegment = new ArraySegment<byte>(buffer, 0, result.Count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if(result.MessageType == WebSocketMessageType.Close)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        await wsToUpstreamClient.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "",
 | 
				
			||||||
 | 
					                        CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        await wsToDownstreamService.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "",
 | 
				
			||||||
 | 
					                        CancellationToken.None);
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    await wsToDownstreamService.SendAsync(sendSegment, result.MessageType, result.EndOfMessage,
 | 
				
			||||||
 | 
					                        CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (wsToUpstreamClient.State != WebSocketState.Open)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        await wsToDownstreamService.CloseAsync(WebSocketCloseStatus.Empty, "",
 | 
				
			||||||
 | 
					                            CancellationToken.None);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var receiveFromDownstreamAndSendToUpstream = Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var buffer = new byte[1024 * 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (wsToDownstreamService.State == WebSocketState.Open || wsToDownstreamService.State == WebSocketState.CloseSent)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    if (wsToUpstreamClient.State != WebSocketState.Open)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var receiveSegment = new ArraySegment<byte>(buffer);
 | 
				
			||||||
 | 
					                        var result = await wsToDownstreamService.ReceiveAsync(receiveSegment, CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (result.MessageType == WebSocketMessageType.Close)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        var sendSegment = new ArraySegment<byte>(buffer, 0, result.Count);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        //send to upstream client
 | 
				
			||||||
 | 
					                        await wsToUpstreamClient.SendAsync(sendSegment, result.MessageType, result.EndOfMessage,
 | 
				
			||||||
 | 
					                            CancellationToken.None);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Task.WhenAll(receiveFromDownstreamAndSendToUpstream, receiveFromUpstreamSendToDownstream);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					using Ocelot.Middleware.Pipeline;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.WebSockets.Middleware
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public static class WebSocketsProxyMiddlewareExtensions
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        public static IOcelotPipelineBuilder UseWebSocketsProxyMiddleware(this IOcelotPipelineBuilder builder)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            return builder.UseMiddleware<WebSocketsProxyMiddleware>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,6 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Net;
 | 
					 | 
				
			||||||
using Consul;
 | 
					 | 
				
			||||||
using Microsoft.AspNetCore.Builder;
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
using Microsoft.AspNetCore.Hosting;
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
using Microsoft.AspNetCore.Http;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
@@ -28,7 +26,7 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_use_service_discovery_and_load_balance_request()
 | 
					        public void should_load_balance_request()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var downstreamServiceOneUrl = "http://localhost:50881";
 | 
					            var downstreamServiceOneUrl = "http://localhost:50881";
 | 
				
			||||||
            var downstreamServiceTwoUrl = "http://localhost:50892";
 | 
					            var downstreamServiceTwoUrl = "http://localhost:50892";
 | 
				
			||||||
@@ -74,18 +72,6 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void ThenOnlyOneServiceHasBeenCalled()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _counterOne.ShouldBe(10);
 | 
					 | 
				
			||||||
            _counterTwo.ShouldBe(0);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
         private void GivenIResetCounters()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _counterOne = 0;
 | 
					 | 
				
			||||||
            _counterTwo = 0;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
 | 
					        private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _counterOne.ShouldBeInRange(bottom, top);
 | 
					            _counterOne.ShouldBeInRange(bottom, top);
 | 
				
			||||||
@@ -121,7 +107,7 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
                            context.Response.StatusCode = statusCode;
 | 
					                            context.Response.StatusCode = statusCode;
 | 
				
			||||||
                            await context.Response.WriteAsync(response);
 | 
					                            await context.Response.WriteAsync(response);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        catch (System.Exception exception)
 | 
					                        catch (Exception exception)
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            await context.Response.WriteAsync(exception.StackTrace);
 | 
					                            await context.Response.WriteAsync(exception.StackTrace);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,12 +40,49 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
        public string RequestIdKey = "OcRequestId";
 | 
					        public string RequestIdKey = "OcRequestId";
 | 
				
			||||||
        private readonly Random _random;
 | 
					        private readonly Random _random;
 | 
				
			||||||
        private IWebHostBuilder _webHostBuilder;
 | 
					        private IWebHostBuilder _webHostBuilder;
 | 
				
			||||||
 | 
					        private WebHostBuilder _ocelotBuilder;
 | 
				
			||||||
 | 
					        private IWebHost _ocelotHost;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Steps()
 | 
					        public Steps()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _random = new Random();
 | 
					            _random = new Random();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task StartFakeOcelotWithWebSockets()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _ocelotBuilder = new WebHostBuilder();
 | 
				
			||||||
 | 
					            _ocelotBuilder.ConfigureServices(s =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                s.AddSingleton(_ocelotBuilder);
 | 
				
			||||||
 | 
					                s.AddOcelot();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            _ocelotBuilder.UseKestrel()
 | 
				
			||||||
 | 
					                .UseUrls("http://localhost:5000")
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .ConfigureAppConfiguration((hostingContext, config) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
 | 
				
			||||||
 | 
					                    var env = hostingContext.HostingEnvironment;
 | 
				
			||||||
 | 
					                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
 | 
				
			||||||
 | 
					                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
 | 
				
			||||||
 | 
					                    config.AddJsonFile("configuration.json");
 | 
				
			||||||
 | 
					                    config.AddEnvironmentVariables();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .ConfigureLogging((hostingContext, logging) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
 | 
				
			||||||
 | 
					                    logging.AddConsole();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.UseWebSockets();
 | 
				
			||||||
 | 
					                    app.UseOcelot().Wait();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .UseIISIntegration();
 | 
				
			||||||
 | 
					            _ocelotHost = _ocelotBuilder.Build();
 | 
				
			||||||
 | 
					            await _ocelotHost.StartAsync();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
 | 
					        public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var configurationPath = TestConfiguration.ConfigurationPath;
 | 
					            var configurationPath = TestConfiguration.ConfigurationPath;
 | 
				
			||||||
@@ -698,6 +735,7 @@ namespace Ocelot.AcceptanceTests
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            _ocelotClient?.Dispose();
 | 
					            _ocelotClient?.Dispose();
 | 
				
			||||||
            _ocelotServer?.Dispose();
 | 
					            _ocelotServer?.Dispose();
 | 
				
			||||||
 | 
					            _ocelotHost?.Dispose();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public void ThenTheRequestIdIsReturned()
 | 
					        public void ThenTheRequestIdIsReturned()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										487
									
								
								test/Ocelot.AcceptanceTests/WebSocketTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										487
									
								
								test/Ocelot.AcceptanceTests/WebSocketTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,487 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Net.WebSockets;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Consul;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.AcceptanceTests
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class WebSocketTests : IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private IWebHost _firstDownstreamHost;
 | 
				
			||||||
 | 
					        private IWebHost _secondDownstreamHost;
 | 
				
			||||||
 | 
					        private readonly List<string> _secondRecieved;
 | 
				
			||||||
 | 
					        private readonly List<string> _firstRecieved;
 | 
				
			||||||
 | 
					        private readonly List<ServiceEntry> _serviceEntries;
 | 
				
			||||||
 | 
					        private readonly Steps _steps;
 | 
				
			||||||
 | 
					        private IWebHost _fakeConsulBuilder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public WebSocketTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _steps = new Steps();
 | 
				
			||||||
 | 
					            _firstRecieved = new List<string>();
 | 
				
			||||||
 | 
					            _secondRecieved = new List<string>();
 | 
				
			||||||
 | 
					            _serviceEntries = new List<ServiceEntry>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public async Task should_proxy_websocket_input_to_downstream_service()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var downstreamPort = 5001;
 | 
				
			||||||
 | 
					            var downstreamHost = "localhost";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var config = new FileConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    new FileReRoute
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        UpstreamPathTemplate = "/",
 | 
				
			||||||
 | 
					                        DownstreamPathTemplate = "/ws",
 | 
				
			||||||
 | 
					                        DownstreamScheme = "ws",
 | 
				
			||||||
 | 
					                        DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            new FileHostAndPort
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                Host = downstreamHost,
 | 
				
			||||||
 | 
					                                Port = downstreamPort
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(_ => _steps.GivenThereIsAConfiguration(config))
 | 
				
			||||||
 | 
					                .And(_ => _steps.StartFakeOcelotWithWebSockets())
 | 
				
			||||||
 | 
					                .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
 | 
				
			||||||
 | 
					                .When(_ => StartClient("ws://localhost:5000/"))
 | 
				
			||||||
 | 
					                .Then(_ => _firstRecieved.Count.ShouldBe(10))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public async Task should_proxy_websocket_input_to_downstream_service_and_use_load_balancer()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var downstreamPort = 5005;
 | 
				
			||||||
 | 
					            var downstreamHost = "localhost";
 | 
				
			||||||
 | 
					            var secondDownstreamPort = 5006;
 | 
				
			||||||
 | 
					            var secondDownstreamHost = "localhost";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var config = new FileConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    new FileReRoute
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        UpstreamPathTemplate = "/",
 | 
				
			||||||
 | 
					                        DownstreamPathTemplate = "/ws",
 | 
				
			||||||
 | 
					                        DownstreamScheme = "ws",
 | 
				
			||||||
 | 
					                        DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            new FileHostAndPort
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                Host = downstreamHost,
 | 
				
			||||||
 | 
					                                Port = downstreamPort
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            new FileHostAndPort
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                Host = secondDownstreamHost,
 | 
				
			||||||
 | 
					                                Port = secondDownstreamPort
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        LoadBalancer = "RoundRobin"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(_ => _steps.GivenThereIsAConfiguration(config))
 | 
				
			||||||
 | 
					                .And(_ => _steps.StartFakeOcelotWithWebSockets())
 | 
				
			||||||
 | 
					                .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
 | 
				
			||||||
 | 
					                .And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}","/ws"))
 | 
				
			||||||
 | 
					                .When(_ => WhenIStartTheClients())
 | 
				
			||||||
 | 
					                .Then(_ => ThenBothDownstreamServicesAreCalled())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public async Task should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var downstreamPort = 5007;
 | 
				
			||||||
 | 
					            var downstreamHost = "localhost";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var secondDownstreamPort = 5008;
 | 
				
			||||||
 | 
					            var secondDownstreamHost = "localhost";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var serviceName = "websockets";
 | 
				
			||||||
 | 
					            var consulPort = 8509;
 | 
				
			||||||
 | 
					            var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}";
 | 
				
			||||||
 | 
					            var serviceEntryOne = new ServiceEntry()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Service = new AgentService()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Service = serviceName,
 | 
				
			||||||
 | 
					                    Address = downstreamHost,
 | 
				
			||||||
 | 
					                    Port = downstreamPort,
 | 
				
			||||||
 | 
					                    ID = Guid.NewGuid().ToString(),
 | 
				
			||||||
 | 
					                    Tags = new string[0]
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            var serviceEntryTwo = new ServiceEntry()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Service = new AgentService()
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    Service = serviceName,
 | 
				
			||||||
 | 
					                    Address = secondDownstreamHost,
 | 
				
			||||||
 | 
					                    Port = secondDownstreamPort,
 | 
				
			||||||
 | 
					                    ID = Guid.NewGuid().ToString(),
 | 
				
			||||||
 | 
					                    Tags = new string[0]
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var config = new FileConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    new FileReRoute
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        UpstreamPathTemplate = "/",
 | 
				
			||||||
 | 
					                        DownstreamPathTemplate = "/ws",
 | 
				
			||||||
 | 
					                        DownstreamScheme = "ws",
 | 
				
			||||||
 | 
					                        LoadBalancer = "RoundRobin",
 | 
				
			||||||
 | 
					                        ServiceName = serviceName,
 | 
				
			||||||
 | 
					                        UseServiceDiscovery = true
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                GlobalConfiguration = new FileGlobalConfiguration
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Host = "localhost",
 | 
				
			||||||
 | 
					                        Port = consulPort,
 | 
				
			||||||
 | 
					                        Type = "consul"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            this.Given(_ => _steps.GivenThereIsAConfiguration(config))
 | 
				
			||||||
 | 
					                .And(_ => _steps.StartFakeOcelotWithWebSockets())
 | 
				
			||||||
 | 
					                .And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName))
 | 
				
			||||||
 | 
					                .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
 | 
				
			||||||
 | 
					                .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
 | 
				
			||||||
 | 
					                .And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws"))
 | 
				
			||||||
 | 
					                .When(_ => WhenIStartTheClients())
 | 
				
			||||||
 | 
					                .Then(_ => ThenBothDownstreamServicesAreCalled())
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void ThenBothDownstreamServicesAreCalled()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _firstRecieved.Count.ShouldBe(10);
 | 
				
			||||||
 | 
					            _firstRecieved.ForEach(x =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                x.ShouldBe("test");
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _secondRecieved.Count.ShouldBe(10);
 | 
				
			||||||
 | 
					            _secondRecieved.ForEach(x =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                x.ShouldBe("chocolate");
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            foreach (var serviceEntry in serviceEntries)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _serviceEntries.Add(serviceEntry);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _fakeConsulBuilder = new WebHostBuilder()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .UseKestrel()
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .UseIISIntegration()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.Run(async context =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (context.Request.Path.Value == $"/v1/health/service/{serviceName}")
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            await context.Response.WriteJsonAsync(_serviceEntries);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _fakeConsulBuilder.Start();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task WhenIStartTheClients()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var firstClient = StartClient("ws://localhost:5000/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var secondClient = StartSecondClient("ws://localhost:5000/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Task.WhenAll(firstClient, secondClient);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task StartClient(string url)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var client = new ClientWebSocket();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            await client.ConnectAsync(new Uri(url), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var sending = Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string line = "test";
 | 
				
			||||||
 | 
					                for (int i = 0; i < 10; i++)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var bytes = Encoding.UTF8.GetBytes(line);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true,
 | 
				
			||||||
 | 
					                        CancellationToken.None);
 | 
				
			||||||
 | 
					                    await Task.Delay(10);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var receiving = Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var buffer = new byte[1024 * 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (true)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (result.MessageType == WebSocketMessageType.Text)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    else if (result.MessageType == WebSocketMessageType.Close)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Task.WhenAll(sending, receiving);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task StartSecondClient(string url)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            await Task.Delay(500);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var client = new ClientWebSocket();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await client.ConnectAsync(new Uri(url), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var sending = Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string line = "test";
 | 
				
			||||||
 | 
					                for (int i = 0; i < 10; i++)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var bytes = Encoding.UTF8.GetBytes(line);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true,
 | 
				
			||||||
 | 
					                        CancellationToken.None);
 | 
				
			||||||
 | 
					                    await Task.Delay(10);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var receiving = Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var buffer = new byte[1024 * 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (true)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (result.MessageType == WebSocketMessageType.Text)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        _secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    else if (result.MessageType == WebSocketMessageType.Close)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Task.WhenAll(sending, receiving);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task StartFakeDownstreamService(string url, string path)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _firstDownstreamHost = new WebHostBuilder()
 | 
				
			||||||
 | 
					                .ConfigureServices(s => { }).UseKestrel()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .ConfigureAppConfiguration((hostingContext, config) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
 | 
				
			||||||
 | 
					                    var env = hostingContext.HostingEnvironment;
 | 
				
			||||||
 | 
					                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
 | 
				
			||||||
 | 
					                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
 | 
				
			||||||
 | 
					                    config.AddEnvironmentVariables();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .ConfigureLogging((hostingContext, logging) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
 | 
				
			||||||
 | 
					                    logging.AddConsole();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.UseWebSockets();
 | 
				
			||||||
 | 
					                    app.Use(async (context, next) =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (context.Request.Path == path)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (context.WebSockets.IsWebSocketRequest)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
 | 
				
			||||||
 | 
					                                await Echo(webSocket);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                context.Response.StatusCode = 400;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            await next();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .UseIISIntegration().Build();
 | 
				
			||||||
 | 
					            await _firstDownstreamHost.StartAsync();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task StartSecondFakeDownstreamService(string url, string path)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _secondDownstreamHost = new WebHostBuilder()
 | 
				
			||||||
 | 
					                .ConfigureServices(s => { }).UseKestrel()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .ConfigureAppConfiguration((hostingContext, config) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
 | 
				
			||||||
 | 
					                    var env = hostingContext.HostingEnvironment;
 | 
				
			||||||
 | 
					                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
 | 
				
			||||||
 | 
					                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
 | 
				
			||||||
 | 
					                    config.AddEnvironmentVariables();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .ConfigureLogging((hostingContext, logging) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
 | 
				
			||||||
 | 
					                    logging.AddConsole();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.UseWebSockets();
 | 
				
			||||||
 | 
					                    app.Use(async (context, next) =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (context.Request.Path == path)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (context.WebSockets.IsWebSocketRequest)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
 | 
				
			||||||
 | 
					                                await Message(webSocket);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                context.Response.StatusCode = 400;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            await next();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .UseIISIntegration().Build();
 | 
				
			||||||
 | 
					            await _secondDownstreamHost.StartAsync();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task Echo(WebSocket webSocket)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var buffer = new byte[1024 * 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (!result.CloseStatus.HasValue)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception e)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Console.WriteLine(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task Message(WebSocket webSocket)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var buffer = new byte[1024 * 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var bytes = Encoding.UTF8.GetBytes("chocolate");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (!result.CloseStatus.HasValue)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await webSocket.SendAsync(new ArraySegment<byte>(bytes), result.MessageType, result.EndOfMessage, CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception e)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Console.WriteLine(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _steps.Dispose();
 | 
				
			||||||
 | 
					            _firstDownstreamHost?.Dispose();
 | 
				
			||||||
 | 
					            _secondDownstreamHost?.Dispose();
 | 
				
			||||||
 | 
					            _fakeConsulBuilder?.Dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -43,7 +43,7 @@ namespace Ocelot.UnitTests.Cache
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
            _cacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
 | 
					            _cacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
 | 
				
			||||||
            _downstreamContext = new DownstreamContext(new DefaultHttpContext());
 | 
					            _downstreamContext = new DownstreamContext(new DefaultHttpContext());
 | 
				
			||||||
            _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123");
 | 
					            _downstreamContext.DownstreamRequest = new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"));
 | 
				
			||||||
            _next = context => Task.CompletedTask;
 | 
					            _next = context => Task.CompletedTask;
 | 
				
			||||||
            _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _regionCreator);
 | 
					            _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _regionCreator);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ namespace Ocelot.UnitTests.Cache
 | 
				
			|||||||
            _logger = new Mock<IOcelotLogger>();
 | 
					            _logger = new Mock<IOcelotLogger>();
 | 
				
			||||||
            _loggerFactory.Setup(x => x.CreateLogger<OutputCacheMiddleware>()).Returns(_logger.Object);
 | 
					            _loggerFactory.Setup(x => x.CreateLogger<OutputCacheMiddleware>()).Returns(_logger.Object);
 | 
				
			||||||
            _next = context => Task.CompletedTask;
 | 
					            _next = context => Task.CompletedTask;
 | 
				
			||||||
            _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123");
 | 
					            _downstreamContext.DownstreamRequest = new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
 | 
				
			|||||||
    using Xunit;
 | 
					    using Xunit;
 | 
				
			||||||
    using Shouldly;
 | 
					    using Shouldly;
 | 
				
			||||||
    using Microsoft.AspNetCore.Http;
 | 
					    using Microsoft.AspNetCore.Http;
 | 
				
			||||||
 | 
					    using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class DownstreamUrlCreatorMiddlewareTests
 | 
					    public class DownstreamUrlCreatorMiddlewareTests
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -30,6 +31,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
 | 
				
			|||||||
        private DownstreamUrlCreatorMiddleware _middleware;
 | 
					        private DownstreamUrlCreatorMiddleware _middleware;
 | 
				
			||||||
        private DownstreamContext _downstreamContext;
 | 
					        private DownstreamContext _downstreamContext;
 | 
				
			||||||
        private OcelotRequestDelegate _next;
 | 
					        private OcelotRequestDelegate _next;
 | 
				
			||||||
 | 
					        private HttpRequestMessage _request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public DownstreamUrlCreatorMiddlewareTests()
 | 
					        public DownstreamUrlCreatorMiddlewareTests()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -38,7 +40,8 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
 | 
				
			|||||||
            _logger = new Mock<IOcelotLogger>();
 | 
					            _logger = new Mock<IOcelotLogger>();
 | 
				
			||||||
            _loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object);
 | 
					            _loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object);
 | 
				
			||||||
            _downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
 | 
					            _downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
 | 
				
			||||||
            _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
 | 
					            _request = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
 | 
				
			||||||
 | 
					            _downstreamContext.DownstreamRequest = new DownstreamRequest(_request);
 | 
				
			||||||
            _next = context => Task.CompletedTask;
 | 
					            _next = context => Task.CompletedTask;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -208,7 +211,9 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void GivenTheDownstreamRequestUriIs(string uri)
 | 
					        private void GivenTheDownstreamRequestUriIs(string uri)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamContext.DownstreamRequest.RequestUri = new Uri(uri);
 | 
					            _request.RequestUri = new Uri(uri);
 | 
				
			||||||
 | 
					            //todo - not sure if needed
 | 
				
			||||||
 | 
					            _downstreamContext.DownstreamRequest = new DownstreamRequest(_request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheUrlReplacerWillReturn(string path)
 | 
					        private void GivenTheUrlReplacerWillReturn(string path)
 | 
				
			||||||
@@ -221,7 +226,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void ThenTheDownstreamRequestUriIs(string expectedUri)
 | 
					        private void ThenTheDownstreamRequestUriIs(string expectedUri)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamContext.DownstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri);
 | 
					            _downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ using Shouldly;
 | 
				
			|||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.UnitTests.Headers
 | 
					namespace Ocelot.UnitTests.Headers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -18,7 +19,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly AddHeadersToRequest _addHeadersToRequest;
 | 
					        private readonly AddHeadersToRequest _addHeadersToRequest;
 | 
				
			||||||
        private readonly Mock<IClaimsParser> _parser;
 | 
					        private readonly Mock<IClaimsParser> _parser;
 | 
				
			||||||
        private readonly HttpRequestMessage _downstreamRequest;
 | 
					        private readonly DownstreamRequest _downstreamRequest;
 | 
				
			||||||
        private List<Claim> _claims;
 | 
					        private List<Claim> _claims;
 | 
				
			||||||
        private List<ClaimToThing> _configuration;
 | 
					        private List<ClaimToThing> _configuration;
 | 
				
			||||||
        private Response _result;
 | 
					        private Response _result;
 | 
				
			||||||
@@ -28,7 +29,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            _parser = new Mock<IClaimsParser>();
 | 
					            _parser = new Mock<IClaimsParser>();
 | 
				
			||||||
            _addHeadersToRequest = new AddHeadersToRequest(_parser.Object);
 | 
					            _addHeadersToRequest = new AddHeadersToRequest(_parser.Object);
 | 
				
			||||||
            _downstreamRequest = new HttpRequestMessage();
 | 
					            _downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ using Ocelot.Middleware;
 | 
				
			|||||||
namespace Ocelot.UnitTests.Headers
 | 
					namespace Ocelot.UnitTests.Headers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using System.Threading.Tasks;
 | 
					    using System.Threading.Tasks;
 | 
				
			||||||
 | 
					    using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class HttpHeadersTransformationMiddlewareTests
 | 
					    public class HttpHeadersTransformationMiddlewareTests
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -68,7 +69,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void GivenTheDownstreamRequestIs()
 | 
					        private void GivenTheDownstreamRequestIs()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamContext.DownstreamRequest = new HttpRequestMessage();
 | 
					            _downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheHttpResponseMessageIs()
 | 
					        private void GivenTheHttpResponseMessageIs()
 | 
				
			||||||
@@ -97,7 +98,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
 | 
					        private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _postReplacer.Verify(x => x.Replace(It.IsAny<HttpResponseMessage>(), It.IsAny<List<HeaderFindAndReplace>>(), It.IsAny<HttpRequestMessage>()), Times.Once);
 | 
					            _postReplacer.Verify(x => x.Replace(It.IsAny<HttpResponseMessage>(), It.IsAny<List<HeaderFindAndReplace>>(), It.IsAny<DownstreamRequest>()), Times.Once);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheFollowingRequest()
 | 
					        private void GivenTheFollowingRequest()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
    using Ocelot.Headers;
 | 
					    using Ocelot.Headers;
 | 
				
			||||||
    using Ocelot.Headers.Middleware;
 | 
					    using Ocelot.Headers.Middleware;
 | 
				
			||||||
    using Ocelot.Logging;
 | 
					    using Ocelot.Logging;
 | 
				
			||||||
 | 
					    using Ocelot.Request.Middleware;
 | 
				
			||||||
    using Ocelot.Responses;
 | 
					    using Ocelot.Responses;
 | 
				
			||||||
    using TestStack.BDDfy;
 | 
					    using TestStack.BDDfy;
 | 
				
			||||||
    using Xunit;
 | 
					    using Xunit;
 | 
				
			||||||
@@ -37,7 +38,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
            _loggerFactory.Setup(x => x.CreateLogger<HttpRequestHeadersBuilderMiddleware>()).Returns(_logger.Object);
 | 
					            _loggerFactory.Setup(x => x.CreateLogger<HttpRequestHeadersBuilderMiddleware>()).Returns(_logger.Object);
 | 
				
			||||||
            _next = context => Task.CompletedTask;
 | 
					            _next = context => Task.CompletedTask;
 | 
				
			||||||
            _middleware = new HttpRequestHeadersBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
 | 
					            _middleware = new HttpRequestHeadersBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
 | 
				
			||||||
            _downstreamContext.DownstreamRequest = new HttpRequestMessage();
 | 
					            _downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
@@ -81,7 +82,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
                .Setup(x => x.SetHeadersOnDownstreamRequest(
 | 
					                .Setup(x => x.SetHeadersOnDownstreamRequest(
 | 
				
			||||||
                    It.IsAny<List<ClaimToThing>>(),
 | 
					                    It.IsAny<List<ClaimToThing>>(),
 | 
				
			||||||
                    It.IsAny<IEnumerable<System.Security.Claims.Claim>>(),
 | 
					                    It.IsAny<IEnumerable<System.Security.Claims.Claim>>(),
 | 
				
			||||||
                    It.IsAny<HttpRequestMessage>()))
 | 
					                    It.IsAny<DownstreamRequest>()))
 | 
				
			||||||
                .Returns(new OkResponse());
 | 
					                .Returns(new OkResponse());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ using Moq;
 | 
				
			|||||||
using Ocelot.Infrastructure;
 | 
					using Ocelot.Infrastructure;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.UnitTests.Headers
 | 
					namespace Ocelot.UnitTests.Headers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -21,7 +22,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
        private HttpResponseHeaderReplacer _replacer;
 | 
					        private HttpResponseHeaderReplacer _replacer;
 | 
				
			||||||
        private List<HeaderFindAndReplace> _headerFindAndReplaces;
 | 
					        private List<HeaderFindAndReplace> _headerFindAndReplaces;
 | 
				
			||||||
        private Response _result;
 | 
					        private Response _result;
 | 
				
			||||||
        private HttpRequestMessage _request;
 | 
					        private DownstreamRequest _request;
 | 
				
			||||||
        private Mock<IBaseUrlFinder> _finder;
 | 
					        private Mock<IBaseUrlFinder> _finder;
 | 
				
			||||||
        private Mock<IRequestScopedDataRepository> _repo;
 | 
					        private Mock<IRequestScopedDataRepository> _repo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -69,7 +70,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var downstreamUrl = "http://downstream.com/";
 | 
					            var downstreamUrl = "http://downstream.com/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var request = new HttpRequestMessage();
 | 
					            var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
 | 
				
			||||||
            request.RequestUri = new System.Uri(downstreamUrl);
 | 
					            request.RequestUri = new System.Uri(downstreamUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = new HttpResponseMessage();
 | 
					            var response = new HttpResponseMessage();
 | 
				
			||||||
@@ -91,7 +92,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var downstreamUrl = "http://downstream.com/";
 | 
					            var downstreamUrl = "http://downstream.com/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var request = new HttpRequestMessage();
 | 
					            var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
 | 
				
			||||||
            request.RequestUri = new System.Uri(downstreamUrl);
 | 
					            request.RequestUri = new System.Uri(downstreamUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = new HttpResponseMessage();
 | 
					            var response = new HttpResponseMessage();
 | 
				
			||||||
@@ -113,7 +114,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var downstreamUrl = "http://downstream.com/test/product";
 | 
					            var downstreamUrl = "http://downstream.com/test/product";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var request = new HttpRequestMessage();
 | 
					            var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
 | 
				
			||||||
            request.RequestUri = new System.Uri(downstreamUrl);
 | 
					            request.RequestUri = new System.Uri(downstreamUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = new HttpResponseMessage();
 | 
					            var response = new HttpResponseMessage();
 | 
				
			||||||
@@ -135,7 +136,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var downstreamUrl = "http://downstream.com/test/product";
 | 
					            var downstreamUrl = "http://downstream.com/test/product";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var request = new HttpRequestMessage();
 | 
					            var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
 | 
				
			||||||
            request.RequestUri = new System.Uri(downstreamUrl);
 | 
					            request.RequestUri = new System.Uri(downstreamUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = new HttpResponseMessage();
 | 
					            var response = new HttpResponseMessage();
 | 
				
			||||||
@@ -157,7 +158,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var downstreamUrl = "http://downstream.com:123/test/product";
 | 
					            var downstreamUrl = "http://downstream.com:123/test/product";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var request = new HttpRequestMessage();
 | 
					            var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
 | 
				
			||||||
            request.RequestUri = new System.Uri(downstreamUrl);
 | 
					            request.RequestUri = new System.Uri(downstreamUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = new HttpResponseMessage();
 | 
					            var response = new HttpResponseMessage();
 | 
				
			||||||
@@ -179,7 +180,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var downstreamUrl = "http://downstream.com:123/test/product";
 | 
					            var downstreamUrl = "http://downstream.com:123/test/product";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var request = new HttpRequestMessage();
 | 
					            var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
 | 
				
			||||||
            request.RequestUri = new System.Uri(downstreamUrl);
 | 
					            request.RequestUri = new System.Uri(downstreamUrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var response = new HttpResponseMessage();
 | 
					            var response = new HttpResponseMessage();
 | 
				
			||||||
@@ -198,7 +199,7 @@ namespace Ocelot.UnitTests.Headers
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void GivenTheRequestIs(HttpRequestMessage request)
 | 
					        private void GivenTheRequestIs(HttpRequestMessage request)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _request = request;
 | 
					            _request = new DownstreamRequest(request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void ThenTheHeadersAreNotReplaced()
 | 
					        private void ThenTheHeadersAreNotReplaced()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ using Moq;
 | 
				
			|||||||
using Ocelot.Infrastructure;
 | 
					using Ocelot.Infrastructure;
 | 
				
			||||||
using Ocelot.Infrastructure.RequestData;
 | 
					using Ocelot.Infrastructure.RequestData;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
using Shouldly;
 | 
					using Shouldly;
 | 
				
			||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
@@ -43,8 +44,9 @@ namespace Ocelot.UnitTests.Infrastructure
 | 
				
			|||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_return_downstream_base_url_when_port_is_not_80_or_443()
 | 
					        public void should_return_downstream_base_url_when_port_is_not_80_or_443()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var request = new HttpRequestMessage();
 | 
					            var httpRequest = new HttpRequestMessage();
 | 
				
			||||||
            request.RequestUri = new Uri("http://www.bbc.co.uk");
 | 
					            httpRequest.RequestUri = new Uri("http://www.bbc.co.uk");
 | 
				
			||||||
 | 
					            var request = new DownstreamRequest(httpRequest);
 | 
				
			||||||
            var result = _placeholders.Get("{DownstreamBaseUrl}", request);
 | 
					            var result = _placeholders.Get("{DownstreamBaseUrl}", request);
 | 
				
			||||||
            result.Data.ShouldBe("http://www.bbc.co.uk/");
 | 
					            result.Data.ShouldBe("http://www.bbc.co.uk/");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -53,8 +55,9 @@ namespace Ocelot.UnitTests.Infrastructure
 | 
				
			|||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_return_downstream_base_url_when_port_is_80_or_443()
 | 
					        public void should_return_downstream_base_url_when_port_is_80_or_443()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
              var request = new HttpRequestMessage();
 | 
					            var httpRequest = new HttpRequestMessage();
 | 
				
			||||||
            request.RequestUri = new Uri("http://www.bbc.co.uk:123");
 | 
					            httpRequest.RequestUri = new Uri("http://www.bbc.co.uk:123");
 | 
				
			||||||
 | 
					            var request = new DownstreamRequest(httpRequest);
 | 
				
			||||||
            var result = _placeholders.Get("{DownstreamBaseUrl}", request);
 | 
					            var result = _placeholders.Get("{DownstreamBaseUrl}", request);
 | 
				
			||||||
            result.Data.ShouldBe("http://www.bbc.co.uk:123/");
 | 
					            result.Data.ShouldBe("http://www.bbc.co.uk:123/");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -62,7 +65,8 @@ namespace Ocelot.UnitTests.Infrastructure
 | 
				
			|||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
        public void should_return_key_does_not_exist_for_http_request_message()
 | 
					        public void should_return_key_does_not_exist_for_http_request_message()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var result = _placeholders.Get("{Test}", new System.Net.Http.HttpRequestMessage());
 | 
					            var request = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://west.com"));
 | 
				
			||||||
 | 
					            var result = _placeholders.Get("{Test}", request);
 | 
				
			||||||
            result.IsError.ShouldBeTrue();
 | 
					            result.IsError.ShouldBeTrue();
 | 
				
			||||||
            result.Errors[0].Message.ShouldBe("Unable to find placeholder called {Test}");
 | 
					            result.Errors[0].Message.ShouldBe("Unable to find placeholder called {Test}");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					using Ocelot.Infrastructure.Extensions;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Infrastructure
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class StringExtensionsTests
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_trim_start()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var test = "/string";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            test = test.TrimStart("/");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            test.ShouldBe("string");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public void should_return_source()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var test = "string";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            test = test.LastCharAsForwardSlash();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            test.ShouldBe("string/");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,6 +5,7 @@ using Ocelot.LoadBalancer.LoadBalancers;
 | 
				
			|||||||
using Ocelot.ServiceDiscovery;
 | 
					using Ocelot.ServiceDiscovery;
 | 
				
			||||||
using Shouldly;
 | 
					using Shouldly;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Providers;
 | 
				
			||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
    using Ocelot.LoadBalancer.LoadBalancers;
 | 
					    using Ocelot.LoadBalancer.LoadBalancers;
 | 
				
			||||||
    using Ocelot.LoadBalancer.Middleware;
 | 
					    using Ocelot.LoadBalancer.Middleware;
 | 
				
			||||||
    using Ocelot.Logging;
 | 
					    using Ocelot.Logging;
 | 
				
			||||||
 | 
					    using Ocelot.Request.Middleware;
 | 
				
			||||||
    using Ocelot.Responses;
 | 
					    using Ocelot.Responses;
 | 
				
			||||||
    using Ocelot.Values;
 | 
					    using Ocelot.Values;
 | 
				
			||||||
    using Shouldly;
 | 
					    using Shouldly;
 | 
				
			||||||
@@ -39,13 +40,13 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
            _loadBalancerHouse = new Mock<ILoadBalancerHouse>();
 | 
					            _loadBalancerHouse = new Mock<ILoadBalancerHouse>();
 | 
				
			||||||
            _loadBalancer = new Mock<ILoadBalancer>();
 | 
					            _loadBalancer = new Mock<ILoadBalancer>();
 | 
				
			||||||
            _loadBalancerHouse = new Mock<ILoadBalancerHouse>();
 | 
					            _loadBalancerHouse = new Mock<ILoadBalancerHouse>();
 | 
				
			||||||
            _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "");
 | 
					            _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "http://test.com/");
 | 
				
			||||||
            _downstreamContext = new DownstreamContext(new DefaultHttpContext());
 | 
					            _downstreamContext = new DownstreamContext(new DefaultHttpContext());
 | 
				
			||||||
            _loggerFactory = new Mock<IOcelotLoggerFactory>();
 | 
					            _loggerFactory = new Mock<IOcelotLoggerFactory>();
 | 
				
			||||||
            _logger = new Mock<IOcelotLogger>();
 | 
					            _logger = new Mock<IOcelotLogger>();
 | 
				
			||||||
            _loggerFactory.Setup(x => x.CreateLogger<LoadBalancingMiddleware>()).Returns(_logger.Object);
 | 
					            _loggerFactory.Setup(x => x.CreateLogger<LoadBalancingMiddleware>()).Returns(_logger.Object);
 | 
				
			||||||
            _next = context => Task.CompletedTask;
 | 
					            _next = context => Task.CompletedTask;
 | 
				
			||||||
            _downstreamContext.DownstreamRequest = _downstreamRequest;
 | 
					            _downstreamContext.DownstreamRequest = new DownstreamRequest(_downstreamRequest);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
@@ -122,6 +123,7 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
        private void GivenTheDownStreamUrlIs(string downstreamUrl)
 | 
					        private void GivenTheDownStreamUrlIs(string downstreamUrl)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamRequest.RequestUri = new System.Uri(downstreamUrl);
 | 
					            _downstreamRequest.RequestUri = new System.Uri(downstreamUrl);
 | 
				
			||||||
 | 
					            _downstreamContext.DownstreamRequest = new DownstreamRequest(_downstreamRequest);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheLoadBalancerReturnsAnError()
 | 
					        private void GivenTheLoadBalancerReturnsAnError()
 | 
				
			||||||
@@ -185,7 +187,7 @@ namespace Ocelot.UnitTests.LoadBalancer
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri)
 | 
					        private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamContext.DownstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri);
 | 
					            _downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ using Ocelot.Configuration.Builder;
 | 
				
			|||||||
using Ocelot.Errors;
 | 
					using Ocelot.Errors;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
using Ocelot.Middleware.Multiplexer;
 | 
					using Ocelot.Middleware.Multiplexer;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Ocelot.UnitTests.Responder;
 | 
					using Ocelot.UnitTests.Responder;
 | 
				
			||||||
using Shouldly;
 | 
					using Shouldly;
 | 
				
			||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
@@ -48,7 +49,7 @@ namespace Ocelot.UnitTests.Middleware
 | 
				
			|||||||
                    new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Bill says hi") },
 | 
					                    new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Bill says hi") },
 | 
				
			||||||
                DownstreamReRoute = billDownstreamReRoute,
 | 
					                DownstreamReRoute = billDownstreamReRoute,
 | 
				
			||||||
                Errors = new List<Error> { new AnyError() },
 | 
					                Errors = new List<Error> { new AnyError() },
 | 
				
			||||||
                DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.bbc.co.uk")),
 | 
					                DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, new Uri("http://www.bbc.co.uk"))),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var downstreamContexts = new List<DownstreamContext> { billDownstreamContext };
 | 
					            var downstreamContexts = new List<DownstreamContext> { billDownstreamContext };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,24 +12,27 @@ using TestStack.BDDfy;
 | 
				
			|||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
using System.Net.Http;
 | 
					using System.Net.Http;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.UnitTests.QueryStrings
 | 
					namespace Ocelot.UnitTests.QueryStrings
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class AddQueriesToRequestTests
 | 
					    public class AddQueriesToRequestTests
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly AddQueriesToRequest _addQueriesToRequest;
 | 
					        private readonly AddQueriesToRequest _addQueriesToRequest;
 | 
				
			||||||
        private HttpRequestMessage _downstreamRequest;
 | 
					        private DownstreamRequest _downstreamRequest;
 | 
				
			||||||
        private readonly Mock<IClaimsParser> _parser;
 | 
					        private readonly Mock<IClaimsParser> _parser;
 | 
				
			||||||
        private List<ClaimToThing> _configuration;
 | 
					        private List<ClaimToThing> _configuration;
 | 
				
			||||||
        private List<Claim> _claims;
 | 
					        private List<Claim> _claims;
 | 
				
			||||||
        private Response _result;
 | 
					        private Response _result;
 | 
				
			||||||
        private Response<string> _claimValue;
 | 
					        private Response<string> _claimValue;
 | 
				
			||||||
 | 
					        private HttpRequestMessage _request;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public AddQueriesToRequestTests()
 | 
					        public AddQueriesToRequestTests()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
 | 
					            _request = new HttpRequestMessage(HttpMethod.Post, "http://my.url/abc?q=123");
 | 
				
			||||||
            _parser = new Mock<IClaimsParser>();
 | 
					            _parser = new Mock<IClaimsParser>();
 | 
				
			||||||
            _addQueriesToRequest = new AddQueriesToRequest(_parser.Object);
 | 
					            _addQueriesToRequest = new AddQueriesToRequest(_parser.Object);
 | 
				
			||||||
            _downstreamRequest = new HttpRequestMessage(HttpMethod.Post, "http://my.url/abc?q=123");
 | 
					            _downstreamRequest = new DownstreamRequest(_request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
@@ -78,7 +81,7 @@ namespace Ocelot.UnitTests.QueryStrings
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void TheTheQueryStringIs(string expected)
 | 
					        private void TheTheQueryStringIs(string expected)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamRequest.RequestUri.Query.ShouldBe(expected);
 | 
					            _downstreamRequest.Query.ShouldBe(expected);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
@@ -123,7 +126,7 @@ namespace Ocelot.UnitTests.QueryStrings
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void ThenTheQueryIsAdded()
 | 
					        private void ThenTheQueryIsAdded()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var queries = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(_downstreamRequest.RequestUri.OriginalString);
 | 
					            var queries = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(_downstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString);
 | 
				
			||||||
            var query = queries.First(x => x.Key == "query-key");
 | 
					            var query = queries.First(x => x.Key == "query-key");
 | 
				
			||||||
            query.Value.First().ShouldBe(_claimValue.Data);
 | 
					            query.Value.First().ShouldBe(_claimValue.Data);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -140,15 +143,18 @@ namespace Ocelot.UnitTests.QueryStrings
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void GivenTheDownstreamRequestHasQueryString(string queryString)
 | 
					        private void GivenTheDownstreamRequestHasQueryString(string queryString)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamRequest = new HttpRequestMessage(HttpMethod.Post, $"http://my.url/abc{queryString}");
 | 
					            _request = new HttpRequestMessage(HttpMethod.Post, $"http://my.url/abc{queryString}");
 | 
				
			||||||
 | 
					            _downstreamRequest = new DownstreamRequest(_request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheDownstreamRequestHasQueryString(string key, string value)
 | 
					        private void GivenTheDownstreamRequestHasQueryString(string key, string value)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var newUri = Microsoft.AspNetCore.WebUtilities.QueryHelpers
 | 
					            var newUri = Microsoft.AspNetCore.WebUtilities.QueryHelpers
 | 
				
			||||||
                .AddQueryString(_downstreamRequest.RequestUri.OriginalString, key, value);
 | 
					                .AddQueryString(_downstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString, key, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _downstreamRequest.RequestUri = new Uri(newUri);
 | 
					            _request.RequestUri = new Uri(newUri);
 | 
				
			||||||
 | 
					            //todo - might not need to instanciate
 | 
				
			||||||
 | 
					            _downstreamRequest = new DownstreamRequest(_request);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private void GivenTheClaimParserReturns(Response<string> claimValue)
 | 
					        private void GivenTheClaimParserReturns(Response<string> claimValue)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ namespace Ocelot.UnitTests.QueryStrings
 | 
				
			|||||||
    using System.Security.Claims;
 | 
					    using System.Security.Claims;
 | 
				
			||||||
    using Microsoft.AspNetCore.Http;
 | 
					    using Microsoft.AspNetCore.Http;
 | 
				
			||||||
    using System.Threading.Tasks;
 | 
					    using System.Threading.Tasks;
 | 
				
			||||||
 | 
					    using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class QueryStringBuilderMiddlewareTests
 | 
					    public class QueryStringBuilderMiddlewareTests
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -36,7 +37,7 @@ namespace Ocelot.UnitTests.QueryStrings
 | 
				
			|||||||
            _loggerFactory.Setup(x => x.CreateLogger<QueryStringBuilderMiddleware>()).Returns(_logger.Object);
 | 
					            _loggerFactory.Setup(x => x.CreateLogger<QueryStringBuilderMiddleware>()).Returns(_logger.Object);
 | 
				
			||||||
            _next = context => Task.CompletedTask;
 | 
					            _next = context => Task.CompletedTask;
 | 
				
			||||||
            _addQueries = new Mock<IAddQueriesToRequest>();
 | 
					            _addQueries = new Mock<IAddQueriesToRequest>();
 | 
				
			||||||
            _downstreamContext.DownstreamRequest = new HttpRequestMessage();
 | 
					            _downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
 | 
				
			||||||
            _middleware = new QueryStringBuilderMiddleware(_next, _loggerFactory.Object, _addQueries.Object);
 | 
					            _middleware = new QueryStringBuilderMiddleware(_next, _loggerFactory.Object, _addQueries.Object);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,7 +75,7 @@ namespace Ocelot.UnitTests.QueryStrings
 | 
				
			|||||||
                .Setup(x => x.SetQueriesOnDownstreamRequest(
 | 
					                .Setup(x => x.SetQueriesOnDownstreamRequest(
 | 
				
			||||||
                    It.IsAny<List<ClaimToThing>>(),
 | 
					                    It.IsAny<List<ClaimToThing>>(),
 | 
				
			||||||
                    It.IsAny<IEnumerable<Claim>>(),
 | 
					                    It.IsAny<IEnumerable<Claim>>(),
 | 
				
			||||||
                    It.IsAny<HttpRequestMessage>()))
 | 
					                    It.IsAny<DownstreamRequest>()))
 | 
				
			||||||
                .Returns(new OkResponse());
 | 
					                .Returns(new OkResponse());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ namespace Ocelot.UnitTests.RateLimit
 | 
				
			|||||||
    using Microsoft.Extensions.Caching.Memory;
 | 
					    using Microsoft.Extensions.Caching.Memory;
 | 
				
			||||||
    using System.IO;
 | 
					    using System.IO;
 | 
				
			||||||
    using System.Threading.Tasks;
 | 
					    using System.Threading.Tasks;
 | 
				
			||||||
 | 
					    using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class ClientRateLimitMiddlewareTests
 | 
					    public class ClientRateLimitMiddlewareTests
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -100,7 +101,7 @@ namespace Ocelot.UnitTests.RateLimit
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                var request = new HttpRequestMessage(new HttpMethod("GET"), _url);
 | 
					                var request = new HttpRequestMessage(new HttpMethod("GET"), _url);
 | 
				
			||||||
                request.Headers.Add("ClientId", clientId);
 | 
					                request.Headers.Add("ClientId", clientId);
 | 
				
			||||||
                _downstreamContext.DownstreamRequest = request;
 | 
					                _downstreamContext.DownstreamRequest = new DownstreamRequest(request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
 | 
					                _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
 | 
				
			||||||
                _responseStatusCode = (int)_downstreamContext.HttpContext.Response.StatusCode;
 | 
					                _responseStatusCode = (int)_downstreamContext.HttpContext.Response.StatusCode;
 | 
				
			||||||
@@ -115,7 +116,7 @@ namespace Ocelot.UnitTests.RateLimit
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                var request = new HttpRequestMessage(new HttpMethod("GET"), _url);
 | 
					                var request = new HttpRequestMessage(new HttpMethod("GET"), _url);
 | 
				
			||||||
                request.Headers.Add("ClientId", clientId);
 | 
					                request.Headers.Add("ClientId", clientId);
 | 
				
			||||||
                _downstreamContext.DownstreamRequest = request;
 | 
					                _downstreamContext.DownstreamRequest = new DownstreamRequest(request);
 | 
				
			||||||
                _downstreamContext.HttpContext.Request.Headers.TryAdd("ClientId", clientId);
 | 
					                _downstreamContext.HttpContext.Request.Headers.TryAdd("ClientId", clientId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
 | 
					                _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,7 +88,7 @@ namespace Ocelot.UnitTests.Request
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private void GivenTheMapperWillReturnAMappedRequest()
 | 
					        private void GivenTheMapperWillReturnAMappedRequest()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _mappedRequest = new OkResponse<HttpRequestMessage>(new HttpRequestMessage());
 | 
					            _mappedRequest = new OkResponse<HttpRequestMessage>(new HttpRequestMessage(HttpMethod.Get, "http://www.bbc.co.uk"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _requestMapper
 | 
					            _requestMapper
 | 
				
			||||||
                .Setup(rm => rm.Map(It.IsAny<HttpRequest>()))
 | 
					                .Setup(rm => rm.Map(It.IsAny<HttpRequest>()))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ namespace Ocelot.UnitTests.RequestId
 | 
				
			|||||||
    using Shouldly;
 | 
					    using Shouldly;
 | 
				
			||||||
    using TestStack.BDDfy;
 | 
					    using TestStack.BDDfy;
 | 
				
			||||||
    using Xunit;
 | 
					    using Xunit;
 | 
				
			||||||
 | 
					    using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class ReRouteRequestIdMiddlewareTests
 | 
					    public class ReRouteRequestIdMiddlewareTests
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -35,7 +36,7 @@ namespace Ocelot.UnitTests.RequestId
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public ReRouteRequestIdMiddlewareTests()
 | 
					        public ReRouteRequestIdMiddlewareTests()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _downstreamRequest = new HttpRequestMessage();
 | 
					            _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "http://test.com");
 | 
				
			||||||
            _repo = new Mock<IRequestScopedDataRepository>();
 | 
					            _repo = new Mock<IRequestScopedDataRepository>();
 | 
				
			||||||
            _downstreamContext = new DownstreamContext(new DefaultHttpContext());
 | 
					            _downstreamContext = new DownstreamContext(new DefaultHttpContext());
 | 
				
			||||||
            _loggerFactory = new Mock<IOcelotLoggerFactory>();
 | 
					            _loggerFactory = new Mock<IOcelotLoggerFactory>();
 | 
				
			||||||
@@ -47,7 +48,7 @@ namespace Ocelot.UnitTests.RequestId
 | 
				
			|||||||
                return Task.CompletedTask;
 | 
					                return Task.CompletedTask;
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            _middleware = new ReRouteRequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object);
 | 
					            _middleware = new ReRouteRequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object);
 | 
				
			||||||
            _downstreamContext.DownstreamRequest = _downstreamRequest;
 | 
					            _downstreamContext.DownstreamRequest = new DownstreamRequest(_downstreamRequest);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ using Ocelot.Configuration;
 | 
				
			|||||||
using Ocelot.Configuration.Builder;
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
using Ocelot.Middleware;
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
using Ocelot.Requester;
 | 
					using Ocelot.Requester;
 | 
				
			||||||
using Ocelot.Responses;
 | 
					using Ocelot.Responses;
 | 
				
			||||||
using Shouldly;
 | 
					using Shouldly;
 | 
				
			||||||
@@ -170,7 +171,7 @@ namespace Ocelot.UnitTests.Requester
 | 
				
			|||||||
            var context = new DownstreamContext(new DefaultHttpContext())
 | 
					            var context = new DownstreamContext(new DefaultHttpContext())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                DownstreamReRoute = downstream,
 | 
					                DownstreamReRoute = downstream,
 | 
				
			||||||
                DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://localhost:5003") },
 | 
					                DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:5003") }),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _context = context;
 | 
					            _context = context;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,13 +12,16 @@ using Ocelot.Middleware;
 | 
				
			|||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
using Shouldly;
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using Ocelot.Request.Middleware;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Ocelot.UnitTests.Requester
 | 
					namespace Ocelot.UnitTests.Requester
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class HttpClientHttpRequesterTest
 | 
					    public class HttpClientHttpRequesterTest
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        private readonly Mock<IHttpClientCache> _cacheHandlers;
 | 
					        private readonly Mock<IHttpClientCache> _cacheHandlers;
 | 
				
			||||||
        private Mock<IDelegatingHandlerHandlerFactory> _house;
 | 
					        private Mock<IDelegatingHandlerHandlerFactory> _factory;
 | 
				
			||||||
        private Response<HttpResponseMessage> _response;
 | 
					        private Response<HttpResponseMessage> _response;
 | 
				
			||||||
        private readonly HttpClientHttpRequester _httpClientRequester;
 | 
					        private readonly HttpClientHttpRequester _httpClientRequester;
 | 
				
			||||||
        private DownstreamContext _request;
 | 
					        private DownstreamContext _request;
 | 
				
			||||||
@@ -27,8 +30,8 @@ namespace Ocelot.UnitTests.Requester
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public HttpClientHttpRequesterTest()
 | 
					        public HttpClientHttpRequesterTest()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _house = new Mock<IDelegatingHandlerHandlerFactory>();
 | 
					            _factory = new Mock<IDelegatingHandlerHandlerFactory>();
 | 
				
			||||||
            _house.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(new List<Func<DelegatingHandler>>()));
 | 
					            _factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(new List<Func<DelegatingHandler>>()));
 | 
				
			||||||
            _logger = new Mock<IOcelotLogger>();
 | 
					            _logger = new Mock<IOcelotLogger>();
 | 
				
			||||||
            _loggerFactory = new Mock<IOcelotLoggerFactory>();
 | 
					            _loggerFactory = new Mock<IOcelotLoggerFactory>();
 | 
				
			||||||
            _loggerFactory
 | 
					            _loggerFactory
 | 
				
			||||||
@@ -38,7 +41,7 @@ namespace Ocelot.UnitTests.Requester
 | 
				
			|||||||
            _httpClientRequester = new HttpClientHttpRequester(
 | 
					            _httpClientRequester = new HttpClientHttpRequester(
 | 
				
			||||||
                _loggerFactory.Object, 
 | 
					                _loggerFactory.Object, 
 | 
				
			||||||
                _cacheHandlers.Object, 
 | 
					                _cacheHandlers.Object, 
 | 
				
			||||||
                _house.Object);            
 | 
					                _factory.Object);            
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        [Fact]
 | 
					        [Fact]
 | 
				
			||||||
@@ -50,10 +53,11 @@ namespace Ocelot.UnitTests.Requester
 | 
				
			|||||||
            var context = new DownstreamContext(new DefaultHttpContext())
 | 
					            var context = new DownstreamContext(new DefaultHttpContext())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                DownstreamReRoute = reRoute,
 | 
					                DownstreamReRoute = reRoute,
 | 
				
			||||||
                DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") },
 | 
					                DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x=>x.GivenTheRequestIs(context))
 | 
					            this.Given(x=>x.GivenTheRequestIs(context))
 | 
				
			||||||
 | 
					                .And(x => GivenTheHouseReturnsOkHandler())
 | 
				
			||||||
                .When(x=>x.WhenIGetResponse())
 | 
					                .When(x=>x.WhenIGetResponse())
 | 
				
			||||||
                .Then(x => x.ThenTheResponseIsCalledCorrectly())
 | 
					                .Then(x => x.ThenTheResponseIsCalledCorrectly())
 | 
				
			||||||
                .BDDfy();
 | 
					                .BDDfy();
 | 
				
			||||||
@@ -68,7 +72,7 @@ namespace Ocelot.UnitTests.Requester
 | 
				
			|||||||
            var context = new DownstreamContext(new DefaultHttpContext())
 | 
					            var context = new DownstreamContext(new DefaultHttpContext())
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                DownstreamReRoute = reRoute,
 | 
					                DownstreamReRoute = reRoute,
 | 
				
			||||||
                DownstreamRequest = new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") },
 | 
					                DownstreamRequest = new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.Given(x => x.GivenTheRequestIs(context))
 | 
					            this.Given(x => x.GivenTheRequestIs(context))
 | 
				
			||||||
@@ -96,5 +100,23 @@ namespace Ocelot.UnitTests.Requester
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            _response.IsError.ShouldBeTrue();
 | 
					            _response.IsError.ShouldBeTrue();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private void GivenTheHouseReturnsOkHandler()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var handlers = new List<Func<DelegatingHandler>>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                () => new OkDelegatingHandler()
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class OkDelegatingHandler : DelegatingHandler
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return Task.FromResult(new HttpResponseMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }  
 | 
					    }  
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using Ocelot.ServiceDiscovery;
 | 
					using Ocelot.ServiceDiscovery;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Providers;
 | 
				
			||||||
using Ocelot.Values;
 | 
					using Ocelot.Values;
 | 
				
			||||||
using Shouldly;
 | 
					using Shouldly;
 | 
				
			||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,8 @@ using Microsoft.AspNetCore.Http;
 | 
				
			|||||||
using Moq;
 | 
					using Moq;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
using Ocelot.ServiceDiscovery;
 | 
					using Ocelot.ServiceDiscovery;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Providers;
 | 
				
			||||||
using Ocelot.Values;
 | 
					using Ocelot.Values;
 | 
				
			||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,7 @@
 | 
				
			|||||||
namespace Ocelot.UnitTests.ServiceDiscovery
 | 
					using Ocelot.ServiceDiscovery.Configuration;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Providers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.ServiceDiscovery
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using System;
 | 
					    using System;
 | 
				
			||||||
    using System.Collections.Generic;
 | 
					    using System.Collections.Generic;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ using Ocelot.Configuration;
 | 
				
			|||||||
using Ocelot.Configuration.Builder;
 | 
					using Ocelot.Configuration.Builder;
 | 
				
			||||||
using Ocelot.Logging;
 | 
					using Ocelot.Logging;
 | 
				
			||||||
using Ocelot.ServiceDiscovery;
 | 
					using Ocelot.ServiceDiscovery;
 | 
				
			||||||
 | 
					using Ocelot.ServiceDiscovery.Providers;
 | 
				
			||||||
using Shouldly;
 | 
					using Shouldly;
 | 
				
			||||||
using TestStack.BDDfy;
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
using Xunit;
 | 
					using Xunit;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,239 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Net.WebSockets;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Consul;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Builder;
 | 
				
			||||||
 | 
					using Microsoft.AspNetCore.Hosting;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Configuration;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.DependencyInjection;
 | 
				
			||||||
 | 
					using Microsoft.Extensions.Logging;
 | 
				
			||||||
 | 
					using Newtonsoft.Json;
 | 
				
			||||||
 | 
					using Ocelot.Configuration.File;
 | 
				
			||||||
 | 
					using Ocelot.DependencyInjection;
 | 
				
			||||||
 | 
					using Ocelot.Middleware;
 | 
				
			||||||
 | 
					using Shouldly;
 | 
				
			||||||
 | 
					using TestStack.BDDfy;
 | 
				
			||||||
 | 
					using Xunit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Ocelot.UnitTests.Websockets
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class WebSocketsProxyMiddlewareTests : IDisposable
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private IWebHost _firstDownstreamHost;
 | 
				
			||||||
 | 
					        private readonly List<string> _firstRecieved;
 | 
				
			||||||
 | 
					        private WebHostBuilder _ocelotBuilder;
 | 
				
			||||||
 | 
					        private IWebHost _ocelotHost;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public WebSocketsProxyMiddlewareTests()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _firstRecieved = new List<string>();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [Fact]
 | 
				
			||||||
 | 
					        public async Task should_proxy_websocket_input_to_downstream_service()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var downstreamPort = 5001;
 | 
				
			||||||
 | 
					            var downstreamHost = "localhost";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var config = new FileConfiguration
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                ReRoutes = new List<FileReRoute>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    new FileReRoute
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        UpstreamPathTemplate = "/",
 | 
				
			||||||
 | 
					                        DownstreamPathTemplate = "/ws",
 | 
				
			||||||
 | 
					                        DownstreamScheme = "ws",
 | 
				
			||||||
 | 
					                        DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            new FileHostAndPort
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                Host = downstreamHost,
 | 
				
			||||||
 | 
					                                Port = downstreamPort
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.Given(_ => GivenThereIsAConfiguration(config))
 | 
				
			||||||
 | 
					                .And(_ => StartFakeOcelotWithWebSockets())
 | 
				
			||||||
 | 
					                .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws"))
 | 
				
			||||||
 | 
					                .When(_ => StartClient("ws://localhost:5000/"))
 | 
				
			||||||
 | 
					                .Then(_ => _firstRecieved.Count.ShouldBe(10))
 | 
				
			||||||
 | 
					                .BDDfy();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void Dispose()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _firstDownstreamHost?.Dispose();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task StartFakeOcelotWithWebSockets()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _ocelotBuilder = new WebHostBuilder();
 | 
				
			||||||
 | 
					            _ocelotBuilder.ConfigureServices(s =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                s.AddSingleton(_ocelotBuilder);
 | 
				
			||||||
 | 
					                s.AddOcelot();
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            _ocelotBuilder.UseKestrel()
 | 
				
			||||||
 | 
					                .UseUrls("http://localhost:5000")
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .ConfigureAppConfiguration((hostingContext, config) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
 | 
				
			||||||
 | 
					                    var env = hostingContext.HostingEnvironment;
 | 
				
			||||||
 | 
					                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
 | 
				
			||||||
 | 
					                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
 | 
				
			||||||
 | 
					                    config.AddJsonFile("configuration.json");
 | 
				
			||||||
 | 
					                    config.AddEnvironmentVariables();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .ConfigureLogging((hostingContext, logging) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
 | 
				
			||||||
 | 
					                    logging.AddConsole();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.UseWebSockets();
 | 
				
			||||||
 | 
					                    app.UseOcelot().Wait();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .UseIISIntegration();
 | 
				
			||||||
 | 
					            _ocelotHost = _ocelotBuilder.Build();
 | 
				
			||||||
 | 
					            await _ocelotHost.StartAsync();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var configurationPath = Path.Combine(AppContext.BaseDirectory, "configuration.json");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (File.Exists(configurationPath))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                File.Delete(configurationPath);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            File.WriteAllText(configurationPath, jsonConfiguration);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task StartFakeDownstreamService(string url, string path)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _firstDownstreamHost = new WebHostBuilder()
 | 
				
			||||||
 | 
					                .ConfigureServices(s => { }).UseKestrel()
 | 
				
			||||||
 | 
					                .UseUrls(url)
 | 
				
			||||||
 | 
					                .UseContentRoot(Directory.GetCurrentDirectory())
 | 
				
			||||||
 | 
					                .ConfigureAppConfiguration((hostingContext, config) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
 | 
				
			||||||
 | 
					                    var env = hostingContext.HostingEnvironment;
 | 
				
			||||||
 | 
					                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
 | 
				
			||||||
 | 
					                        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
 | 
				
			||||||
 | 
					                    config.AddEnvironmentVariables();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .ConfigureLogging((hostingContext, logging) =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
 | 
				
			||||||
 | 
					                    logging.AddConsole();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .Configure(app =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    app.UseWebSockets();
 | 
				
			||||||
 | 
					                    app.Use(async (context, next) =>
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if (context.Request.Path == path)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            if (context.WebSockets.IsWebSocketRequest)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
 | 
				
			||||||
 | 
					                                await Echo(webSocket);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            else
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                context.Response.StatusCode = 400;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            await next();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .UseIISIntegration().Build();
 | 
				
			||||||
 | 
					            await _firstDownstreamHost.StartAsync();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task StartClient(string url)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var client = new ClientWebSocket();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await client.ConnectAsync(new Uri(url), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var sending = Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                string line = "test";
 | 
				
			||||||
 | 
					                for (int i = 0; i < 10; i++)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var bytes = Encoding.UTF8.GetBytes(line);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    await client.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true,
 | 
				
			||||||
 | 
					                        CancellationToken.None);
 | 
				
			||||||
 | 
					                    await Task.Delay(10);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var receiving = Task.Run(async () =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var buffer = new byte[1024 * 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (true)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var result = await client.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (result.MessageType == WebSocketMessageType.Text)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    else if (result.MessageType == WebSocketMessageType.Close)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            await Task.WhenAll(sending, receiving);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private async Task Echo(WebSocket webSocket)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            try
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var buffer = new byte[1024 * 4];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (!result.CloseStatus.HasValue)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (Exception e)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Console.WriteLine(e);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user