merged develop

This commit is contained in:
TomPallister 2017-02-11 12:07:08 +00:00
commit e33fe4cf7d
34 changed files with 674 additions and 295 deletions

BIN
.DS_Store vendored

Binary file not shown.

6
.gitignore vendored
View File

@ -236,3 +236,9 @@ _Pvt_Extensions
# FAKE - F# Make
.fake/
tools/
# MacOS
.DS_Store
# Ocelot acceptance test config
test/Ocelot.AcceptanceTests/configuration.json

BIN
src/.DS_Store vendored

Binary file not shown.

BIN
src/Ocelot/.DS_Store vendored

Binary file not shown.

Binary file not shown.

View File

@ -28,6 +28,8 @@ namespace Ocelot.Configuration.Builder
private int _downstreamPort;
private string _loadBalancer;
private ServiceProviderConfiguraion _serviceProviderConfiguraion;
private bool _useQos;
private QoSOptions _qosOptions;
public ReRouteBuilder WithLoadBalancer(string loadBalancer)
{
@ -135,6 +137,19 @@ namespace Ocelot.Configuration.Builder
return this;
}
public ReRouteBuilder WithIsQos(bool input)
{
_useQos = input;
return this;
}
public ReRouteBuilder WithQosOptions(QoSOptions input)
{
_qosOptions = input;
return this;
}
public ReRouteBuilder WithLoadBalancerKey(string loadBalancerKey)
{
_loadBalancerKey = loadBalancerKey;
@ -175,7 +190,9 @@ namespace Ocelot.Configuration.Builder
_downstreamHost,
_downstreamPort,
_loadBalancerKey,
_serviceProviderConfiguraion);
_serviceProviderConfiguraion,
_useQos,
_qosOptions);
}
}
}

View File

@ -95,6 +95,7 @@ namespace Ocelot.Configuration.Creator
var loadBalancerKey = BuildLoadBalancerKey(fileReRoute);
var upstreamTemplatePattern = BuildUpstreamTemplate(fileReRoute);
var isQos = fileReRoute.QoSOptions.ExceptionsAllowedBeforeBreaking > 0 && fileReRoute.QoSOptions.TimeoutValue >0;
var serviceProviderConfiguration = BuildServiceProviderConfiguration(fileReRoute, globalConfiguration);
@ -102,6 +103,7 @@ namespace Ocelot.Configuration.Creator
var claimsToHeaders = BuildAddThingsToRequest(fileReRoute.AddHeadersToRequest);
var claimsToClaims = BuildAddThingsToRequest(fileReRoute.AddClaimsToRequest);
var claimsToQueries = BuildAddThingsToRequest(fileReRoute.AddQueriesToRequest);

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Configuration.File
{
public class FileQoSOptions
{
public int ExceptionsAllowedBeforeBreaking { get; set; }
public int DurationOfBreak { get; set; }
public int TimeoutValue { get; set; }
}
}

View File

@ -12,6 +12,7 @@ namespace Ocelot.Configuration.File
AddQueriesToRequest = new Dictionary<string, string>();
AuthenticationOptions = new FileAuthenticationOptions();
FileCacheOptions = new FileCacheOptions();
QoSOptions = new FileQoSOptions();
}
public string DownstreamPathTemplate { get; set; }
@ -29,6 +30,7 @@ namespace Ocelot.Configuration.File
public string DownstreamScheme {get;set;}
public string DownstreamHost {get;set;}
public int DownstreamPort { get; set; }
public FileQoSOptions QoSOptions { get; set; }
public string LoadBalancer {get;set;}
}
}

View File

@ -0,0 +1,29 @@
using Polly.Timeout;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Ocelot.Configuration
{
public class QoSOptions
{
public QoSOptions(int exceptionsAllowedBeforeBreaking, int durationofBreak, int timeoutValue, TimeoutStrategy timeoutStrategy = TimeoutStrategy.Pessimistic)
{
ExceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
DurationOfBreak = TimeSpan.FromMilliseconds(durationofBreak);
TimeoutValue = TimeSpan.FromMilliseconds(timeoutValue);
TimeoutStrategy = timeoutStrategy;
}
public int ExceptionsAllowedBeforeBreaking { get; private set; }
public TimeSpan DurationOfBreak { get; private set; }
public TimeSpan TimeoutValue { get; private set; }
public TimeoutStrategy TimeoutStrategy { get; private set; }
}
}

