Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/Ocelot/Configuration/Builder/DownstreamRouteBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class DownstreamRouteBuilder
private Dictionary<string, UpstreamHeaderTemplate> _upstreamHeaders;
private MetadataOptions _metadataOptions;
private int? _timeout;
private bool _connectionClose;

public DownstreamRouteBuilder()
{
Expand Down Expand Up @@ -253,6 +254,12 @@ public DownstreamRouteBuilder WithTimeout(int? timeout)
return this;
}

public DownstreamRouteBuilder WithConnectionClose(bool connectionClose)
{
_connectionClose = connectionClose;
return this;
}

public DownstreamRoute Build()
{
return new DownstreamRoute(
Expand Down Expand Up @@ -288,6 +295,7 @@ public DownstreamRoute Build()
_downstreamHttpVersionPolicy,
_upstreamHeaders,
_metadataOptions,
_timeout);
_timeout,
_connectionClose);
}
}
14 changes: 14 additions & 0 deletions src/Ocelot/Configuration/Creator/ConnectionCloseCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Ocelot.Configuration.File;

namespace Ocelot.Configuration.Creator
{
public class ConnectionCloseCreator : IConnectionCloseCreator
{
public bool Create(bool fileRouteConnectionClose, FileGlobalConfiguration globalConfiguration)
{
var globalConnectionClose = globalConfiguration.ConnectionClose;

return fileRouteConnectionClose || globalConnectionClose;
}
}
}
9 changes: 9 additions & 0 deletions src/Ocelot/Configuration/Creator/IConnectionCloseCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Ocelot.Configuration.File;

