mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Feature/websockets (#273)
* #212 - hacked websockets proxy together * faffing around * #212 hacking away :( * #212 websockets proxy middleware working * #212 map when for webockets working * #212 some test refactor * #212 temp commit * #212 websockets proxy working, tests passing...need to do some tidying and write docs * #212 more code coverage * #212 docs for websockets * #212 updated readme * #212 tidying up after websockets refactoring * #212 tidying up after websockets refactoring * #212 tidying up after websockets refactoring * stuck a warning in about logging levels into docs!
This commit is contained in:
parent
4493b22d0d
commit
463a7bdab4
@ -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
|
||||||
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@ namespace Ocelot.Values
|
|||||||
Priority = priority;
|
Priority = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user