View File

@ -8,16 +8,27 @@ namespace Ocelot.Configuration
public class ReRoute
{
public ReRoute(PathTemplate downstreamPathTemplate,
PathTemplate upstreamTemplate, HttpMethod upstreamHttpMethod,
PathTemplate upstreamTemplate,
HttpMethod upstreamHttpMethod,
string upstreamTemplatePattern,
bool isAuthenticated, AuthenticationOptions authenticationOptions,
bool isAuthenticated,
AuthenticationOptions authenticationOptions,
List<ClaimToThing> configurationHeaderExtractorProperties,
List<ClaimToThing> claimsToClaims,
Dictionary<string, string> routeClaimsRequirement, bool isAuthorised,
Dictionary<string, string> routeClaimsRequirement,
bool isAuthorised,
List<ClaimToThing> claimsToQueries,
string requestIdKey, bool isCached, CacheOptions fileCacheOptions,
string downstreamScheme, string loadBalancer, string downstreamHost,
int downstreamPort, string loadBalancerKey, ServiceProviderConfiguraion serviceProviderConfiguraion)
string requestIdKey,
bool isCached,
CacheOptions fileCacheOptions,
string downstreamScheme,
string loadBalancer,
string downstreamHost,
int downstreamPort,
string loadBalancerKey,
ServiceProviderConfiguraion serviceProviderConfiguraion,
bool isQos,
QoSOptions qos)
{
LoadBalancerKey = loadBalancerKey;
ServiceProviderConfiguraion = serviceProviderConfiguraion;
@ -42,6 +53,8 @@ namespace Ocelot.Configuration
ClaimsToHeaders = configurationHeaderExtractorProperties
?? new List<ClaimToThing>();
DownstreamScheme = downstreamScheme;
IsQos = isQos;
QosOptions = qos;
}
public string LoadBalancerKey {get;private set;}
@ -60,6 +73,8 @@ namespace Ocelot.Configuration
public bool IsCached { get; private set; }
public CacheOptions FileCacheOptions { get; private set; }
public string DownstreamScheme {get;private set;}
public bool IsQos { get; private set; }
public QoSOptions QosOptions { get; private set; }
public string LoadBalancer {get;private set;}
public string DownstreamHost { get; private set; }
public int DownstreamPort { get; private set; }

View File

@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Responses;
using Ocelot.Configuration;
namespace Ocelot.Request.Builder
{
@ -15,7 +16,9 @@ namespace Ocelot.Request.Builder
IRequestCookieCollection cookies,
QueryString queryString,
string contentType,
RequestId.RequestId requestId)
RequestId.RequestId requestId,
bool isQos,
QoSOptions qos)
{
var request = await new RequestBuilder()
.WithHttpMethod(httpMethod)
@ -26,6 +29,8 @@ namespace Ocelot.Request.Builder
.WithHeaders(headers)
.WithRequestId(requestId)
.WithCookies(cookies)
.WithIsQos(isQos)
.WithQos(qos)
.Build();
return new OkResponse<Request>(request);

View File

@ -2,6 +2,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Responses;
using Ocelot.Configuration;
namespace Ocelot.Request.Builder
{
@ -14,6 +15,8 @@ namespace Ocelot.Request.Builder
IRequestCookieCollection cookies,
QueryString queryString,
string contentType,
RequestId.RequestId requestId);
RequestId.RequestId requestId,
bool isQos,
QoSOptions qos);
}
}

View File

