diff --git a/build.cake b/build.cake index 9960c588..e988060e 100644 --- a/build.cake +++ b/build.cake @@ -1,6 +1,7 @@ #tool "nuget:?package=GitVersion.CommandLine" #tool "nuget:?package=GitReleaseNotes" -#addin "nuget:?package=Cake.Json" +#addin nuget:?package=Cake.Json +#addin nuget:?package=Newtonsoft.Json&version=9.0.1 #tool "nuget:?package=OpenCover" #tool "nuget:?package=ReportGenerator" #tool coveralls.net @@ -260,11 +261,19 @@ Task("ReleasePackagesToUnstableFeed") Task("EnsureStableReleaseRequirements") .Does(() => { + Information("Check if stable release..."); + if (!AppVeyor.IsRunningOnAppVeyor) { throw new Exception("Stable release should happen via appveyor"); } - + + Information("Running on AppVeyor..."); + + Information("IsTag = " + AppVeyor.Environment.Repository.Tag.IsTag); + + Information("Name = " + AppVeyor.Environment.Repository.Tag.Name); + var isTag = AppVeyor.Environment.Repository.Tag.IsTag && !string.IsNullOrWhiteSpace(AppVeyor.Environment.Repository.Tag.Name); @@ -273,6 +282,8 @@ Task("EnsureStableReleaseRequirements") { throw new Exception("Stable release should happen from a published GitHub release"); } + + Information("Release is stable..."); }); Task("UpdateVersionInfo") @@ -287,19 +298,48 @@ Task("DownloadGitHubReleaseArtifacts") .IsDependentOn("UpdateVersionInfo") .Does(() => { - EnsureDirectoryExists(packagesDir); + try + { + Information("DownloadGitHubReleaseArtifacts"); - var releaseUrl = tagsUrl + releaseTag; - var assets_url = ParseJson(GetResource(releaseUrl)) - .GetValue("assets_url") - .Value(); + EnsureDirectoryExists(packagesDir); - foreach(var asset in DeserializeJson(GetResource(assets_url))) - { - var file = packagesDir + File(asset.Value("name")); - Information("Downloading " + file); - DownloadFile(asset.Value("browser_download_url"), file); - } + Information("Directory exists..."); + + var releaseUrl = tagsUrl + releaseTag; + + Information("Release url " + releaseUrl); + + //var releaseJson = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)); + + var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)) + .GetValue("assets_url") + .Value(); + + Information("Assets url " + assets_url); + + var assets = GetResource(assets_url); + + Information("Assets " + assets_url); + + foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets)) + { + Information("In the loop.."); + + var file = packagesDir + File(asset.Value("name")); + + Information("Downloading " + file); + + DownloadFile(asset.Value("browser_download_url"), file); + } + + Information("Out of the loop..."); + } + catch(Exception exception) + { + Information("There was an exception " + exception); + throw; + } }); Task("ReleasePackagesToStableFeed") @@ -394,19 +434,31 @@ private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFi /// gets the resource from the specified url private string GetResource(string url) { - Information("Getting resource from " + url); + try + { + Information("Getting resource from " + url); - var assetsRequest = System.Net.WebRequest.CreateHttp(url); - assetsRequest.Method = "GET"; - assetsRequest.Accept = "application/vnd.github.v3+json"; - assetsRequest.UserAgent = "BuildScript"; + var assetsRequest = System.Net.WebRequest.CreateHttp(url); + assetsRequest.Method = "GET"; + assetsRequest.Accept = "application/vnd.github.v3+json"; + assetsRequest.UserAgent = "BuildScript"; - using (var assetsResponse = assetsRequest.GetResponse()) - { - var assetsStream = assetsResponse.GetResponseStream(); - var assetsReader = new StreamReader(assetsStream); - return assetsReader.ReadToEnd(); - } + using (var assetsResponse = assetsRequest.GetResponse()) + { + var assetsStream = assetsResponse.GetResponseStream(); + var assetsReader = new StreamReader(assetsStream); + var response = assetsReader.ReadToEnd(); + + Information("Response is " + response); + + return response; + } + } + catch(Exception exception) + { + Information("There was an exception " + exception); + throw; + } } private bool ShouldPublishToUnstableFeed(string filter, string branchName) diff --git a/build.ps1 b/build.ps1 index 44de5793..265bd123 100644 --- a/build.ps1 +++ b/build.ps1 @@ -21,34 +21,35 @@ The build script target to run. The build configuration to use. .PARAMETER Verbosity Specifies the amount of information to be displayed. +.PARAMETER ShowDescription +Shows description about tasks. +.PARAMETER DryRun +Performs a dry run. .PARAMETER Experimental -Tells Cake to use the latest Roslyn release. -.PARAMETER WhatIf -Performs a dry run of the build script. -No tasks will be executed. +Uses the nightly builds of the Roslyn script engine. .PARAMETER Mono -Tells Cake to use the Mono scripting engine. +Uses the Mono Compiler rather than the Roslyn script engine. .PARAMETER SkipToolPackageRestore Skips restoring of packages. .PARAMETER ScriptArgs Remaining arguments are added here. .LINK -http://cakebuild.net +https://cakebuild.net #> [CmdletBinding()] Param( [string]$Script = "build.cake", - [string]$Target = "Default", - [ValidateSet("Release", "Debug")] - [string]$Configuration = "Release", + [string]$Target, + [string]$Configuration, [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] - [string]$Verbosity = "Verbose", + [string]$Verbosity, + [switch]$ShowDescription, + [Alias("WhatIf", "Noop")] + [switch]$DryRun, [switch]$Experimental, - [Alias("DryRun","Noop")] - [switch]$WhatIf, [switch]$Mono, [switch]$SkipToolPackageRestore, [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] @@ -80,6 +81,15 @@ function MD5HashFile([string] $filePath) } } +function GetProxyEnabledWebClient +{ + $wc = New-Object System.Net.WebClient + $proxy = [System.Net.WebRequest]::GetSystemWebProxy() + $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $wc.Proxy = $proxy + return $wc +} + Write-Host "Preparing to run build script..." if(!$PSScriptRoot){ @@ -87,31 +97,15 @@ if(!$PSScriptRoot){ } $TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" +$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" - -# Should we use mono? -$UseMono = ""; -if($Mono.IsPresent) { - Write-Verbose -Message "Using the Mono based scripting engine." - $UseMono = "-mono" -} - -# Should we use the new Roslyn? -$UseExperimental = ""; -if($Experimental.IsPresent -and !($Mono.IsPresent)) { - Write-Verbose -Message "Using experimental version of Roslyn." - $UseExperimental = "-experimental" -} - -# Is this a dry run? -$UseDryRun = ""; -if($WhatIf.IsPresent) { - $UseDryRun = "-dryrun" -} +$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" +$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" # Make sure tools folder exists if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { @@ -121,8 +115,10 @@ if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { # Make sure that packages.config exist. if (!(Test-Path $PACKAGES_CONFIG)) { - Write-Verbose -Message "Downloading packages.config..." - try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { + Write-Verbose -Message "Downloading packages.config..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { Throw "Could not download packages.config." } } @@ -130,7 +126,7 @@ if (!(Test-Path $PACKAGES_CONFIG)) { # Try find NuGet.exe in path if not exists if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Trying to find nuget.exe in PATH..." - $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) } + $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." @@ -142,7 +138,8 @@ if (!(Test-Path $NUGET_EXE)) { if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Downloading NuGet.exe..." try { - (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE) + $wc = GetProxyEnabledWebClient + $wc.DownloadFile($NUGET_URL, $NUGET_EXE) } catch { Throw "Could not download NuGet.exe." } @@ -175,6 +172,41 @@ if(-Not $SkipToolPackageRestore.IsPresent) { $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" } Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore addins from NuGet +if (Test-Path $ADDINS_PACKAGES_CONFIG) { + Push-Location + Set-Location $ADDINS_DIR + + Write-Verbose -Message "Restoring addins from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occured while restoring NuGet addins." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore modules from NuGet +if (Test-Path $MODULES_PACKAGES_CONFIG) { + Push-Location + Set-Location $MODULES_DIR + + Write-Verbose -Message "Restoring modules from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occured while restoring NuGet modules." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + Pop-Location } @@ -183,7 +215,20 @@ if (!(Test-Path $CAKE_EXE)) { Throw "Could not find Cake.exe at $CAKE_EXE" } + + +# Build Cake arguments +$cakeArguments = @("$Script"); +if ($Target) { $cakeArguments += "-target=$Target" } +if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } +if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } +if ($ShowDescription) { $cakeArguments += "-showdescription" } +if ($DryRun) { $cakeArguments += "-dryrun" } +if ($Experimental) { $cakeArguments += "-experimental" } +if ($Mono) { $cakeArguments += "-mono" } +$cakeArguments += $ScriptArgs + # Start Cake Write-Host "Running build script..." -Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" -exit $LASTEXITCODE \ No newline at end of file +&$CAKE_EXE $cakeArguments +exit $LASTEXITCODE diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs index 8df1757c..1d820600 100644 --- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs +++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs @@ -36,7 +36,7 @@ namespace Ocelot.Authentication.Middleware { _logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated"); - var result = await context.AuthenticateAsync(DownstreamRoute.ReRoute.AuthenticationProviderKey); + var result = await context.AuthenticateAsync(DownstreamRoute.ReRoute.AuthenticationOptions.AuthenticationProviderKey); context.User = result.Principal; diff --git a/src/Ocelot/Configuration/AuthenticationOptions.cs b/src/Ocelot/Configuration/AuthenticationOptions.cs index 477d7283..21e53ad7 100644 --- a/src/Ocelot/Configuration/AuthenticationOptions.cs +++ b/src/Ocelot/Configuration/AuthenticationOptions.cs @@ -4,11 +4,13 @@ namespace Ocelot.Configuration { public class AuthenticationOptions { - public AuthenticationOptions(List allowedScopes) + public AuthenticationOptions(List allowedScopes, string authenticationProviderKey) { AllowedScopes = allowedScopes; + AuthenticationProviderKey = authenticationProviderKey; } public List AllowedScopes { get; private set; } + public string AuthenticationProviderKey { get; private set; } } } diff --git a/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs index 3a5f14b0..8146236e 100644 --- a/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs @@ -5,6 +5,7 @@ namespace Ocelot.Configuration.Builder public class AuthenticationOptionsBuilder { private List _allowedScopes = new List(); + private string _authenticationProviderKey; public AuthenticationOptionsBuilder WithAllowedScopes(List allowedScopes) { @@ -12,9 +13,15 @@ namespace Ocelot.Configuration.Builder return this; } + public AuthenticationOptionsBuilder WithAuthenticationProviderKey(string authenticationProviderKey) + { + _authenticationProviderKey = authenticationProviderKey; + return this; + } + public AuthenticationOptions Build() { - return new AuthenticationOptions(_allowedScopes); + return new AuthenticationOptions(_allowedScopes, _authenticationProviderKey); } } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs index 5281985a..272242c8 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs @@ -29,6 +29,7 @@ namespace Ocelot.Configuration.Builder private ServiceProviderConfiguration _serviceProviderConfiguraion; private bool _useQos; private QoSOptions _qosOptions; + private HttpHandlerOptions _httpHandlerOptions; public bool _enableRateLimiting; public RateLimitOptions _rateLimitOptions; private string _authenticationProviderKey; @@ -183,6 +184,11 @@ namespace Ocelot.Configuration.Builder return this; } + public ReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input) + { + _httpHandlerOptions = input; + return this; + } public ReRoute Build() { @@ -211,7 +217,7 @@ namespace Ocelot.Configuration.Builder _qosOptions, _enableRateLimiting, _rateLimitOptions, - _authenticationProviderKey); + _httpHandlerOptions); } } } diff --git a/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs b/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs index 5f4d8ba8..1e3f9aa0 100644 --- a/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs @@ -6,7 +6,7 @@ namespace Ocelot.Configuration.Creator { public AuthenticationOptions Create(FileReRoute reRoute) { - return new AuthenticationOptions(reRoute.AuthenticationOptions.AllowedScopes); + return new AuthenticationOptions(reRoute.AuthenticationOptions.AllowedScopes, reRoute.AuthenticationOptions.AuthenticationProviderKey); } } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs index 88019907..9fe77bbf 100644 --- a/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileOcelotConfigurationCreator.cs @@ -38,6 +38,7 @@ namespace Ocelot.Configuration.Creator private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator; private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; private readonly IRegionCreator _regionCreator; + private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; public FileOcelotConfigurationCreator( IOptions options, @@ -55,7 +56,8 @@ namespace Ocelot.Configuration.Creator IQoSOptionsCreator qosOptionsCreator, IReRouteOptionsCreator fileReRouteOptionsCreator, IRateLimitOptionsCreator rateLimitOptionsCreator, - IRegionCreator regionCreator + IRegionCreator regionCreator, + IHttpHandlerOptionsCreator httpHandlerOptionsCreator ) { _regionCreator = regionCreator; @@ -74,6 +76,7 @@ namespace Ocelot.Configuration.Creator _serviceProviderConfigCreator = serviceProviderConfigCreator; _qosOptionsCreator = qosOptionsCreator; _fileReRouteOptionsCreator = fileReRouteOptionsCreator; + _httpHandlerOptionsCreator = httpHandlerOptionsCreator; } public async Task> Create() @@ -143,6 +146,8 @@ namespace Ocelot.Configuration.Creator var region = _regionCreator.Create(fileReRoute); + var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute); + var reRoute = new ReRouteBuilder() .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) @@ -168,7 +173,7 @@ namespace Ocelot.Configuration.Creator .WithQosOptions(qosOptions) .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) .WithRateLimitOptions(rateLimitOption) - .WithAuthenticationProviderKey(fileReRoute.AuthenticationOptions.AuthenticationProviderKey) + .WithHttpHandlerOptions(httpHandlerOptions) .Build(); await SetupLoadBalancer(reRoute); diff --git a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs new file mode 100644 index 00000000..52aa7694 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs @@ -0,0 +1,13 @@ +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public class HttpHandlerOptionsCreator : IHttpHandlerOptionsCreator + { + public HttpHandlerOptions Create(FileReRoute fileReRoute) + { + return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect, + fileReRoute.HttpHandlerOptions.UseCookieContainer); + } + } +} diff --git a/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs new file mode 100644 index 00000000..34e5ffd1 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs @@ -0,0 +1,12 @@ +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + /// + /// Describes creation of HttpHandlerOptions + /// + public interface IHttpHandlerOptionsCreator + { + HttpHandlerOptions Create(FileReRoute fileReRoute); + } +} diff --git a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs index 95b339a9..4e02a158 100644 --- a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs +++ b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs @@ -41,8 +41,8 @@ namespace Ocelot.Configuration.Creator } var route = reRoute.ReRouteIsCaseSensitive - ? $"{upstreamTemplate}{RegExMatchEndString}" - : $"{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}"; + ? $"^{upstreamTemplate}{RegExMatchEndString}" + : $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}"; return route; } diff --git a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs new file mode 100644 index 00000000..ea352767 --- /dev/null +++ b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs @@ -0,0 +1,15 @@ +namespace Ocelot.Configuration.File +{ + public class FileHttpHandlerOptions + { + public FileHttpHandlerOptions() + { + AllowAutoRedirect = true; + UseCookieContainer = true; + } + + public bool AllowAutoRedirect { get; set; } + + public bool UseCookieContainer { get; set; } + } +} diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index af946a6c..9abb5a7d 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -15,6 +15,7 @@ namespace Ocelot.Configuration.File QoSOptions = new FileQoSOptions(); RateLimitOptions = new FileRateLimitRule(); AuthenticationOptions = new FileAuthenticationOptions(); + HttpHandlerOptions = new FileHttpHandlerOptions(); } public string DownstreamPathTemplate { get; set; } @@ -35,5 +36,6 @@ namespace Ocelot.Configuration.File public string LoadBalancer {get;set;} public FileRateLimitRule RateLimitOptions { get; set; } public FileAuthenticationOptions AuthenticationOptions { get; set; } + public FileHttpHandlerOptions HttpHandlerOptions { get; set; } } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/HttpHandlerOptions.cs b/src/Ocelot/Configuration/HttpHandlerOptions.cs new file mode 100644 index 00000000..0ec72d1d --- /dev/null +++ b/src/Ocelot/Configuration/HttpHandlerOptions.cs @@ -0,0 +1,25 @@ +namespace Ocelot.Configuration +{ + /// + /// Describes configuration parameters for http handler, + /// that is created to handle a request to service + /// + public class HttpHandlerOptions + { + public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer) + { + AllowAutoRedirect = allowAutoRedirect; + UseCookieContainer = useCookieContainer; + } + + /// + /// Specify if auto redirect is enabled + /// + public bool AllowAutoRedirect { get; private set; } + + /// + /// Specify is handler has to use a cookie container + /// + public bool UseCookieContainer { get; private set; } + } +} diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/ReRoute.cs index 75040be7..cfc8b9ba 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/ReRoute.cs @@ -30,7 +30,7 @@ namespace Ocelot.Configuration QoSOptions qosOptions, bool enableEndpointRateLimiting, RateLimitOptions ratelimitOptions, - string authenticationProviderKey) + HttpHandlerOptions httpHandlerOptions) { ReRouteKey = reRouteKey; ServiceProviderConfiguraion = serviceProviderConfiguraion; @@ -59,7 +59,7 @@ namespace Ocelot.Configuration QosOptionsOptions = qosOptions; EnableEndpointEndpointRateLimiting = enableEndpointRateLimiting; RateLimitOptions = ratelimitOptions; - AuthenticationProviderKey = authenticationProviderKey; + HttpHandlerOptions = httpHandlerOptions; } public string ReRouteKey {get;private set;} @@ -86,6 +86,6 @@ namespace Ocelot.Configuration public ServiceProviderConfiguration ServiceProviderConfiguraion { get; private set; } public bool EnableEndpointEndpointRateLimiting { get; private set; } public RateLimitOptions RateLimitOptions { get; private set; } - public string AuthenticationProviderKey { get; private set; } + public HttpHandlerOptions HttpHandlerOptions { get; private set; } } } \ No newline at end of file diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index bacae519..430ae18d 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -124,6 +124,7 @@ namespace Ocelot.DependencyInjection services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddSingleton(); // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // could maybe use a scoped data repository diff --git a/src/Ocelot/Request/Builder/HttpRequestCreator.cs b/src/Ocelot/Request/Builder/HttpRequestCreator.cs index f95db4b9..8c3c9218 100644 --- a/src/Ocelot/Request/Builder/HttpRequestCreator.cs +++ b/src/Ocelot/Request/Builder/HttpRequestCreator.cs @@ -10,9 +10,11 @@ namespace Ocelot.Request.Builder public async Task> Build( HttpRequestMessage httpRequestMessage, bool isQos, - IQoSProvider qosProvider) + IQoSProvider qosProvider, + bool useCookieContainer, + bool allowAutoRedirect) { - return new OkResponse(new Request(httpRequestMessage, isQos, qosProvider)); + return new OkResponse(new Request(httpRequestMessage, isQos, qosProvider, useCookieContainer, allowAutoRedirect)); } } } \ No newline at end of file diff --git a/src/Ocelot/Request/Builder/IRequestCreator.cs b/src/Ocelot/Request/Builder/IRequestCreator.cs index 85fbfa8d..d290db4f 100644 --- a/src/Ocelot/Request/Builder/IRequestCreator.cs +++ b/src/Ocelot/Request/Builder/IRequestCreator.cs @@ -11,6 +11,8 @@ Task> Build( HttpRequestMessage httpRequestMessage, bool isQos, - IQoSProvider qosProvider); + IQoSProvider qosProvider, + bool useCookieContainer, + bool allowAutoRedirect); } } diff --git a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs index c59af7a6..8e05f2ea 100644 --- a/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs +++ b/src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs @@ -46,7 +46,9 @@ namespace Ocelot.Request.Middleware var buildResult = await _requestCreator.Build( DownstreamRequest, DownstreamRoute.ReRoute.IsQos, - qosProvider.Data); + qosProvider.Data, + DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer, + DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect); if (buildResult.IsError) { diff --git a/src/Ocelot/Request/Request.cs b/src/Ocelot/Request/Request.cs index 3f1c654c..9f0b66e2 100644 --- a/src/Ocelot/Request/Request.cs +++ b/src/Ocelot/Request/Request.cs @@ -8,15 +8,21 @@ namespace Ocelot.Request public Request( HttpRequestMessage httpRequestMessage, bool isQos, - IQoSProvider qosProvider) + IQoSProvider qosProvider, + bool allowAutoRedirect, + bool useCookieContainer) { HttpRequestMessage = httpRequestMessage; IsQos = isQos; QosProvider = qosProvider; + AllowAutoRedirect = allowAutoRedirect; + UseCookieContainer = useCookieContainer; } public HttpRequestMessage HttpRequestMessage { get; private set; } public bool IsQos { get; private set; } public IQoSProvider QosProvider { get; private set; } + public bool AllowAutoRedirect { get; private set; } + public bool UseCookieContainer { get; private set; } } } diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index 141eab8b..83f475c2 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -20,9 +20,9 @@ namespace Ocelot.Requester return this; } - public IHttpClient Create() + public IHttpClient Create(bool useCookies, bool allowAutoRedirect) { - var httpclientHandler = new HttpClientHandler(); + var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = allowAutoRedirect, UseCookies = useCookies}; var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler)); diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index 3f85af0c..a6a28915 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Net.Http; using System.Threading.Tasks; +using Ocelot.Configuration; using Ocelot.Logging; using Ocelot.Responses; using Polly.CircuitBreaker; @@ -14,7 +15,8 @@ namespace Ocelot.Requester private readonly IHttpClientCache _cacheHandlers; private readonly IOcelotLogger _logger; - public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers) + public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, + IHttpClientCache cacheHandlers) { _logger = loggerFactory.CreateLogger(); _cacheHandlers = cacheHandlers; @@ -25,8 +27,8 @@ namespace Ocelot.Requester var builder = new HttpClientBuilder(); var cacheKey = GetCacheKey(request, builder); - - var httpClient = GetHttpClient(cacheKey, builder); + + var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect); try { @@ -54,13 +56,13 @@ namespace Ocelot.Requester } - private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder) + private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect) { var httpClient = _cacheHandlers.Get(cacheKey); if (httpClient == null) { - httpClient = builder.Create(); + httpClient = builder.Create(useCookieContainer, allowAutoRedirect); } return httpClient; } diff --git a/src/Ocelot/Requester/IHttpClientBuilder.cs b/src/Ocelot/Requester/IHttpClientBuilder.cs index 832fd8d1..6de5f87a 100644 --- a/src/Ocelot/Requester/IHttpClientBuilder.cs +++ b/src/Ocelot/Requester/IHttpClientBuilder.cs @@ -15,11 +15,13 @@ namespace Ocelot.Requester /// /// Sets a PollyCircuitBreakingDelegatingHandler . /// - IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger); - - /// + IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger); + + /// /// Creates the - /// - IHttpClient Create(); + /// + /// Defines if http client should use cookie container + /// Defines if http client should allow auto redirect + IHttpClient Create(bool useCookies, bool allowAutoRedirect); } } diff --git a/test/Ocelot.AcceptanceTests/QoSTests.cs b/test/Ocelot.AcceptanceTests/QoSTests.cs index 8825b75b..e8a8e6a1 100644 --- a/test/Ocelot.AcceptanceTests/QoSTests.cs +++ b/test/Ocelot.AcceptanceTests/QoSTests.cs @@ -98,7 +98,7 @@ namespace Ocelot.AcceptanceTests DownstreamScheme = "http", DownstreamHost = "localhost", DownstreamPort = 51880, - UpstreamPathTemplate = "working", + UpstreamPathTemplate = "/working", UpstreamHttpMethod = new List { "Get" }, } } diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index 6a284b37..9c3487d4 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -61,6 +61,47 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void bug() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/api/v1/vacancy", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, + UpstreamPathTemplate = "/vacancy/", + UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, + ServiceName = "botCore", + LoadBalancer = "LeastConnection" + }, + new FileReRoute + { + DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, + UpstreamPathTemplate = "/vacancy/{vacancyId}", + UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, + ServiceName = "botCore", + LoadBalancer = "LeastConnection" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/vacancy/1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + [Fact] public void should_return_response_200_when_path_missing_forward_slash_as_first_char() { @@ -402,6 +443,46 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_return_404_when_calling_upstream_route_with_no_matching_downstream_re_route_github_issue_134() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/api/v1/vacancy", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, + UpstreamPathTemplate = "/vacancy/", + UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, + ServiceName = "botCore", + LoadBalancer = "LeastConnection" + }, + new FileReRoute + { + DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", + DownstreamScheme = "http", + DownstreamHost = "localhost", + DownstreamPort = 51879, + UpstreamPathTemplate = "/vacancy/{vacancyId}", + UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, + ServiceName = "botCore", + LoadBalancer = "LeastConnection" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/v1/vacancy/1", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("api/vacancy/1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) + .BDDfy(); + } + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) { _builder = new WebHostBuilder() @@ -413,7 +494,7 @@ namespace Ocelot.AcceptanceTests { app.UsePathBase(basePath); app.Run(async context => - { + { _downstreamPath = context.Request.PathBase.Value; context.Response.StatusCode = statusCode; await context.Response.WriteAsync(responseBody); diff --git a/test/Ocelot.ManualTest/Program.cs b/test/Ocelot.ManualTest/Program.cs index f90cfbc6..98b1f927 100644 --- a/test/Ocelot.ManualTest/Program.cs +++ b/test/Ocelot.ManualTest/Program.cs @@ -14,8 +14,8 @@ namespace Ocelot.ManualTest }); builder.UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) - .UseStartup(); - + .UseIISIntegration() + .UseStartup(); var host = builder.Build(); host.Run(); } diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index 17e5a2b4..d897bf64 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -1,689 +1,730 @@ -using System.Collections.Generic; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Moq; -using Ocelot.Cache; -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Ocelot.Configuration.Validator; -using Ocelot.LoadBalancer.LoadBalancers; -using Ocelot.Logging; -using Ocelot.Requester.QoS; -using Ocelot.Responses; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - using System.Collections; - - using Ocelot.UnitTests.TestData; - - public class FileConfigurationCreatorTests - { - private readonly Mock> _fileConfig; - private readonly Mock _validator; - private Response _config; - private FileConfiguration _fileConfiguration; - private readonly Mock _logger; - private readonly FileOcelotConfigurationCreator _ocelotConfigurationCreator; - private readonly Mock _loadBalancerFactory; - private readonly Mock _loadBalancerHouse; - private readonly Mock _loadBalancer; - private readonly Mock _qosProviderFactory; - private readonly Mock _qosProviderHouse; - private readonly Mock _qosProvider; - private Mock _claimsToThingCreator; - private Mock _authOptionsCreator; - private Mock _upstreamTemplatePatternCreator; - private Mock _requestIdKeyCreator; - private Mock _serviceProviderConfigCreator; - private Mock _qosOptionsCreator; - private Mock _fileReRouteOptionsCreator; - private Mock _rateLimitOptions; - private Mock _regionCreator; - - public FileConfigurationCreatorTests() - { - _qosProviderFactory = new Mock(); - _qosProviderHouse = new Mock(); - _qosProvider = new Mock(); - _logger = new Mock(); - _validator = new Mock(); - _fileConfig = new Mock>(); - _loadBalancerFactory = new Mock(); - _loadBalancerHouse = new Mock(); - _loadBalancer = new Mock(); - _claimsToThingCreator = new Mock(); - _authOptionsCreator = new Mock(); - _upstreamTemplatePatternCreator = new Mock(); - _requestIdKeyCreator = new Mock(); - _serviceProviderConfigCreator = new Mock(); - _qosOptionsCreator = new Mock(); - _fileReRouteOptionsCreator = new Mock(); - _rateLimitOptions = new Mock(); - _regionCreator = new Mock(); - - _ocelotConfigurationCreator = new FileOcelotConfigurationCreator( - _fileConfig.Object, _validator.Object, _logger.Object, - _loadBalancerFactory.Object, _loadBalancerHouse.Object, - _qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object, - _authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object, - _serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object, - _rateLimitOptions.Object, _regionCreator.Object); - } - - [Fact] - public void should_call_region_creator() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHost = "127.0.0.1", - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - FileCacheOptions = new FileCacheOptions - { - Region = "region" - } - } - }, - })) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingRegionIsReturned("region")) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheRegionCreatorIsCalledCorrectly("region")) - .BDDfy(); - } - - private void GivenTheFollowingRegionIsReturned(string region) - { - _regionCreator - .Setup(x => x.Create(It.IsAny())) - .Returns(region); - } - - private void ThenTheRegionCreatorIsCalledCorrectly(string expected) - { - _regionCreator - .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); - } - - [Fact] - public void should_call_rate_limit_options_creator() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHost = "127.0.0.1", - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheRateLimitOptionsCreatorIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_call_qos_options_creator() - { - var expected = new QoSOptionsBuilder() - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .WithTimeoutValue(1) - .Build(); - - var serviceOptions = new ReRouteOptionsBuilder() - .WithIsQos(true) - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHost = "127.0.0.1", - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - QoSOptions = new FileQoSOptions - { - TimeoutValue = 1, - DurationOfBreak = 1, - ExceptionsAllowedBeforeBreaking = 1 - } - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions)) - .And(x => x.GivenTheQosProviderFactoryReturns()) - .And(x => x.GivenTheQosOptionsCreatorReturns(expected)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheQosOptionsAre(expected)) - .And(x => x.TheQosProviderFactoryIsCalledCorrectly()) - .And(x => x.ThenTheQosProviderHouseIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_create_load_balancer() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHost = "127.0.0.1", - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheLoadBalancerFactoryReturns()) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly()) - .And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_use_downstream_host() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHost = "127.0.0.1", - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamHost("127.0.0.1") - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - })) - .BDDfy(); - } - - [Fact] - public void should_use_downstream_scheme() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamScheme = "https", - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - }, - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamScheme("https") - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build() - })) - .BDDfy(); - } - - [Fact] - public void should_use_service_discovery_for_downstream_service_host() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = false, - ServiceName = "ProductService" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Provider = "consul", - Host = "127.0.0.1" - } - } - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder() - .WithUseServiceDiscovery(true) - .WithServiceDiscoveryProvider("consul") - .WithServiceDiscoveryProviderHost("127.0.0.1") - .WithServiceName("ProductService") - .Build()) - .Build() - })) - .BDDfy(); - } - - [Fact] - public void should_not_use_service_discovery_for_downstream_host_url_when_no_service_name() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = false, - } - } - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder() - .WithUseServiceDiscovery(false) - .Build()) - .Build() - })) - .BDDfy(); - } - - [Fact] - public void should_call_template_pattern_creator_correctly() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = false - } - } - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$")) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") - .Build() - })) - .BDDfy(); - } - - [Fact] - public void should_call_request_id_creator() - { - var reRouteOptions = new ReRouteOptionsBuilder() - .Build(); - - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - RequestIdKey = "blahhhh" - } - })) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheRequestIdCreatorReturns("blahhhh")) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(new List - { - new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithRequestIdKey("blahhhh") - .Build() - })) - .And(x => x.ThenTheRequestIdKeyCreatorIsCalledCorrectly()) - .BDDfy(); - } - - [Theory] - [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] - public void should_create_with_headers_to_extract(FileConfiguration fileConfig) - { - var reRouteOptions = new ReRouteOptionsBuilder() - .WithIsAuthenticated(true) - .Build(); - - var authenticationOptions = new AuthenticationOptionsBuilder() - .WithAllowedScopes(new List()) - .Build(); - - var expected = new List - { - new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithAuthenticationOptions(authenticationOptions) - .WithClaimsToHeaders(new List - { - new ClaimToThing("CustomerId", "CustomerId", "", 0), - }) - .Build() - }; - - this.Given(x => x.GivenTheConfigIs(fileConfig)) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheClaimsToThingCreatorReturns(new List { new ClaimToThing("CustomerId", "CustomerId", "", 0) })) - .And(x => x.GivenTheLoadBalancerFactoryReturns()) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(expected)) - .And(x => x.ThenTheAuthenticationOptionsAre(expected)) - .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) - .BDDfy(); - } - - [Theory] - [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] - public void should_create_with_authentication_properties(FileConfiguration fileConfig) - { - var reRouteOptions = new ReRouteOptionsBuilder() - .WithIsAuthenticated(true) - .Build(); - - var authenticationOptions = new AuthenticationOptionsBuilder() - .WithAllowedScopes(new List()) - .Build(); - - var expected = new List - { - new ReRouteBuilder() - .WithDownstreamPathTemplate("/products/{productId}") - .WithUpstreamPathTemplate("/api/products/{productId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithAuthenticationOptions(authenticationOptions) - .Build() - }; - - this.Given(x => x.GivenTheConfigIs(fileConfig)) - .And(x => x.GivenTheConfigIsValid()) - .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) - .And(x => x.GivenTheLoadBalancerFactoryReturns()) - .When(x => x.WhenICreateTheConfig()) - .Then(x => x.ThenTheReRoutesAre(expected)) - .And(x => x.ThenTheAuthenticationOptionsAre(expected)) - .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) - .BDDfy(); - } - - private void GivenTheFollowingOptionsAreReturned(ReRouteOptions fileReRouteOptions) - { - _fileReRouteOptionsCreator - .Setup(x => x.Create(It.IsAny())) - .Returns(fileReRouteOptions); - } - - private void ThenTheRateLimitOptionsCreatorIsCalledCorrectly() - { - _rateLimitOptions - .Verify(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - } - - private void GivenTheConfigIsValid() - { - _validator - .Setup(x => x.IsValid(It.IsAny())) - .ReturnsAsync(new OkResponse(new ConfigurationValidationResult(false))); - } - - private void GivenTheConfigIs(FileConfiguration fileConfiguration) - { - _fileConfiguration = fileConfiguration; - _fileConfig - .Setup(x => x.Value) - .Returns(_fileConfiguration); - } - - private void WhenICreateTheConfig() - { - _config = _ocelotConfigurationCreator.Create().Result; - } - - private void ThenTheReRoutesAre(List expectedReRoutes) - { - for (int i = 0; i < _config.Data.ReRoutes.Count; i++) - { - var result = _config.Data.ReRoutes[i]; - var expected = expectedReRoutes[i]; - - result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value); - result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); - result.UpstreamPathTemplate.Value.ShouldBe(expected.UpstreamPathTemplate.Value); - result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern); - result.ClaimsToClaims.Count.ShouldBe(expected.ClaimsToClaims.Count); - result.ClaimsToHeaders.Count.ShouldBe(expected.ClaimsToHeaders.Count); - result.ClaimsToQueries.Count.ShouldBe(expected.ClaimsToQueries.Count); - result.RequestIdKey.ShouldBe(expected.RequestIdKey); - - } - } - - private void ThenTheServiceConfigurationIs(ServiceProviderConfiguration expected) - { - for (int i = 0; i < _config.Data.ReRoutes.Count; i++) - { - var result = _config.Data.ReRoutes[i]; - result.ServiceProviderConfiguraion.DownstreamHost.ShouldBe(expected.DownstreamHost); - result.ServiceProviderConfiguraion.DownstreamPort.ShouldBe(expected.DownstreamPort); - result.ServiceProviderConfiguraion.ServiceDiscoveryProvider.ShouldBe(expected.ServiceDiscoveryProvider); - result.ServiceProviderConfiguraion.ServiceName.ShouldBe(expected.ServiceName); - result.ServiceProviderConfiguraion.ServiceProviderHost.ShouldBe(expected.ServiceProviderHost); - result.ServiceProviderConfiguraion.ServiceProviderPort.ShouldBe(expected.ServiceProviderPort); - } - } - - private void ThenTheAuthenticationOptionsAre(List expectedReRoutes) - { - for (int i = 0; i < _config.Data.ReRoutes.Count; i++) - { - var result = _config.Data.ReRoutes[i].AuthenticationOptions; - var expected = expectedReRoutes[i].AuthenticationOptions; - result.AllowedScopes.ShouldBe(expected.AllowedScopes); - } - } - - private void GivenTheLoadBalancerFactoryReturns() - { - _loadBalancerFactory - .Setup(x => x.Get(It.IsAny())) - .ReturnsAsync(_loadBalancer.Object); - } - - private void TheLoadBalancerFactoryIsCalledCorrectly() - { - _loadBalancerFactory - .Verify(x => x.Get(It.IsAny()), Times.Once); - } - - private void ThenTheLoadBalancerHouseIsCalledCorrectly() - { - _loadBalancerHouse - .Verify(x => x.Add(It.IsAny(), _loadBalancer.Object), Times.Once); - } - - private void GivenTheQosProviderFactoryReturns() - { - _qosProviderFactory - .Setup(x => x.Get(It.IsAny())) - .Returns(_qosProvider.Object); - } - - private void TheQosProviderFactoryIsCalledCorrectly() - { - _qosProviderFactory - .Verify(x => x.Get(It.IsAny()), Times.Once); - } - - private void ThenTheQosProviderHouseIsCalledCorrectly() - { - _qosProviderHouse - .Verify(x => x.Add(It.IsAny(), _qosProvider.Object), Times.Once); - } - - private void GivenTheClaimsToThingCreatorReturns(List claimsToThing) - { - _claimsToThingCreator - .Setup(x => x.Create(_fileConfiguration.ReRoutes[0].AddHeadersToRequest)) - .Returns(claimsToThing); - } - - private void GivenTheAuthOptionsCreatorReturns(AuthenticationOptions authOptions) - { - _authOptionsCreator - .Setup(x => x.Create(It.IsAny())) - .Returns(authOptions); - } - - private void ThenTheAuthOptionsCreatorIsCalledCorrectly() - { - _authOptionsCreator - .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); - } - - private void GivenTheUpstreamTemplatePatternCreatorReturns(string pattern) - { - _upstreamTemplatePatternCreator - .Setup(x => x.Create(It.IsAny())) - .Returns(pattern); - } - - private void ThenTheRequestIdKeyCreatorIsCalledCorrectly() - { - _requestIdKeyCreator - .Verify(x => x.Create(_fileConfiguration.ReRoutes[0], _fileConfiguration.GlobalConfiguration), Times.Once); - } - - private void GivenTheRequestIdCreatorReturns(string requestId) - { - _requestIdKeyCreator - .Setup(x => x.Create(It.IsAny(), It.IsAny())) - .Returns(requestId); - } - - private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions) - { - _qosOptionsCreator - .Setup(x => x.Create(_fileConfiguration.ReRoutes[0])) - .Returns(qosOptions); - } - - private void ThenTheQosOptionsAre(QoSOptions qosOptions) - { - _config.Data.ReRoutes[0].QosOptionsOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); - - _config.Data.ReRoutes[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); - _config.Data.ReRoutes[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); - } - } -} +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; +using Ocelot.Cache; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Ocelot.Configuration.Validator; +using Ocelot.LoadBalancer.LoadBalancers; +using Ocelot.Logging; +using Ocelot.Requester.QoS; +using Ocelot.Responses; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + using System.Collections; + + using Ocelot.UnitTests.TestData; + + public class FileConfigurationCreatorTests + { + private readonly Mock> _fileConfig; + private readonly Mock _validator; + private Response _config; + private FileConfiguration _fileConfiguration; + private readonly Mock _logger; + private readonly FileOcelotConfigurationCreator _ocelotConfigurationCreator; + private readonly Mock _loadBalancerFactory; + private readonly Mock _loadBalancerHouse; + private readonly Mock _loadBalancer; + private readonly Mock _qosProviderFactory; + private readonly Mock _qosProviderHouse; + private readonly Mock _qosProvider; + private Mock _claimsToThingCreator; + private Mock _authOptionsCreator; + private Mock _upstreamTemplatePatternCreator; + private Mock _requestIdKeyCreator; + private Mock _serviceProviderConfigCreator; + private Mock _qosOptionsCreator; + private Mock _fileReRouteOptionsCreator; + private Mock _rateLimitOptions; + private Mock _regionCreator; + private Mock _httpHandlerOptionsCreator; + + public FileConfigurationCreatorTests() + { + _qosProviderFactory = new Mock(); + _qosProviderHouse = new Mock(); + _qosProvider = new Mock(); + _logger = new Mock(); + _validator = new Mock(); + _fileConfig = new Mock>(); + _loadBalancerFactory = new Mock(); + _loadBalancerHouse = new Mock(); + _loadBalancer = new Mock(); + _claimsToThingCreator = new Mock(); + _authOptionsCreator = new Mock(); + _upstreamTemplatePatternCreator = new Mock(); + _requestIdKeyCreator = new Mock(); + _serviceProviderConfigCreator = new Mock(); + _qosOptionsCreator = new Mock(); + _fileReRouteOptionsCreator = new Mock(); + _rateLimitOptions = new Mock(); + _regionCreator = new Mock(); + _httpHandlerOptionsCreator = new Mock(); + + _ocelotConfigurationCreator = new FileOcelotConfigurationCreator( + _fileConfig.Object, _validator.Object, _logger.Object, + _loadBalancerFactory.Object, _loadBalancerHouse.Object, + _qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object, + _authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object, + _serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object, + _rateLimitOptions.Object, _regionCreator.Object, _httpHandlerOptionsCreator.Object); + } + + [Fact] + public void should_call_region_creator() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + FileCacheOptions = new FileCacheOptions + { + Region = "region" + } + } + }, + })) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingRegionIsReturned("region")) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheRegionCreatorIsCalledCorrectly("region")) + .BDDfy(); + } + + private void GivenTheFollowingRegionIsReturned(string region) + { + _regionCreator + .Setup(x => x.Create(It.IsAny())) + .Returns(region); + } + + private void ThenTheRegionCreatorIsCalledCorrectly(string expected) + { + _regionCreator + .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); + } + + [Fact] + public void should_call_rate_limit_options_creator() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + } + }, + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheRateLimitOptionsCreatorIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_call_qos_options_creator() + { + var expected = new QoSOptionsBuilder() + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .WithTimeoutValue(1) + .Build(); + + var serviceOptions = new ReRouteOptionsBuilder() + .WithIsQos(true) + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + QoSOptions = new FileQoSOptions + { + TimeoutValue = 1, + DurationOfBreak = 1, + ExceptionsAllowedBeforeBreaking = 1 + } + } + }, + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions)) + .And(x => x.GivenTheQosProviderFactoryReturns()) + .And(x => x.GivenTheQosOptionsCreatorReturns(expected)) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheQosOptionsAre(expected)) + .And(x => x.TheQosProviderFactoryIsCalledCorrectly()) + .And(x => x.ThenTheQosProviderHouseIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_create_load_balancer() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + } + }, + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .And(x => x.GivenTheLoadBalancerFactoryReturns()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.TheLoadBalancerFactoryIsCalledCorrectly()) + .And(x => x.ThenTheLoadBalancerHouseIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_use_downstream_host() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + } + }, + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamHost("127.0.0.1") + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build() + })) + .BDDfy(); + } + + [Fact] + public void should_use_downstream_scheme() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamScheme = "https", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + } + }, + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamScheme("https") + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build() + })) + .BDDfy(); + } + + [Fact] + public void should_use_service_discovery_for_downstream_service_host() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + ReRouteIsCaseSensitive = false, + ServiceName = "ProductService" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Provider = "consul", + Host = "127.0.0.1" + } + } + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder() + .WithUseServiceDiscovery(true) + .WithServiceDiscoveryProvider("consul") + .WithServiceDiscoveryProviderHost("127.0.0.1") + .WithServiceName("ProductService") + .Build()) + .Build() + })) + .BDDfy(); + } + + [Fact] + public void should_not_use_service_discovery_for_downstream_host_url_when_no_service_name() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + ReRouteIsCaseSensitive = false, + } + } + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithServiceProviderConfiguraion(new ServiceProviderConfigurationBuilder() + .WithUseServiceDiscovery(false) + .Build()) + .Build() + })) + .BDDfy(); + } + + [Fact] + public void should_call_template_pattern_creator_correctly() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + ReRouteIsCaseSensitive = false + } + } + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$")) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamTemplatePattern("(?i)/api/products/.*/$") + .Build() + })) + .BDDfy(); + } + + [Fact] + public void should_call_request_id_creator() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + ReRouteIsCaseSensitive = true + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + RequestIdKey = "blahhhh" + } + })) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .And(x => x.GivenTheRequestIdCreatorReturns("blahhhh")) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithRequestIdKey("blahhhh") + .Build() + })) + .And(x => x.ThenTheRequestIdKeyCreatorIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_call_httpHandler_creator() + { + var reRouteOptions = new ReRouteOptionsBuilder() + .Build(); + var httpHandlerOptions = new HttpHandlerOptions(true, true); + + this.Given(x => x.GivenTheConfigIs(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamHost = "127.0.0.1", + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" } + } + }, + })) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingHttpHandlerOptionsAreReturned(httpHandlerOptions)) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly()) + .BDDfy(); + } + + private void GivenTheFollowingHttpHandlerOptionsAreReturned(HttpHandlerOptions httpHandlerOptions) + { + _httpHandlerOptionsCreator.Setup(x => x.Create(It.IsAny())) + .Returns(httpHandlerOptions); + } + + private void ThenTheHttpHandlerOptionsCreatorIsCalledCorrectly() + { + _httpHandlerOptionsCreator.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once()); + } + + [Theory] + [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] + public void should_create_with_headers_to_extract(FileConfiguration fileConfig) + { + var reRouteOptions = new ReRouteOptionsBuilder() + .WithIsAuthenticated(true) + .Build(); + + var authenticationOptions = new AuthenticationOptionsBuilder() + .WithAllowedScopes(new List()) + .Build(); + + var expected = new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithAuthenticationOptions(authenticationOptions) + .WithClaimsToHeaders(new List + { + new ClaimToThing("CustomerId", "CustomerId", "", 0), + }) + .Build() + }; + + this.Given(x => x.GivenTheConfigIs(fileConfig)) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .And(x => x.GivenTheClaimsToThingCreatorReturns(new List { new ClaimToThing("CustomerId", "CustomerId", "", 0) })) + .And(x => x.GivenTheLoadBalancerFactoryReturns()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(expected)) + .And(x => x.ThenTheAuthenticationOptionsAre(expected)) + .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) + .BDDfy(); + } + + [Theory] + [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] + public void should_create_with_authentication_properties(FileConfiguration fileConfig) + { + var reRouteOptions = new ReRouteOptionsBuilder() + .WithIsAuthenticated(true) + .Build(); + + var authenticationOptions = new AuthenticationOptionsBuilder() + .WithAllowedScopes(new List()) + .Build(); + + var expected = new List + { + new ReRouteBuilder() + .WithDownstreamPathTemplate("/products/{productId}") + .WithUpstreamPathTemplate("/api/products/{productId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithAuthenticationOptions(authenticationOptions) + .Build() + }; + + this.Given(x => x.GivenTheConfigIs(fileConfig)) + .And(x => x.GivenTheConfigIsValid()) + .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) + .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) + .And(x => x.GivenTheLoadBalancerFactoryReturns()) + .When(x => x.WhenICreateTheConfig()) + .Then(x => x.ThenTheReRoutesAre(expected)) + .And(x => x.ThenTheAuthenticationOptionsAre(expected)) + .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) + .BDDfy(); + } + + private void GivenTheFollowingOptionsAreReturned(ReRouteOptions fileReRouteOptions) + { + _fileReRouteOptionsCreator + .Setup(x => x.Create(It.IsAny())) + .Returns(fileReRouteOptions); + } + + private void ThenTheRateLimitOptionsCreatorIsCalledCorrectly() + { + _rateLimitOptions + .Verify(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + private void GivenTheConfigIsValid() + { + _validator + .Setup(x => x.IsValid(It.IsAny())) + .ReturnsAsync(new OkResponse(new ConfigurationValidationResult(false))); + } + + private void GivenTheConfigIs(FileConfiguration fileConfiguration) + { + _fileConfiguration = fileConfiguration; + _fileConfig + .Setup(x => x.Value) + .Returns(_fileConfiguration); + } + + private void WhenICreateTheConfig() + { + _config = _ocelotConfigurationCreator.Create().Result; + } + + private void ThenTheReRoutesAre(List expectedReRoutes) + { + for (int i = 0; i < _config.Data.ReRoutes.Count; i++) + { + var result = _config.Data.ReRoutes[i]; + var expected = expectedReRoutes[i]; + + result.DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate.Value); + result.UpstreamHttpMethod.ShouldBe(expected.UpstreamHttpMethod); + result.UpstreamPathTemplate.Value.ShouldBe(expected.UpstreamPathTemplate.Value); + result.UpstreamTemplatePattern.ShouldBe(expected.UpstreamTemplatePattern); + result.ClaimsToClaims.Count.ShouldBe(expected.ClaimsToClaims.Count); + result.ClaimsToHeaders.Count.ShouldBe(expected.ClaimsToHeaders.Count); + result.ClaimsToQueries.Count.ShouldBe(expected.ClaimsToQueries.Count); + result.RequestIdKey.ShouldBe(expected.RequestIdKey); + + } + } + + private void ThenTheServiceConfigurationIs(ServiceProviderConfiguration expected) + { + for (int i = 0; i < _config.Data.ReRoutes.Count; i++) + { + var result = _config.Data.ReRoutes[i]; + result.ServiceProviderConfiguraion.DownstreamHost.ShouldBe(expected.DownstreamHost); + result.ServiceProviderConfiguraion.DownstreamPort.ShouldBe(expected.DownstreamPort); + result.ServiceProviderConfiguraion.ServiceDiscoveryProvider.ShouldBe(expected.ServiceDiscoveryProvider); + result.ServiceProviderConfiguraion.ServiceName.ShouldBe(expected.ServiceName); + result.ServiceProviderConfiguraion.ServiceProviderHost.ShouldBe(expected.ServiceProviderHost); + result.ServiceProviderConfiguraion.ServiceProviderPort.ShouldBe(expected.ServiceProviderPort); + } + } + + private void ThenTheAuthenticationOptionsAre(List expectedReRoutes) + { + for (int i = 0; i < _config.Data.ReRoutes.Count; i++) + { + var result = _config.Data.ReRoutes[i].AuthenticationOptions; + var expected = expectedReRoutes[i].AuthenticationOptions; + result.AllowedScopes.ShouldBe(expected.AllowedScopes); + } + } + + private void GivenTheLoadBalancerFactoryReturns() + { + _loadBalancerFactory + .Setup(x => x.Get(It.IsAny())) + .ReturnsAsync(_loadBalancer.Object); + } + + private void TheLoadBalancerFactoryIsCalledCorrectly() + { + _loadBalancerFactory + .Verify(x => x.Get(It.IsAny()), Times.Once); + } + + private void ThenTheLoadBalancerHouseIsCalledCorrectly() + { + _loadBalancerHouse + .Verify(x => x.Add(It.IsAny(), _loadBalancer.Object), Times.Once); + } + + private void GivenTheQosProviderFactoryReturns() + { + _qosProviderFactory + .Setup(x => x.Get(It.IsAny())) + .Returns(_qosProvider.Object); + } + + private void TheQosProviderFactoryIsCalledCorrectly() + { + _qosProviderFactory + .Verify(x => x.Get(It.IsAny()), Times.Once); + } + + private void ThenTheQosProviderHouseIsCalledCorrectly() + { + _qosProviderHouse + .Verify(x => x.Add(It.IsAny(), _qosProvider.Object), Times.Once); + } + + private void GivenTheClaimsToThingCreatorReturns(List claimsToThing) + { + _claimsToThingCreator + .Setup(x => x.Create(_fileConfiguration.ReRoutes[0].AddHeadersToRequest)) + .Returns(claimsToThing); + } + + private void GivenTheAuthOptionsCreatorReturns(AuthenticationOptions authOptions) + { + _authOptionsCreator + .Setup(x => x.Create(It.IsAny())) + .Returns(authOptions); + } + + private void ThenTheAuthOptionsCreatorIsCalledCorrectly() + { + _authOptionsCreator + .Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once); + } + + private void GivenTheUpstreamTemplatePatternCreatorReturns(string pattern) + { + _upstreamTemplatePatternCreator + .Setup(x => x.Create(It.IsAny())) + .Returns(pattern); + } + + private void ThenTheRequestIdKeyCreatorIsCalledCorrectly() + { + _requestIdKeyCreator + .Verify(x => x.Create(_fileConfiguration.ReRoutes[0], _fileConfiguration.GlobalConfiguration), Times.Once); + } + + private void GivenTheRequestIdCreatorReturns(string requestId) + { + _requestIdKeyCreator + .Setup(x => x.Create(It.IsAny(), It.IsAny())) + .Returns(requestId); + } + + private void GivenTheQosOptionsCreatorReturns(QoSOptions qosOptions) + { + _qosOptionsCreator + .Setup(x => x.Create(_fileConfiguration.ReRoutes[0])) + .Returns(qosOptions); + } + + private void ThenTheQosOptionsAre(QoSOptions qosOptions) + { + _config.Data.ReRoutes[0].QosOptionsOptions.DurationOfBreak.ShouldBe(qosOptions.DurationOfBreak); + + _config.Data.ReRoutes[0].QosOptionsOptions.ExceptionsAllowedBeforeBreaking.ShouldBe(qosOptions.ExceptionsAllowedBeforeBreaking); + _config.Data.ReRoutes[0].QosOptionsOptions.TimeoutValue.ShouldBe(qosOptions.TimeoutValue); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs new file mode 100644 index 00000000..69f414e5 --- /dev/null +++ b/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs @@ -0,0 +1,71 @@ +using Ocelot.Configuration; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + + public class HttpHandlerOptionsCreatorTests + { + private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; + private FileReRoute _fileReRoute; + private HttpHandlerOptions _httpHandlerOptions; + + public HttpHandlerOptionsCreatorTests() + { + _httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(); + } + + [Fact] + public void should_create_options_with_useCookie_and_allowAutoRedirect_true_as_default() + { + var fileReRoute = new FileReRoute(); + var expectedOptions = new HttpHandlerOptions(true, true); + + this.Given(x => GivenTheFollowing(fileReRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_with_specified_useCookie_and_allowAutoRedirect() + { + var fileReRoute = new FileReRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + AllowAutoRedirect = false, + UseCookieContainer = false + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false); + + this.Given(x => GivenTheFollowing(fileReRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + private void GivenTheFollowing(FileReRoute fileReRoute) + { + _fileReRoute = fileReRoute; + } + + private void WhenICreateHttpHandlerOptions() + { + _httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute); + } + + private void ThenTheFollowingOptionsReturned(HttpHandlerOptions options) + { + _httpHandlerOptions.ShouldNotBeNull(); + _httpHandlerOptions.AllowAutoRedirect.ShouldBe(options.AllowAutoRedirect); + _httpHandlerOptions.UseCookieContainer.ShouldBe(options.UseCookieContainer); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs index 34db1232..21c5be38 100644 --- a/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs @@ -28,7 +28,7 @@ namespace Ocelot.UnitTests.Configuration this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("(?i)/PRODUCTS/.*/$")) + .Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/.*/$")) .BDDfy(); } @@ -42,7 +42,7 @@ namespace Ocelot.UnitTests.Configuration }; this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("/PRODUCTS/.*/$")) + .Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.*/$")) .BDDfy(); } @@ -57,7 +57,7 @@ namespace Ocelot.UnitTests.Configuration this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/$")) + .Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/$")) .BDDfy(); } @@ -72,7 +72,7 @@ namespace Ocelot.UnitTests.Configuration this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/variants/.*/$")) + .Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/variants/.*/$")) .BDDfy(); } [Fact] @@ -86,7 +86,7 @@ namespace Ocelot.UnitTests.Configuration this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("/api/products/.*/variants/.*/$")) + .Then(x => x.ThenTheFollowingIsReturned("^/api/products/.*/variants/.*/$")) .BDDfy(); } diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/RegExUrlMatcherTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/RegExUrlMatcherTests.cs index 70dd747d..9f76228b 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/RegExUrlMatcherTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/UrlMatcher/RegExUrlMatcherTests.cs @@ -28,6 +28,16 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher .BDDfy(); } + [Fact] + public void should_not_match_issue_134() + { + this.Given(x => x.GivenIHaveAUpstreamPath("/api/vacancy/1/")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^(?i)/vacancy/.*/$")) + .When(x => x.WhenIMatchThePaths()) + .And(x => x.ThenTheResultIsFalse()) + .BDDfy(); + } + [Fact] public void should_match_forward_slash_only_regex() { @@ -42,7 +52,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void should_find_match_when_template_smaller_than_valid_path() { this.Given(x => x.GivenIHaveAUpstreamPath("/api/products/2354325435624623464235")) - .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("/api/products/.*$")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/api/products/.*$")) .When(x => x.WhenIMatchThePaths()) .And(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -52,7 +62,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void should_not_find_match() { this.Given(x => x.GivenIHaveAUpstreamPath("/api/values")) - .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("/$")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^/$")) .When(x => x.WhenIMatchThePaths()) .And(x => x.ThenTheResultIsFalse()) .BDDfy(); @@ -62,7 +72,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url() { this.Given(x => x.GivenIHaveAUpstreamPath("")) - .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("$")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^$")) .When(x => x.WhenIMatchThePaths()) .And(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -72,7 +82,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url_with_no_slash() { this.Given(x => x.GivenIHaveAUpstreamPath("api")) - .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api$")) + .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -82,7 +92,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url_with_one_slash() { this.Given(x => x.GivenIHaveAUpstreamPath("api/")) - .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/$")) + .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -92,7 +102,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url_with_downstream_template() { this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/")) - .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/$")) + .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -102,7 +112,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url_with_downstream_template_with_one_place_holder() { this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1")) - .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*$")) + .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -112,7 +122,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url_with_downstream_template_with_two_place_holders() { this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/2")) - .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/.*$")) + .Given(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/.*$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -122,7 +132,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url_with_downstream_template_with_two_place_holders_seperated_by_something() { this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2")) - .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*$")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -132,7 +142,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url_with_downstream_template_with_three_place_holders_seperated_by_something() { this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2/variant/123")) - .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*/variant/.*$")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/.*$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -142,7 +152,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void can_match_down_stream_url_with_downstream_template_with_three_place_holders() { this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/1/categories/2/variant/")) - .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*/variant/$")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -152,7 +162,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void should_ignore_case_sensitivity() { this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/")) - .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("(?i)api/product/products/.*/categories/.*/variant/$")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^(?i)api/product/products/.*/categories/.*/variant/$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsTrue()) .BDDfy(); @@ -162,7 +172,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder.UrlMatcher public void should_respect_case_sensitivity() { this.Given(x => x.GivenIHaveAUpstreamPath("API/product/products/1/categories/2/variant/")) - .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("api/product/products/.*/categories/.*/variant/$")) + .And(x => x.GivenIHaveAnUpstreamUrlTemplatePattern("^api/product/products/.*/categories/.*/variant/$")) .When(x => x.WhenIMatchThePaths()) .Then(x => x.ThenTheResultIsFalse()) .BDDfy(); diff --git a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs index 02f2b208..807c35a5 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestBuilderMiddlewareTests.cs @@ -15,8 +15,9 @@ using TestStack.BDDfy; using Xunit; using Ocelot.Requester.QoS; - using Microsoft.AspNetCore.Builder; - + using Ocelot.Configuration; + using Microsoft.AspNetCore.Builder; + public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest { private readonly Mock _requestBuilder; @@ -50,12 +51,13 @@ new ReRouteBuilder() .WithRequestIdKey("LSRequestId") .WithUpstreamHttpMethod(new List { "Get" }) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true)) .Build()); this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) .And(x => x.GivenTheQosProviderHouseReturns(new OkResponse(new NoQoSProvider()))) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider()))) + .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false))) .When(x => x.WhenICallTheMiddleware()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .BDDfy(); @@ -103,7 +105,11 @@ _request = new OkResponse(request); _requestBuilder - .Setup(x => x.Build(It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(x => x.Build(It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) .ReturnsAsync(_request); } diff --git a/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs b/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs index d831aed9..f4f67ca0 100644 --- a/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs +++ b/test/Ocelot.UnitTests/Request/HttpRequestCreatorTests.cs @@ -15,6 +15,9 @@ private readonly bool _isQos; private readonly IQoSProvider _qoSProvider; private readonly HttpRequestMessage _requestMessage; + private readonly bool _useCookieContainer; + private readonly bool _allowAutoRedirect; + private Response _response; public HttpRequestCreatorTests() @@ -22,6 +25,9 @@ _requestCreator = new HttpRequestCreator(); _isQos = true; _qoSProvider = new NoQoSProvider(); + _useCookieContainer = false; + _allowAutoRedirect = false; + _requestMessage = new HttpRequestMessage(); } @@ -30,12 +36,19 @@ { this.When(x => x.WhenIBuildARequest()) .Then(x => x.ThenTheRequestContainsTheRequestMessage()) + .Then(x => x.ThenTheRequestContainsTheIsQos()) + .Then(x => x.ThenTheRequestContainsTheQosProvider()) + .Then(x => x.ThenTheRequestContainsUseCookieContainer()) + .Then(x => x.ThenTheRequestContainsAllowAutoRedirect()) .BDDfy(); } private void WhenIBuildARequest() { - _response = _requestCreator.Build(_requestMessage, _isQos, _qoSProvider).GetAwaiter().GetResult(); + _response = _requestCreator.Build(_requestMessage, + _isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect) + .GetAwaiter() + .GetResult(); } private void ThenTheRequestContainsTheRequestMessage() @@ -52,5 +65,15 @@ { _response.Data.QosProvider.ShouldBe(_qoSProvider); } + + private void ThenTheRequestContainsUseCookieContainer() + { + _response.Data.UseCookieContainer.ShouldBe(_useCookieContainer); + } + + private void ThenTheRequestContainsAllowAutoRedirect() + { + _response.Data.AllowAutoRedirect.ShouldBe(_allowAutoRedirect); + } } } diff --git a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs index f5570f15..45aafecd 100644 --- a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs @@ -28,7 +28,7 @@ [Fact] public void should_call_scoped_data_repository_correctly() { - this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider()))) + this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false))) .And(x => x.GivenTheRequesterReturns(new HttpResponseMessage())) .And(x => x.GivenTheScopedRepoReturns()) .When(x => x.WhenICallTheMiddleware()) diff --git a/tools/packages.config b/tools/packages.config index 70a1e599..747e13e6 100644 --- a/tools/packages.config +++ b/tools/packages.config @@ -1,4 +1,4 @@ - +