mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 00:38:16 +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:
@ -37,7 +37,7 @@ namespace Ocelot.Cache.Middleware
|
||||
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);
|
||||
|
||||
|
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
|
||||
{
|
||||
private IPlaceholders _placeholders;
|
||||
private IOcelotLogger _logger;
|
||||
private readonly IPlaceholders _placeholders;
|
||||
private readonly IOcelotLogger _logger;
|
||||
|
||||
public HeaderFindAndReplaceCreator(IPlaceholders placeholders, IOcelotLoggerFactory factory)
|
||||
{
|
||||
_logger = factory.CreateLogger<HeaderFindAndReplaceCreator>();;
|
||||
_logger = factory.CreateLogger<HeaderFindAndReplaceCreator>();
|
||||
_placeholders = placeholders;
|
||||
}
|
||||
|
||||
|
@ -14,21 +14,10 @@ namespace Ocelot.Configuration.Creator
|
||||
Downstream = downstream;
|
||||
}
|
||||
|
||||
public List<HeaderFindAndReplace> Upstream { get; private set; }
|
||||
public List<HeaderFindAndReplace> Upstream { get; }
|
||||
|
||||
public List<HeaderFindAndReplace> Downstream { get; private set; }
|
||||
public List<AddHeader> AddHeadersToDownstream {get;private set;}
|
||||
}
|
||||
public List<HeaderFindAndReplace> Downstream { get; }
|
||||
|
||||
public class AddHeader
|
||||
{
|
||||
public AddHeader(string key, string value)
|
||||
{
|
||||
this.Key = key;
|
||||
this.Value = value;
|
||||
|
||||
}
|
||||
public string Key { get; private set; }
|
||||
public string Value { get; private set; }
|
||||
public List<AddHeader> AddHeadersToDownstream { get; }
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
|
||||
namespace Ocelot.Configuration.Repository
|
||||
{
|
||||
|
@ -4,5 +4,4 @@
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
UriBuilder uriBuilder;
|
||||
|
||||
context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme;
|
||||
|
||||
if (ServiceFabricRequest(context))
|
||||
{
|
||||
uriBuilder = CreateServiceFabricUri(context, dsPath);
|
||||
var pathAndQuery = CreateServiceFabricUri(context, dsPath);
|
||||
context.DownstreamRequest.AbsolutePath = pathAndQuery.path;
|
||||
context.DownstreamRequest.Query = pathAndQuery.query;
|
||||
}
|
||||
else
|
||||
{
|
||||
uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri)
|
||||
{
|
||||
Path = dsPath.Data.Value,
|
||||
Scheme = context.DownstreamReRoute.DownstreamScheme
|
||||
};
|
||||
context.DownstreamRequest.AbsolutePath = dsPath.Data.Value;
|
||||
}
|
||||
|
||||
context.DownstreamRequest.RequestUri = uriBuilder.Uri;
|
||||
|
||||
_logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", context.DownstreamRequest.RequestUri);
|
||||
_logger.LogDebug("downstream url is {context.DownstreamRequest}", context.DownstreamRequest);
|
||||
|
||||
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 scheme = context.DownstreamReRoute.DownstreamScheme;
|
||||
var host = context.DownstreamRequest.RequestUri.Host;
|
||||
var port = context.DownstreamRequest.RequestUri.Port;
|
||||
var query = context.DownstreamRequest.Query;
|
||||
var serviceFabricPath = $"/{context.DownstreamReRoute.ServiceName + dsPath.Data.Value}";
|
||||
|
||||
Uri uri;
|
||||
|
||||
if (RequestForStatefullService(query))
|
||||
{
|
||||
uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}{query}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var split = string.IsNullOrEmpty(query) ? "?" : "&";
|
||||
uri = new Uri($"{scheme}://{host}:{port}{serviceFabricPath}{query}{split}cmd=instance");
|
||||
return (serviceFabricPath, query);
|
||||
}
|
||||
|
||||
return new UriBuilder(uri);
|
||||
var split = string.IsNullOrEmpty(query) ? "?" : "&";
|
||||
return (serviceFabricPath, $"{query}{split}cmd=instance");
|
||||
}
|
||||
|
||||
private static bool ServiceFabricRequest(DownstreamContext context)
|
||||
|
@ -5,6 +5,7 @@ using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Ocelot.Responses;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Request.Middleware;
|
||||
|
||||
namespace Ocelot.Headers
|
||||
{
|
||||
@ -17,7 +18,7 @@ namespace Ocelot.Headers
|
||||
_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)
|
||||
{
|
||||
|
@ -1,23 +1,22 @@
|
||||
namespace Ocelot.Headers
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
|
||||
public class AddHeadersToResponse : IAddHeadersToResponse
|
||||
{
|
||||
private IPlaceholders _placeholders;
|
||||
private IOcelotLogger _logger;
|
||||
private readonly IPlaceholders _placeholders;
|
||||
private readonly IOcelotLogger _logger;
|
||||
|
||||
public AddHeadersToResponse(IPlaceholders placeholders, IOcelotLoggerFactory factory)
|
||||
{
|
||||
_logger = factory.CreateLogger<AddHeadersToResponse>();
|
||||
_placeholders = placeholders;
|
||||
}
|
||||
|
||||
public void Add(List<AddHeader> addHeaders, HttpResponseMessage response)
|
||||
{
|
||||
foreach(var add in addHeaders)
|
||||
|
@ -5,6 +5,7 @@ using System.Net.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Headers
|
||||
@ -18,7 +19,7 @@ namespace Ocelot.Headers
|
||||
_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)
|
||||
{
|
||||
|
@ -6,10 +6,11 @@
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
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.Net.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Headers
|
||||
{
|
||||
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 Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Infrastructure
|
||||
@ -6,6 +7,6 @@ namespace Ocelot.Infrastructure
|
||||
public interface IPlaceholders
|
||||
{
|
||||
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 Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Infrastructure
|
||||
{
|
||||
public class Placeholders : IPlaceholders
|
||||
{
|
||||
private Dictionary<string, Func<Response<string>>> _placeholders;
|
||||
private Dictionary<string, Func<HttpRequestMessage, string>> _requestPlaceholders;
|
||||
private readonly Dictionary<string, Func<Response<string>>> _placeholders;
|
||||
private readonly Dictionary<string, Func<DownstreamRequest, string>> _requestPlaceholders;
|
||||
private readonly IBaseUrlFinder _finder;
|
||||
private readonly IRequestScopedDataRepository _repo;
|
||||
|
||||
@ -30,13 +31,13 @@ namespace Ocelot.Infrastructure
|
||||
return new OkResponse<string>(traceId.Data);
|
||||
});
|
||||
|
||||
_requestPlaceholders = new Dictionary<string, Func<HttpRequestMessage, string>>();
|
||||
_requestPlaceholders = new Dictionary<string, Func<DownstreamRequest, string>>();
|
||||
_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}/";
|
||||
@ -57,7 +58,7 @@ namespace Ocelot.Infrastructure
|
||||
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))
|
||||
{
|
||||
@ -67,4 +68,4 @@ namespace Ocelot.Infrastructure
|
||||
return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.QueryStrings.Middleware;
|
||||
|
||||
namespace Ocelot.LoadBalancer.Middleware
|
||||
{
|
||||
@ -43,17 +39,13 @@ namespace Ocelot.LoadBalancer.Middleware
|
||||
return;
|
||||
}
|
||||
|
||||
var uriBuilder = new UriBuilder(context.DownstreamRequest.RequestUri);
|
||||
|
||||
uriBuilder.Host = hostAndPort.Data.DownstreamHost;
|
||||
context.DownstreamRequest.Host = hostAndPort.Data.DownstreamHost;
|
||||
|
||||
if (hostAndPort.Data.DownstreamPort > 0)
|
||||
{
|
||||
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
|
||||
context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort;
|
||||
}
|
||||
|
||||
context.DownstreamRequest.RequestUri = uriBuilder.Uri;
|
||||
|
||||
try
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Request.Middleware;
|
||||
|
||||
namespace Ocelot.Middleware
|
||||
{
|
||||
@ -19,7 +21,7 @@ namespace Ocelot.Middleware
|
||||
public ServiceProviderConfiguration ServiceProviderConfiguration {get; set;}
|
||||
public HttpContext HttpContext { get; private set; }
|
||||
public DownstreamReRoute DownstreamReRoute { get; set; }
|
||||
public HttpRequestMessage DownstreamRequest { get; set; }
|
||||
public DownstreamRequest DownstreamRequest { get; set; }
|
||||
public HttpResponseMessage DownstreamResponse { get; set; }
|
||||
public List<Error> Errors { get;set; }
|
||||
public bool IsError => Errors.Count > 0;
|
||||
|
@ -11,5 +11,6 @@ namespace Ocelot.Middleware.Pipeline
|
||||
IServiceProvider ApplicationServices { get; }
|
||||
OcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware);
|
||||
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>>();
|
||||
}
|
||||
|
||||
public OcelotPipelineBuilder(IOcelotPipelineBuilder builder)
|
||||
{
|
||||
ApplicationServices = builder.ApplicationServices;
|
||||
_middlewares = new List<Func<OcelotRequestDelegate, OcelotRequestDelegate>>();
|
||||
}
|
||||
|
||||
public IServiceProvider ApplicationServices { get; }
|
||||
|
||||
public OcelotPipelineBuilder Use(Func<OcelotRequestDelegate, OcelotRequestDelegate> middleware)
|
||||
@ -42,5 +48,10 @@ namespace Ocelot.Middleware.Pipeline
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
public IOcelotPipelineBuilder New()
|
||||
{
|
||||
return new OcelotPipelineBuilder(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Ocelot.Middleware.Pipeline
|
||||
{
|
||||
using Predicate = Func<DownstreamContext, bool>;
|
||||
|
||||
public static class OcelotPipelineBuilderExtensions
|
||||
{
|
||||
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)
|
||||
{
|
||||
var middleware = typeof(T);
|
||||
|
@ -15,18 +15,30 @@ using Ocelot.Request.Middleware;
|
||||
using Ocelot.Requester.Middleware;
|
||||
using Ocelot.RequestId.Middleware;
|
||||
using Ocelot.Responder.Middleware;
|
||||
using Ocelot.WebSockets.Middleware;
|
||||
|
||||
namespace Ocelot.Middleware.Pipeline
|
||||
{
|
||||
public static class OcelotPipelineExtensions
|
||||
{
|
||||
public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuilder builder,
|
||||
OcelotPipelineConfiguration pipelineConfiguration = null)
|
||||
OcelotPipelineConfiguration pipelineConfiguration)
|
||||
{
|
||||
// This is registered to catch any global exceptions that are not handled
|
||||
// It also sets the Request Id if anything is set globally
|
||||
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.
|
||||
builder.UseIfNotNull(pipelineConfiguration.PreErrorResponderMiddleware);
|
||||
|
||||
|
@ -7,6 +7,7 @@ using Ocelot.Responses;
|
||||
using System.Security.Claims;
|
||||
using System.Net.Http;
|
||||
using System;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System.Text;
|
||||
|
||||
@ -21,9 +22,9 @@ namespace Ocelot.QueryStrings
|
||||
_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)
|
||||
{
|
||||
@ -46,11 +47,7 @@ namespace Ocelot.QueryStrings
|
||||
}
|
||||
}
|
||||
|
||||
var uriBuilder = new UriBuilder(downstreamRequest.RequestUri);
|
||||
|
||||
uriBuilder.Query = ConvertDictionaryToQueryString(queryDictionary);
|
||||
|
||||
downstreamRequest.RequestUri = uriBuilder.Uri;
|
||||
downstreamRequest.Query = ConvertDictionaryToQueryString(queryDictionary);
|
||||
|
||||
return new OkResponse();
|
||||
}
|
||||
@ -94,4 +91,4 @@ namespace Ocelot.QueryStrings
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,12 @@ using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using Ocelot.Request.Middleware;
|
||||
|
||||
namespace Ocelot.QueryStrings
|
||||
{
|
||||
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
|
||||
{
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
@ -31,9 +32,9 @@ namespace Ocelot.Request.Middleware
|
||||
return;
|
||||
}
|
||||
|
||||
context.DownstreamRequest = downstreamRequest.Data;
|
||||
context.DownstreamRequest = new DownstreamRequest(downstreamRequest.Data);
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
|
||||
namespace Ocelot.RequestId.Middleware
|
||||
{
|
||||
@ -82,7 +83,7 @@ namespace Ocelot.RequestId.Middleware
|
||||
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);
|
||||
}
|
||||
|
@ -78,9 +78,7 @@ namespace Ocelot.Requester
|
||||
|
||||
private string GetCacheKey(DownstreamContext request)
|
||||
{
|
||||
var baseUrl = $"{request.DownstreamRequest.RequestUri.Scheme}://{request.DownstreamRequest.RequestUri.Authority}{request.DownstreamRequest.RequestUri.AbsolutePath}";
|
||||
|
||||
return baseUrl;
|
||||
return request.DownstreamRequest.OriginalString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ namespace Ocelot.Requester
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.SendAsync(context.DownstreamRequest);
|
||||
var response = await httpClient.SendAsync(context.DownstreamRequest.ToHttpRequestMessage());
|
||||
return new OkResponse<HttpResponseMessage>(response);
|
||||
}
|
||||
catch (TimeoutRejectedException exception)
|
||||
|
@ -1,16 +1,16 @@
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
public class ConsulRegistryConfiguration
|
||||
{
|
||||
public ConsulRegistryConfiguration(string hostName, int port, string keyOfServiceInConsul)
|
||||
{
|
||||
HostName = hostName;
|
||||
Port = port;
|
||||
KeyOfServiceInConsul = keyOfServiceInConsul;
|
||||
}
|
||||
|
||||
public string KeyOfServiceInConsul { get; private set; }
|
||||
public string HostName { get; private set; }
|
||||
public int Port { get; private set; }
|
||||
}
|
||||
}
|
||||
namespace Ocelot.ServiceDiscovery.Configuration
|
||||
{
|
||||
public class ConsulRegistryConfiguration
|
||||
{
|
||||
public ConsulRegistryConfiguration(string hostName, int port, string keyOfServiceInConsul)
|
||||
{
|
||||
HostName = hostName;
|
||||
Port = port;
|
||||
KeyOfServiceInConsul = keyOfServiceInConsul;
|
||||
}
|
||||
|
||||
public string KeyOfServiceInConsul { get; private set; }
|
||||
public string HostName { get; private set; }
|
||||
public int Port { get; private set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
namespace Ocelot.ServiceDiscovery.Configuration
|
||||
{
|
||||
public class ServiceFabricConfiguration
|
||||
{
|
||||
@ -9,8 +9,10 @@
|
||||
ServiceName = serviceName;
|
||||
}
|
||||
|
||||
public string ServiceName { get; private set; }
|
||||
public string HostName { get; private set; }
|
||||
public int Port { get; private set; }
|
||||
public string ServiceName { get; }
|
||||
|
||||
public string HostName { get; }
|
||||
|
||||
public int Port { get; }
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
public class UnableToFindServiceDiscoveryProviderError : Error
|
||||
{
|
||||
public UnableToFindServiceDiscoveryProviderError(string message)
|
||||
: base(message, OcelotErrorCode.UnableToFindServiceDiscoveryProviderError)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery.Errors
|
||||
{
|
||||
public class UnableToFindServiceDiscoveryProviderError : Error
|
||||
{
|
||||
public UnableToFindServiceDiscoveryProviderError(string message)
|
||||
: base(message, OcelotErrorCode.UnableToFindServiceDiscoveryProviderError)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
@ -6,4 +7,4 @@ namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
public class ConfigurationServiceProvider : IServiceDiscoveryProvider
|
||||
{
|
||||
private readonly List<Service> _services;
|
||||
|
||||
public ConfigurationServiceProvider(List<Service> services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<List<Service>> Get()
|
||||
{
|
||||
return await Task.FromResult(_services);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery.Providers
|
||||
{
|
||||
public class ConfigurationServiceProvider : IServiceDiscoveryProvider
|
||||
{
|
||||
private readonly List<Service> _services;
|
||||
|
||||
public ConfigurationServiceProvider(List<Service> services)
|
||||
{
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public async Task<List<Service>> Get()
|
||||
{
|
||||
return await Task.FromResult(_services);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,83 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Consul;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||
{
|
||||
private readonly ConsulRegistryConfiguration _consulConfig;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly ConsulClient _consul;
|
||||
private const string VersionPrefix = "version-";
|
||||
|
||||
public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration consulRegistryConfiguration, IOcelotLoggerFactory factory)
|
||||
{;
|
||||
_logger = factory.CreateLogger<ConsulServiceDiscoveryProvider>();
|
||||
|
||||
var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
|
||||
|
||||
var consulPort = consulRegistryConfiguration?.Port ?? 8500;
|
||||
|
||||
_consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul);
|
||||
|
||||
_consul = new ConsulClient(config =>
|
||||
{
|
||||
config.Address = new Uri($"http://{_consulConfig.HostName}:{_consulConfig.Port}");
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<List<Service>> Get()
|
||||
{
|
||||
var queryResult = await _consul.Health.Service(_consulConfig.KeyOfServiceInConsul, string.Empty, true);
|
||||
|
||||
var services = new List<Service>();
|
||||
|
||||
foreach (var serviceEntry in queryResult.Response)
|
||||
{
|
||||
if (IsValid(serviceEntry))
|
||||
{
|
||||
services.Add(BuildService(serviceEntry));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError($"Unable to use service Address: {serviceEntry.Service.Address} and Port: {serviceEntry.Service.Port} as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
return services.ToList();
|
||||
}
|
||||
|
||||
private Service BuildService(ServiceEntry serviceEntry)
|
||||
{
|
||||
return new Service(
|
||||
serviceEntry.Service.Service,
|
||||
new ServiceHostAndPort(serviceEntry.Service.Address, serviceEntry.Service.Port),
|
||||
serviceEntry.Service.ID,
|
||||
GetVersionFromStrings(serviceEntry.Service.Tags),
|
||||
serviceEntry.Service.Tags ?? Enumerable.Empty<string>());
|
||||
}
|
||||
|
||||
private bool IsValid(ServiceEntry serviceEntry)
|
||||
{
|
||||
if (serviceEntry.Service.Address.Contains("http://") || serviceEntry.Service.Address.Contains("https://") || serviceEntry.Service.Port <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetVersionFromStrings(IEnumerable<string> strings)
|
||||
{
|
||||
return strings
|
||||
?.FirstOrDefault(x => x.StartsWith(VersionPrefix, StringComparison.Ordinal))
|
||||
.TrimStart(VersionPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Consul;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery.Providers
|
||||
{
|
||||
public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||
{
|
||||
private readonly ConsulRegistryConfiguration _consulConfig;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly ConsulClient _consul;
|
||||
private const string VersionPrefix = "version-";
|
||||
|
||||
public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration consulRegistryConfiguration, IOcelotLoggerFactory factory)
|
||||
{;
|
||||
_logger = factory.CreateLogger<ConsulServiceDiscoveryProvider>();
|
||||
|
||||
var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
|
||||
|
||||
var consulPort = consulRegistryConfiguration?.Port ?? 8500;
|
||||
|
||||
_consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul);
|
||||
|
||||
_consul = new ConsulClient(config =>
|
||||
{
|
||||
config.Address = new Uri($"http://{_consulConfig.HostName}:{_consulConfig.Port}");
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<List<Service>> Get()
|
||||
{
|
||||
var queryResult = await _consul.Health.Service(_consulConfig.KeyOfServiceInConsul, string.Empty, true);
|
||||
|
||||
var services = new List<Service>();
|
||||
|
||||
foreach (var serviceEntry in queryResult.Response)
|
||||
{
|
||||
if (IsValid(serviceEntry))
|
||||
{
|
||||
services.Add(BuildService(serviceEntry));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError($"Unable to use service Address: {serviceEntry.Service.Address} and Port: {serviceEntry.Service.Port} as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
return services.ToList();
|
||||
}
|
||||
|
||||
private Service BuildService(ServiceEntry serviceEntry)
|
||||
{
|
||||
return new Service(
|
||||
serviceEntry.Service.Service,
|
||||
new ServiceHostAndPort(serviceEntry.Service.Address, serviceEntry.Service.Port),
|
||||
serviceEntry.Service.ID,
|
||||
GetVersionFromStrings(serviceEntry.Service.Tags),
|
||||
serviceEntry.Service.Tags ?? Enumerable.Empty<string>());
|
||||
}
|
||||
|
||||
private bool IsValid(ServiceEntry serviceEntry)
|
||||
{
|
||||
if (serviceEntry.Service.Address.Contains("http://") || serviceEntry.Service.Address.Contains("https://") || serviceEntry.Service.Port <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetVersionFromStrings(IEnumerable<string> strings)
|
||||
{
|
||||
return strings
|
||||
?.FirstOrDefault(x => x.StartsWith(VersionPrefix, StringComparison.Ordinal))
|
||||
.TrimStart(VersionPrefix);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
public interface IServiceDiscoveryProvider
|
||||
{
|
||||
Task<List<Service>> Get();
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery.Providers
|
||||
{
|
||||
public interface IServiceDiscoveryProvider
|
||||
{
|
||||
Task<List<Service>> Get();
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
namespace Ocelot.ServiceDiscovery.Providers
|
||||
{
|
||||
public class ServiceFabricServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||
{
|
@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.ServiceDiscovery.Configuration;
|
||||
using Ocelot.ServiceDiscovery.Providers;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
|
@ -7,6 +7,6 @@
|
||||
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;
|
||||
}
|
||||
|
||||
public string Value { get; private set; }
|
||||
public string Value { get; }
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ namespace Ocelot.Values
|
||||
public class Service
|
||||
{
|
||||
public Service(string name,
|
||||
ServiceHostAndPort hostAndPort,
|
||||
string id,
|
||||
string version,
|
||||
ServiceHostAndPort hostAndPort,
|
||||
string id,
|
||||
string version,
|
||||
IEnumerable<string> tags)
|
||||
{
|
||||
Name = name;
|
||||
@ -17,14 +17,14 @@ namespace Ocelot.Values
|
||||
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;
|
||||
}
|
||||
|
||||
public string DownstreamHost { get; private set; }
|
||||
public int DownstreamPort { get; private set; }
|
||||
public string DownstreamHost { get; }
|
||||
|
||||
public int DownstreamPort { get; }
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,8 @@ namespace Ocelot.Values
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
public string Template {get;}
|
||||
public int Priority {get;}
|
||||
public string Template { 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>();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user