@ -8,6 +8,7 @@ using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Ocelot.Configuration;
namespace Ocelot.Request.Builder
{
@ -22,6 +23,8 @@ namespace Ocelot.Request.Builder
private RequestId.RequestId _requestId;
private IRequestCookieCollection _cookies;
private readonly string[] _unsupportedHeaders = {"host"};
private bool _isQos;
private QoSOptions _qos;
public RequestBuilder WithHttpMethod(string httpMethod)
{
@ -71,6 +74,18 @@ namespace Ocelot.Request.Builder
return this;
}
public RequestBuilder WithIsQos(bool isqos)
{
_isQos = isqos;
return this;
}
public RequestBuilder WithQos(QoSOptions qos)
{
_qos = qos;
return this;
}
public async Task<Request> Build()
{
var uri = CreateUri();
@ -90,7 +105,7 @@ namespace Ocelot.Request.Builder
var cookieContainer = CreateCookieContainer(uri);
return new Request(httpRequestMessage, cookieContainer);
return new Request(httpRequestMessage, cookieContainer,_isQos,_qos);
}
private Uri CreateUri()

View File

@ -32,7 +32,8 @@ namespace Ocelot.Request.Middleware
var buildResult = await _requestCreator
.Build(context.Request.Method, DownstreamUrl, context.Request.Body,
context.Request.Headers, context.Request.Cookies, context.Request.QueryString,
context.Request.ContentType, new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier));
context.Request.ContentType, new RequestId.RequestId(DownstreamRoute?.ReRoute?.RequestIdKey, context.TraceIdentifier),
DownstreamRoute.ReRoute.IsQos,DownstreamRoute.ReRoute.QosOptions);
if (buildResult.IsError)
{

View File

@ -1,17 +1,23 @@
using System.Net;
using Ocelot.Configuration;
using Ocelot.Values;
using System.Net;
using System.Net.Http;
namespace Ocelot.Request
{
public class Request
{
public Request(HttpRequestMessage httpRequestMessage, CookieContainer cookieContainer)
public Request(HttpRequestMessage httpRequestMessage, CookieContainer cookieContainer,bool isQos, QoSOptions qos)
{
HttpRequestMessage = httpRequestMessage;
CookieContainer = cookieContainer;
IsQos = isQos;
Qos = qos;
}
public HttpRequestMessage HttpRequestMessage { get; private set; }
public CookieContainer CookieContainer { get; private set; }
public bool IsQos { get; private set; }
public QoSOptions Qos { get; private set; }
}
}

View File

@ -0,0 +1,74 @@
using Ocelot.Logging;
using Polly;
using Polly.CircuitBreaker;
using Polly.Timeout;
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Ocelot.Requester
{
public class CircuitBreakingDelegatingHandler : DelegatingHandler
{
private readonly IOcelotLogger _logger;
private readonly int _exceptionsAllowedBeforeBreaking;
private readonly TimeSpan _durationOfBreak;
private readonly Policy _circuitBreakerPolicy;
private readonly TimeoutPolicy _timeoutPolicy;
public CircuitBreakingDelegatingHandler(int exceptionsAllowedBeforeBreaking, TimeSpan durationOfBreak,TimeSpan timeoutValue
,TimeoutStrategy timeoutStrategy, IOcelotLogger logger, HttpMessageHandler innerHandler)
: base(innerHandler)
{
this._exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
this._durationOfBreak = durationOfBreak;
_circuitBreakerPolicy = Policy
.Handle<HttpRequestException>()
.Or<TimeoutRejectedException>()
.Or<TimeoutException>()
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: exceptionsAllowedBeforeBreaking,
durationOfBreak: durationOfBreak,
onBreak: (ex, breakDelay) =>
{
_logger.LogError(".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex);
},
onReset: () => _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again."),
onHalfOpen: () => _logger.LogDebug(".Breaker logging: Half-open; next call is a trial.")
);
_timeoutPolicy = Policy.TimeoutAsync(timeoutValue, timeoutStrategy);
_logger = logger;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Task<HttpResponseMessage> responseTask = null;
try
{
responseTask = Policy.WrapAsync(_circuitBreakerPolicy, _timeoutPolicy).ExecuteAsync<HttpResponseMessage>(() =>
{
return base.SendAsync(request,cancellationToken);
});
return responseTask;
}
catch (BrokenCircuitException ex)
{
_logger.LogError($"Reached to allowed number of exceptions. Circuit is open. AllowedExceptionCount: {_exceptionsAllowedBeforeBreaking}, DurationOfBreak: {_durationOfBreak}",ex);
throw;
}
catch (HttpRequestException)
{
return responseTask;
}
}
private static bool IsTransientFailure(HttpResponseMessage result)
{
return result.StatusCode >= HttpStatusCode.InternalServerError;
}
}
}

View File

@ -0,0 +1,42 @@
using Ocelot.Configuration;
using Ocelot.Logging;
using Ocelot.Values;
using Polly.Timeout;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace Ocelot.Requester
{
internal class HttpClientBuilder
{
private readonly Dictionary<int, Func<DelegatingHandler>> handlers = new Dictionary<int, Func<DelegatingHandler>>();
public HttpClientBuilder WithCircuitBreaker(QoSOptions qos, IOcelotLogger logger, HttpMessageHandler innerHandler)
{
handlers.Add(5000, () => new CircuitBreakingDelegatingHandler(qos.ExceptionsAllowedBeforeBreaking, qos.DurationOfBreak, qos.TimeoutValue, qos.TimeoutStrategy, logger, innerHandler));
return this;
}
internal HttpClient Build(HttpMessageHandler innerHandler)
{
return handlers.Any() ? new HttpClient(CreateHttpMessageHandler()) : new HttpClient(innerHandler);
}
private HttpMessageHandler CreateHttpMessageHandler()
{
HttpMessageHandler httpMessageHandler = new HttpClientHandler();
handlers.OrderByDescending(handler => handler.Key).Select(handler => handler.Value).Reverse().ToList().ForEach(handler =>
{
var delegatingHandler = handler();
delegatingHandler.InnerHandler = httpMessageHandler;
httpMessageHandler = delegatingHandler;
});
return httpMessageHandler;
}
}
}

View File

@ -4,15 +4,30 @@ using System.Net.Http;
using System.Threading.Tasks;
using Ocelot.Errors;
using Ocelot.Responses;
using Ocelot.Logging;
namespace Ocelot.Requester
{
public class HttpClientHttpRequester : IHttpRequester
{
private readonly IOcelotLogger _logger;
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
}
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
{
HttpClientBuilder builder = new HttpClientBuilder();
using (var handler = new HttpClientHandler { CookieContainer = request.CookieContainer })
using (var httpClient = new HttpClient(handler))
{
if (request.IsQos)
{
builder.WithCircuitBreaker(request.Qos, _logger, handler);
}
using (var httpClient = builder.Build(handler))
{
try
{
@ -30,4 +45,5 @@ namespace Ocelot.Requester
}
}
}
}
}

View File

@ -7,5 +7,7 @@ namespace Ocelot.Requester
public interface IHttpRequester
{
Task<Response<HttpResponseMessage>> GetResponse(Request.Request request);
}
}