namespace Ocelot.Configuration.Creator
{
public interface IConnectionCloseCreator
{
bool Create(bool fileRouteConnectionClose, FileGlobalConfiguration globalConfiguration);
}
}
5 changes: 4 additions & 1 deletion src/Ocelot/Configuration/DownstreamRoute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public DownstreamRoute(
HttpVersionPolicy downstreamHttpVersionPolicy,
Dictionary<string, UpstreamHeaderTemplate> upstreamHeaders,
MetadataOptions metadataOptions,
int? timeout)
int? timeout,
bool connectionClose)
{
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
AddHeadersToDownstream = addHeadersToDownstream;
Expand Down Expand Up @@ -74,6 +75,7 @@ public DownstreamRoute(
UpstreamHeaders = upstreamHeaders ?? new();
MetadataOptions = metadataOptions;
Timeout = timeout;
ConnectionClose = connectionClose;
}

public string Key { get; }
Expand All @@ -84,6 +86,7 @@ public DownstreamRoute(
public string ServiceName { get; }
public string ServiceNamespace { get; }
public HttpHandlerOptions HttpHandlerOptions { get; }
public bool ConnectionClose { get; }
public QoSOptions QosOptions { get; }
public string DownstreamScheme { get; }
public string RequestIdKey { get; }
Expand Down
2 changes: 2 additions & 0 deletions src/Ocelot/Configuration/File/FileGlobalConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public FileGlobalConfiguration()
AuthenticationOptions = new();
BaseUrl = default;
CacheOptions = default;
ConnectionClose = false;
DownstreamHeaderTransform = new Dictionary<string, string>();
DownstreamHttpVersion = default;
DownstreamHttpVersionPolicy = default;
Expand All @@ -27,6 +28,7 @@ public FileGlobalConfiguration()
public FileGlobalAuthenticationOptions AuthenticationOptions { get; set; }
public string BaseUrl { get; set; }
public FileGlobalCacheOptions CacheOptions { get; set; }
public bool ConnectionClose { get; set; }
public IDictionary<string, string> DownstreamHeaderTransform { get; set; }
public string DownstreamHttpVersion { get; set; }
public string DownstreamHttpVersionPolicy { get; set; }
Expand Down
7 changes: 5 additions & 2 deletions src/Ocelot/Configuration/File/FileRoute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// Represents the JSON structure of a standard static route (no service discovery).
/// </summary>
public class FileRoute : FileRouteBase, IRouteUpstream, IRouteGrouping, IRouteRateLimiting, ICloneable
{
{
public FileRoute()
{
AddClaimsToRequest = new Dictionary<string, string>();
Expand All @@ -20,6 +20,7 @@ public FileRoute()
UpstreamHeaderTemplates = new Dictionary<string, string>();
UpstreamHeaderTransform = new Dictionary<string, string>();
UpstreamHttpMethod = new();
ConnectionClose = false;
}

public FileRoute(FileRoute from)
Expand All @@ -31,6 +32,7 @@ public FileRoute(FileRoute from)
public Dictionary<string, string> AddHeadersToRequest { get; set; }
public Dictionary<string, string> AddQueriesToRequest { get; set; }
public Dictionary<string, string> ChangeDownstreamPathTemplate { get; set; }
public bool ConnectionClose { get; set; }
public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
public List<string> DelegatingHandlers { get; set; }
public IDictionary<string, string> DownstreamHeaderTransform { get; set; }
Expand Down Expand Up @@ -68,6 +70,7 @@ public static void DeepCopy(FileRoute from, FileRoute to)
to.AddQueriesToRequest = new(from.AddQueriesToRequest);
to.AuthenticationOptions = from.AuthenticationOptions is null ? null : new(from.AuthenticationOptions);
to.ChangeDownstreamPathTemplate = new(from.ChangeDownstreamPathTemplate);
to.ConnectionClose = from.ConnectionClose;
to.DangerousAcceptAnyServerCertificateValidator = from.DangerousAcceptAnyServerCertificateValidator;
to.DelegatingHandlers = new(from.DelegatingHandlers);
to.DownstreamHeaderTransform = new Dictionary<string, string>(from.DownstreamHeaderTransform);
Expand Down Expand Up @@ -114,5 +117,5 @@ public override string ToString()
return !string.IsNullOrWhiteSpace(ServiceName)
? string.Join(':', ServiceNamespace, ServiceName, path)
: path;
}
}
}
1 change: 1 addition & 0 deletions src/Ocelot/DependencyInjection/OcelotBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo
Services.TryAddSingleton<IVersionCreator, HttpVersionCreator>();
Services.TryAddSingleton<IVersionPolicyCreator, HttpVersionPolicyCreator>();
Services.TryAddSingleton<IWebSocketsFactory, WebSocketsFactory>();
Services.TryAddSingleton<IConnectionCloseCreator, ConnectionCloseCreator>();

// Add security
Services.TryAddSingleton<ISecurityOptionsCreator, SecurityOptionsCreator>();
Expand Down
128 changes: 128 additions & 0 deletions src/Ocelot/Requester/HttpClientBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using Ocelot.Configuration;
using Ocelot.Logging;

namespace Ocelot.Requester;

public interface IHttpClientBuilder { }
public interface IHttpClientCache
{
IHttpClient Get(DownstreamRoute cacheKey);
void Set(DownstreamRoute cacheKey, IHttpClient client, TimeSpan span);
}

public class HttpClientBuilder : IHttpClientBuilder
{
private readonly IDelegatingHandlerFactory _factory;
private readonly IHttpClientCache _cacheHandlers;
private readonly IOcelotLogger _logger;
private DownstreamRoute _cacheKey;
private HttpClient _httpClient;
private IHttpClient _client;
private readonly TimeSpan _defaultTimeout;

public HttpClientBuilder(
IDelegatingHandlerFactory factory,
IHttpClientCache cacheHandlers,
IOcelotLogger logger)
{
_factory = factory;
_cacheHandlers = cacheHandlers;
_logger = logger;

// This is hardcoded at the moment but can easily be added to configuration
// if required by a user request.
_defaultTimeout = TimeSpan.FromSeconds(90);
}

public IHttpClient Create(DownstreamRoute downstreamRoute)
{
_cacheKey = downstreamRoute;

var httpClient = _cacheHandlers.Get(_cacheKey);

if (httpClient != null)
{
_client = httpClient;
return httpClient;
}

var handler = CreateHandler(downstreamRoute);

if (downstreamRoute.DangerousAcceptAnyServerCertificateValidator)
{
handler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

_logger
.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamRoute, UpstreamPathTemplate: {downstreamRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {downstreamRoute.DownstreamPathTemplate}");
}

var timeout = downstreamRoute.QosOptions.Timeout == 0
? _defaultTimeout
: TimeSpan.FromMilliseconds(downstreamRoute.QosOptions.Timeout.Value);

_httpClient = new HttpClient(CreateHttpMessageHandler(handler, downstreamRoute))
{
Timeout = timeout,
};

_client = new HttpClientWrapper(_httpClient, downstreamRoute.ConnectionClose); // TODO

return _client;
}

private static HttpClientHandler CreateHandler(DownstreamRoute downstreamRoute)
{
// Dont' create the CookieContainer if UseCookies is not set or the HttpClient will complain
// under .Net Full Framework
var useCookies = downstreamRoute.HttpHandlerOptions.UseCookieContainer;

return useCookies ? UseCookiesHandler(downstreamRoute) : UseNonCookiesHandler(downstreamRoute);
}

private static HttpClientHandler UseNonCookiesHandler(DownstreamRoute downstreamRoute)
{
return new HttpClientHandler
{
AllowAutoRedirect = downstreamRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = downstreamRoute.HttpHandlerOptions.UseCookieContainer,
UseProxy = downstreamRoute.HttpHandlerOptions.UseProxy,
MaxConnectionsPerServer = downstreamRoute.HttpHandlerOptions.MaxConnectionsPerServer,

};
}

private static HttpClientHandler UseCookiesHandler(DownstreamRoute downstreamRoute)
{
return new HttpClientHandler
{
AllowAutoRedirect = downstreamRoute.HttpHandlerOptions.AllowAutoRedirect,
UseCookies = downstreamRoute.HttpHandlerOptions.UseCookieContainer,
UseProxy = downstreamRoute.HttpHandlerOptions.UseProxy,
MaxConnectionsPerServer = downstreamRoute.HttpHandlerOptions.MaxConnectionsPerServer,
CookieContainer = new CookieContainer(),
};
}

public void Save()
{
_cacheHandlers.Set(_cacheKey, _client, TimeSpan.FromHours(24));
}

private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamRoute request)
{
//todo handle error
var handlers = _factory.Get(request);

handlers
.Select(handler => handler)
.Reverse()
.ToList()
.ForEach(handler =>
{
handler.InnerHandler = httpMessageHandler;
httpMessageHandler = handler;
});
return httpMessageHandler;
}
}
30 changes: 30 additions & 0 deletions src/Ocelot/Requester/HttpClientWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Ocelot.Requester
{
public interface IHttpClient { }

/// <summary>
/// This class was made to make unit testing easier when HttpClient is used.
/// </summary>
public class HttpClientWrapper : IHttpClient
{
public HttpClient Client { get; }

public bool ConnectionClose { get; } // TODO

public HttpClientWrapper(HttpClient client, bool connectionClose = false) // TODO
{
Client = client;
ConnectionClose = connectionClose;
}

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default)
{
request.Headers.ConnectionClose = ConnectionClose; // TODO
return Client.SendAsync(request, cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ public DownstreamRouteExtensionsTests()
HttpVersionPolicy.RequestVersionExact,
new(),
new MetadataOptions(new FileMetadataOptions()),
0);
0,
false);
}

[Theory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ static bool EqualsForGui(DownstreamRoute x, DownstreamRoute y)
}

// Two object with absolutely identical internal state
var d1 = new DownstreamRoute(default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default);
var d2 = new DownstreamRoute(default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default);
var d1 = new DownstreamRoute(default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default);
var d2 = new DownstreamRoute(default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default, default);

// Act for Gui :) This will be a gift for Gui for Christmas! LOL
bool happyStart = d1.Equals(d2); // object.Equals(object)
Expand Down
Loading