View File

@ -1,6 +1,5 @@
{
"version": "0.0.0-dev",
"dependencies": {
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
@ -28,11 +27,12 @@
"CacheManager.Core": "0.9.2",
"CacheManager.Microsoft.Extensions.Configuration": "0.9.2",
"CacheManager.Microsoft.Extensions.Logging": "0.9.2",
"Consul": "0.7.2.1"
"Consul": "0.7.2.1",
"Polly": "5.0.3"
},
"runtimes": {
"win10-x64": {},
"osx.10.11-x64":{},
"osx.10.11-x64": {},
"win7-x64": {}
},
"frameworks": {

BIN
test/.DS_Store vendored

Binary file not shown.

View File

@ -165,6 +165,7 @@ namespace Ocelot.AcceptanceTests
DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Post",
AuthenticationOptions = new FileAuthenticationOptions
{
AdditionalScopes = new List<string>(),

View File

@ -35,7 +35,7 @@ namespace Ocelot.AcceptanceTests
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = "Get"
UpstreamHttpMethod = "Get",
}
}
};
@ -63,7 +63,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = "Get",
ReRouteIsCaseSensitive = false
ReRouteIsCaseSensitive = false,
}
}
};
@ -91,7 +91,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = "Get",
ReRouteIsCaseSensitive = true
ReRouteIsCaseSensitive = true,
}
}
};
@ -119,7 +119,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/PRODUCTS/{productId}",
UpstreamHttpMethod = "Get",
ReRouteIsCaseSensitive = true
ReRouteIsCaseSensitive = true,
}
}
};
@ -147,7 +147,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = "Get",
ReRouteIsCaseSensitive = true
ReRouteIsCaseSensitive = true,
}
}
};
@ -175,7 +175,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/PRODUCTS/{productId}",
UpstreamHttpMethod = "Get",
ReRouteIsCaseSensitive = true
ReRouteIsCaseSensitive = true,
}
}
};

View File

@ -88,6 +88,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Get",
}
}
};
@ -125,6 +126,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Get",
}
}
};

View File

@ -39,7 +39,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Get",
RequestIdKey = _steps.RequestIdKey
RequestIdKey = _steps.RequestIdKey,
}
}
};
@ -67,7 +67,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Get",
RequestIdKey = _steps.RequestIdKey
}
}
};

View File

@ -46,6 +46,7 @@ namespace Ocelot.AcceptanceTests
DownstreamPort = 51879,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Get",
}
}
};
@ -74,6 +75,7 @@ namespace Ocelot.AcceptanceTests
DownstreamPort = 51879,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Get",
}
}
};
@ -102,6 +104,7 @@ namespace Ocelot.AcceptanceTests
DownstreamPort = 51879,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Get",
}
}
};
@ -130,6 +133,7 @@ namespace Ocelot.AcceptanceTests
DownstreamPort = 51879,
UpstreamPathTemplate = "/products/",
UpstreamHttpMethod = "Get",
}
}
};
@ -186,6 +190,12 @@ namespace Ocelot.AcceptanceTests
DownstreamPort = 51879,
UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = "Get",
QoSOptions = new FileQoSOptions()
{
ExceptionsAllowedBeforeBreaking = 3,
DurationOfBreak = 5,
TimeoutValue = 5000
}
}
}
};
@ -212,7 +222,7 @@ namespace Ocelot.AcceptanceTests
DownstreamHost = "localhost",
DownstreamPort = 51879,
UpstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = "Get"
UpstreamHttpMethod = "Get",
}
}
};
@ -240,7 +250,7 @@ namespace Ocelot.AcceptanceTests
DownstreamPort = 51879,
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = "Post"
UpstreamHttpMethod = "Post",
}
}
};

View File

@ -1 +0,0 @@
{"ReRoutes":[{"DownstreamPathTemplate":"41879/","UpstreamPathTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":41879,"LoadBalancer":null}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0}}}

View File

@ -7,6 +7,11 @@
"DownstreamPort": 52876,
"UpstreamTemplate": "/identityserverexample",
"UpstreamHttpMethod": "Get",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"AuthenticationOptions": {
"Provider": "IdentityServer",
"ProviderRootUrl": "http://localhost:52888",
@ -47,7 +52,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/posts",
"UpstreamHttpMethod": "Get",
"FileCacheOptions": { "TtlSeconds": 15 }
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/posts/{postId}",
@ -55,7 +64,12 @@
"DownstreamHost": "jsonplaceholder.typicode.com",
"DownstreamPort": 80,
"UpstreamTemplate": "/posts/{postId}",
"UpstreamHttpMethod": "Get"
"UpstreamHttpMethod": "Get",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/posts/{postId}/comments",
@ -63,7 +77,12 @@
"DownstreamHost": "jsonplaceholder.typicode.com",
"DownstreamPort": 80,
"UpstreamTemplate": "/posts/{postId}/comments",
"UpstreamHttpMethod": "Get"
"UpstreamHttpMethod": "Get",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/comments",
@ -71,7 +90,12 @@
"DownstreamHost": "jsonplaceholder.typicode.com",
"DownstreamPort": 80,
"UpstreamTemplate": "/comments",
"UpstreamHttpMethod": "Get"
"UpstreamHttpMethod": "Get",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/posts",
@ -79,7 +103,12 @@
"DownstreamHost": "jsonplaceholder.typicode.com",
"DownstreamPort": 80,
"UpstreamTemplate": "/posts",
"UpstreamHttpMethod": "Post"
"UpstreamHttpMethod": "Post",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/posts/{postId}",
@ -87,7 +116,12 @@
"DownstreamHost": "jsonplaceholder.typicode.com",
"DownstreamPort": 80,
"UpstreamTemplate": "/posts/{postId}",
"UpstreamHttpMethod": "Put"
"UpstreamHttpMethod": "Put",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/posts/{postId}",
@ -95,7 +129,12 @@
"DownstreamHost": "jsonplaceholder.typicode.com",
"DownstreamPort": 80,
"UpstreamTemplate": "/posts/{postId}",
"UpstreamHttpMethod": "Patch"
"UpstreamHttpMethod": "Patch",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/posts/{postId}",
@ -103,7 +142,12 @@
"DownstreamHost": "jsonplaceholder.typicode.com",
"DownstreamPort": 80,
"UpstreamTemplate": "/posts/{postId}",
"UpstreamHttpMethod": "Delete"
"UpstreamHttpMethod": "Delete",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/api/products",
@ -112,6 +156,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/products",
"UpstreamHttpMethod": "Get",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
@ -130,7 +179,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/products",
"UpstreamHttpMethod": "Post",
"FileCacheOptions": { "TtlSeconds": 15 }
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/api/products/{productId}",
@ -139,6 +192,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/products/{productId}",
"UpstreamHttpMethod": "Put",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
@ -148,6 +206,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/products/{productId}",
"UpstreamHttpMethod": "Delete",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
@ -157,6 +220,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/customers",
"UpstreamHttpMethod": "Get",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
@ -166,6 +234,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": "Get",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
@ -175,6 +248,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/customers",
"UpstreamHttpMethod": "Post",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
@ -184,6 +262,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": "Put",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
@ -193,6 +276,11 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/customers/{customerId}",
"UpstreamHttpMethod": "Delete",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
},
{
@ -202,9 +290,15 @@
"DownstreamPort": 80,
"UpstreamTemplate": "/posts/",
"UpstreamHttpMethod": "Get",
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
},
"FileCacheOptions": { "TtlSeconds": 15 }
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId"
}

View File

@ -29,7 +29,6 @@ namespace Ocelot.UnitTests.LoadBalancer
private readonly HttpClient _client;
private HttpResponseMessage _result;
private HostAndPort _hostAndPort;
private OkResponse<Ocelot.Request.Request> _request;
private OkResponse<string> _downstreamUrl;
private OkResponse<DownstreamRoute> _downstreamRoute;
private ErrorResponse<ILoadBalancer> _getLoadBalancerHouseError;

View File

@ -19,6 +19,7 @@ using Ocelot.Request.Middleware;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
using Ocelot.Configuration;
namespace Ocelot.UnitTests.Request
{
@ -74,7 +75,7 @@ namespace Ocelot.UnitTests.Request
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer())))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), new CookieContainer(), true, new QoSOptions(3, 8 ,5000, Polly.Timeout.TimeoutStrategy.Pessimistic))))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy();
@ -93,7 +94,7 @@ namespace Ocelot.UnitTests.Request
_request = new OkResponse<Ocelot.Request.Request>(request);
_requestBuilder
.Setup(x => x.Build(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Stream>(), It.IsAny<IHeaderDictionary>(),
It.IsAny<IRequestCookieCollection>(), It.IsAny<QueryString>(), It.IsAny<string>(), It.IsAny<Ocelot.RequestId.RequestId>()))
It.IsAny<IRequestCookieCollection>(), It.IsAny<QueryString>(), It.IsAny<string>(), It.IsAny<Ocelot.RequestId.RequestId>(),It.IsAny<bool>(), It.IsAny<QoSOptions>()))
.ReturnsAsync(_request);
}

View File

@ -10,6 +10,7 @@ using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Ocelot.Configuration;
namespace Ocelot.UnitTests.Request
{
@ -25,6 +26,8 @@ namespace Ocelot.UnitTests.Request
private readonly IRequestCreator _requestCreator;
private Response<Ocelot.Request.Request> _result;
private Ocelot.RequestId.RequestId _requestId;
private bool _isQos;
private QoSOptions _qos;
public RequestBuilderTests()
{
@ -37,6 +40,7 @@ namespace Ocelot.UnitTests.Request
{
this.Given(x => x.GivenIHaveHttpMethod("GET"))
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
.And(x=> x.GivenTheQos(true,new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheCorrectDownstreamUrlIsUsed("http://www.bbc.co.uk/"))
.BDDfy();
@ -47,6 +51,8 @@ namespace Ocelot.UnitTests.Request
{
this.Given(x => x.GivenIHaveHttpMethod("POST"))
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
.And(x => x.GivenTheQos(true,new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheCorrectHttpMethodIsUsed(HttpMethod.Post))
.BDDfy();
@ -59,6 +65,8 @@ namespace Ocelot.UnitTests.Request
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
.And(x => x.GivenTheContentTypeIs("application/json"))
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheCorrectContentIsUsed(new StringContent("Hi from Tom")))
.BDDfy();
@ -71,6 +79,8 @@ namespace Ocelot.UnitTests.Request
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
.And(x => x.GivenTheContentTypeIs("application/json"))
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary
{
@ -88,6 +98,8 @@ namespace Ocelot.UnitTests.Request
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
.And(x => x.GivenIHaveTheHttpContent(new StringContent("Hi from Tom")))
.And(x => x.GivenTheContentTypeIs("application/json; charset=utf-8"))
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheCorrectContentHeadersAreUsed(new HeaderDictionary
{
@ -107,6 +119,8 @@ namespace Ocelot.UnitTests.Request
{
{"ChopSticks", "Bubbles" }
}))
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
{
@ -124,6 +138,7 @@ namespace Ocelot.UnitTests.Request
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
.And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary()))
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", requestId)))
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
{
@ -142,6 +157,7 @@ namespace Ocelot.UnitTests.Request
{"RequestId", "534534gv54gv45g" }
}))
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId("RequestId", Guid.NewGuid().ToString())))
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheCorrectHeadersAreUsed(new HeaderDictionary
{
@ -161,6 +177,7 @@ namespace Ocelot.UnitTests.Request
.And(x => x.GivenIHaveDownstreamUrl("http://www.bbc.co.uk"))
.And(x => x.GivenTheHttpHeadersAre(new HeaderDictionary()))
.And(x => x.GivenTheRequestIdIs(new Ocelot.RequestId.RequestId(requestIdKey, requestIdValue)))
.And(x => x.GivenTheQos(true, new QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic)))
.When(x => x.WhenICreateARequest())
.And(x => x.ThenTheRequestIdIsNotInTheHeaders())
.BDDfy();
@ -171,6 +188,12 @@ namespace Ocelot.UnitTests.Request
_requestId = requestId;
}
private void GivenTheQos(bool isQos, QoSOptions qos)
{
_isQos = isQos;
_qos = qos;
}
[Fact]
public void should_use_cookies()
{
@ -281,7 +304,7 @@ namespace Ocelot.UnitTests.Request
private void WhenICreateARequest()
{
_result = _requestCreator.Build(_httpMethod, _downstreamUrl, _content?.ReadAsStreamAsync().Result, _headers,
_cookies, _query, _contentType, _requestId).Result;
_cookies, _query, _contentType, _requestId,_isQos,_qos).Result;
}

View File

@ -61,7 +61,7 @@ namespace Ocelot.UnitTests.Requester
[Fact]
public void should_call_scoped_data_repository_correctly()
{
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer())))
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),new CookieContainer(),true, new Ocelot.Configuration.QoSOptions(3, 8, 5000, Polly.Timeout.TimeoutStrategy.Pessimistic))))
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
.And(x => x.GivenTheScopedRepoReturns())
.When(x => x.WhenICallTheMiddleware())

View File

@ -10,7 +10,6 @@ namespace Ocelot.UnitTests.ServiceDiscovery
public class ConfigurationServiceProviderTests
{
private ConfigurationServiceProvider _serviceProvider;
private HostAndPort _hostAndPort;
private List<Service> _result;
private List<Service> _expected;