diff --git a/.circleci/config.yml b/.circleci/config.yml index 0f904362..e6944a97 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,4 +27,3 @@ workflows: branches: ignore: - master - - develop diff --git a/build.cake b/build.cake index 25059612..3d4626bd 100644 --- a/build.cake +++ b/build.cake @@ -1,500 +1,500 @@ -#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" -#tool "nuget:?package=GitReleaseNotes" -#addin nuget:?package=Cake.Json -#addin nuget:?package=Newtonsoft.Json -#addin nuget:?package=System.Net.Http -#tool "nuget:?package=ReportGenerator" -#tool "nuget:?package=coveralls.net&version=0.7.0" -#addin Cake.Coveralls&version=0.10.1 - -// compile -var compileConfig = Argument("configuration", "Release"); - -var slnFile = "./Ocelot.sln"; - -// build artifacts -var artifactsDir = Directory("artifacts"); - -// unit testing -var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); -var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; -var minCodeCoverage = 80d; -var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN"; -var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot"; - -// acceptance testing -var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests"); -var acceptanceTestAssemblies = @"./test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj"; - -// integration testing -var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests"); -var integrationTestAssemblies = @"./test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj"; - -// benchmark testing -var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests"); -var benchmarkTestAssemblies = @"./test/Ocelot.Benchmarks"; - -// packaging -var packagesDir = artifactsDir + Directory("Packages"); -var releaseNotesFile = packagesDir + File("releasenotes.md"); -var artifactsFile = packagesDir + File("artifacts.txt"); - -// stable releases -var tagsUrl = "https://api.github.com/repos/ThreeMammals/ocelot/releases/tags/"; -var nugetFeedStableKey = EnvironmentVariable("OCELOT_NUTGET_API_KEY"); -var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package"; -var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package"; - -// internal build variables - don't change these. -string committedVersion = "0.0.0-dev"; -GitVersion versioning = null; -int releaseId = 0; -string gitHubUsername = "TomPallister"; -string gitHubPassword = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY"); - -var target = Argument("target", "Default"); - -Information("target is " + target); -Information("Build configuration is " + compileConfig); - -Task("Default") - .IsDependentOn("Build"); - -Task("Build") - .IsDependentOn("RunTests"); - -Task("RunTests") - .IsDependentOn("RunUnitTests") - .IsDependentOn("RunAcceptanceTests") - .IsDependentOn("RunIntegrationTests"); - -Task("Release") - .IsDependentOn("Build") - .IsDependentOn("CreateArtifacts") - .IsDependentOn("PublishGitHubRelease") - .IsDependentOn("PublishToNuget"); - -Task("Compile") - .IsDependentOn("Clean") - .IsDependentOn("Version") - .Does(() => - { - var settings = new DotNetCoreBuildSettings - { - Configuration = compileConfig, - }; - - DotNetCoreBuild(slnFile, settings); - }); - -Task("Clean") - .Does(() => - { - if (DirectoryExists(artifactsDir)) - { - DeleteDirectory(artifactsDir, recursive:true); - } - CreateDirectory(artifactsDir); - }); - -Task("Version") - .Does(() => - { - versioning = GetNuGetVersionForCommit(); - var nugetVersion = versioning.NuGetVersion; - Information("SemVer version number: " + nugetVersion); - - if (IsRunningOnCircleCI()) - { - Information("Persisting version number..."); - PersistVersion(committedVersion, nugetVersion); - } - else - { - Information("We are not running on build server, so we won't persist the version number."); - } - }); - -Task("RunUnitTests") - .IsDependentOn("Compile") - .Does(() => - { - var testSettings = new DotNetCoreTestSettings - { - Configuration = compileConfig, - ResultsDirectory = artifactsForUnitTestsDir, - ArgumentCustomization = args => args - // this create the code coverage report - .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings") - }; - - EnsureDirectoryExists(artifactsForUnitTestsDir); - DotNetCoreTest(unitTestAssemblies, testSettings); - - var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml")); - Information(coverageSummaryFile); - Information(artifactsForUnitTestsDir); - // todo bring back report generator to get a friendly report - // ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir); - // https://github.com/danielpalme/ReportGenerator - - if (IsRunningOnCircleCI()) - { - var repoToken = EnvironmentVariable(coverallsRepoToken); - if (string.IsNullOrEmpty(repoToken)) - { - throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken)); - } - - Information(string.Format("Uploading test coverage to {0}", coverallsRepo)); - CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings() - { - RepoToken = repoToken - }); - } - else - { - Information("We are not running on the build server so we won't publish the coverage report to coveralls.io"); - } - - var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage"); - var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage"); - - Information("Sequence Coverage: " + sequenceCoverage); - - if(double.Parse(sequenceCoverage) < minCodeCoverage) - { - var whereToCheck = !IsRunningOnCircleCI() ? coverallsRepo : artifactsForUnitTestsDir; - throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck)); - }; - }); - -Task("RunAcceptanceTests") - .IsDependentOn("Compile") - .Does(() => - { - var settings = new DotNetCoreTestSettings - { - Configuration = compileConfig, - ArgumentCustomization = args => args - .Append("--no-restore") - .Append("--no-build") - }; - - EnsureDirectoryExists(artifactsForAcceptanceTestsDir); - DotNetCoreTest(acceptanceTestAssemblies, settings); - }); - -Task("RunIntegrationTests") - .IsDependentOn("Compile") - .Does(() => - { - var settings = new DotNetCoreTestSettings - { - Configuration = compileConfig, - ArgumentCustomization = args => args - .Append("--no-restore") - .Append("--no-build") - }; - - EnsureDirectoryExists(artifactsForIntegrationTestsDir); - DotNetCoreTest(integrationTestAssemblies, settings); - }); - -Task("CreateArtifacts") - .IsDependentOn("Compile") - .Does(() => - { - EnsureDirectoryExists(packagesDir); - - CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir); - - // todo fix this for docker build - //GenerateReleaseNotes(releaseNotesFile); - - var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg"); - - foreach(var projectFile in projectFiles) - { - System.IO.File.AppendAllLines(artifactsFile, new[]{ - projectFile.GetFilename().FullPath, - // todo fix this for docker build - //"releaseNotes:releasenotes.md" - }); - } - - var artifacts = System.IO.File - .ReadAllLines(artifactsFile) - .Distinct(); - - foreach(var artifact in artifacts) - { - var codePackage = packagesDir + File(artifact); - - Information("Created package " + codePackage); - } - }); - -Task("PublishGitHubRelease") - .IsDependentOn("CreateArtifacts") - .Does(() => - { - if (IsRunningOnCircleCI()) - { - var path = packagesDir.ToString() + @"/**/*"; - - CreateGitHubRelease(); - - foreach (var file in GetFiles(path)) - { - UploadFileToGitHubRelease(file); - } - - CompleteGitHubRelease(); - } - }); - -Task("EnsureStableReleaseRequirements") - .Does(() => - { - Information("Check if stable release..."); - - if (!IsRunningOnCircleCI()) - { - throw new Exception("Stable release should happen via circleci"); - } - - Information("Release is stable..."); - }); - -Task("DownloadGitHubReleaseArtifacts") - .Does(() => - { - - try - { - EnsureDirectoryExists(packagesDir); - - var releaseUrl = tagsUrl + versioning.NuGetVersion; - - var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)) - .Value("assets_url"); - - var assets = GetResource(assets_url); - - foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets)) - { - var file = packagesDir + File(asset.Value("name")); - DownloadFile(asset.Value("browser_download_url"), file); - } - } - catch(Exception exception) - { - Information("There was an exception " + exception); - throw; - } - }); - -Task("PublishToNuget") - .IsDependentOn("DownloadGitHubReleaseArtifacts") - .Does(() => - { - if (IsRunningOnCircleCI()) - { - PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl); - } - }); - -RunTarget(target); - -/// Gets nuique nuget version for this commit -private GitVersion GetNuGetVersionForCommit() -{ - GitVersion(new GitVersionSettings{ - UpdateAssemblyInfo = false, - OutputType = GitVersionOutput.BuildServer - }); - - return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json }); -} - -/// Updates project version in all of our projects -private void PersistVersion(string committedVersion, string newVersion) -{ - Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, newVersion)); - - var projectFiles = GetFiles("./**/*.csproj"); - - foreach(var projectFile in projectFiles) - { - var file = projectFile.ToString(); - - Information(string.Format("Updating {0}...", file)); - - var updatedProjectFile = System.IO.File.ReadAllText(file) - .Replace(committedVersion, newVersion); - - System.IO.File.WriteAllText(file, updatedProjectFile); - } -} - -/// generates release notes based on issues closed in GitHub since the last release -private void GenerateReleaseNotes(ConvertableFilePath file) -{ - if(!IsRunningOnWindows()) - { - Warning("We are not running on Windows so we cannot generate release notes."); - return; - } - - Information("Generating release notes at " + file); - - var releaseNotesExitCode = StartProcess( - @"tools/GitReleaseNotes/tools/gitreleasenotes.exe", - new ProcessSettings { Arguments = ". /o " + file }); - - if (string.IsNullOrEmpty(System.IO.File.ReadAllText(file))) - { - System.IO.File.WriteAllText(file, "No issues closed since last release"); - } - - if (releaseNotesExitCode != 0) - { - throw new Exception("Failed to generate release notes"); - } -} - -/// Publishes code and symbols packages to nuget feed, based on contents of artifacts file -private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl) -{ - Information("PublishPackages"); - var artifacts = System.IO.File - .ReadAllLines(artifactsFile) - .Distinct(); - - foreach(var artifact in artifacts) - { - var codePackage = packagesDir + File(artifact); - - Information("Pushing package " + codePackage); - - Information("Calling NuGetPush"); - - NuGetPush( - codePackage, - new NuGetPushSettings { - ApiKey = feedApiKey, - Source = codeFeedUrl - }); - } -} - -private void CreateGitHubRelease() -{ - var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"todo: notes coming\", \"draft\": true, \"prerelease\": true }}"; - var content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json"); - - using(var client = new System.Net.Http.HttpClient()) - { - client.DefaultRequestHeaders.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue( - "Basic", Convert.ToBase64String( - System.Text.ASCIIEncoding.ASCII.GetBytes( - $"{gitHubUsername}:{gitHubPassword}"))); - - client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); - - var result = client.PostAsync("https://api.github.com/repos/ThreeMammals/Ocelot/releases", content).Result; - if(result.StatusCode != System.Net.HttpStatusCode.Created) - { - throw new Exception("CreateGitHubRelease result.StatusCode = " + result.StatusCode); - } - var returnValue = result.Content.ReadAsStringAsync().Result; - dynamic test = Newtonsoft.Json.JsonConvert.DeserializeObject(returnValue); - releaseId = test.id; - } -} - -private void UploadFileToGitHubRelease(FilePath file) -{ - var data = System.IO.File.ReadAllBytes(file.FullPath); - var content = new System.Net.Http.ByteArrayContent(data); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); - - using(var client = new System.Net.Http.HttpClient()) - { - client.DefaultRequestHeaders.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue( - "Basic", Convert.ToBase64String( - System.Text.ASCIIEncoding.ASCII.GetBytes( - $"{gitHubUsername}:{gitHubPassword}"))); - - client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); - - var result = client.PostAsync($"https://uploads.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}/assets?name={file.GetFilename()}", content).Result; - if(result.StatusCode != System.Net.HttpStatusCode.Created) - { - throw new Exception("UploadFileToGitHubRelease result.StatusCode = " + result.StatusCode); - } - } -} - -private void CompleteGitHubRelease() -{ - var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"todo: notes coming\", \"draft\": false, \"prerelease\": false }}"; - var request = new System.Net.Http.HttpRequestMessage(new System.Net.Http.HttpMethod("Patch"), $"https://api.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}"); - request.Content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json"); - - using(var client = new System.Net.Http.HttpClient()) - { - client.DefaultRequestHeaders.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue( - "Basic", Convert.ToBase64String( - System.Text.ASCIIEncoding.ASCII.GetBytes( - $"{gitHubUsername}:{gitHubPassword}"))); - - client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); - - var result = client.SendAsync(request).Result; - if(result.StatusCode != System.Net.HttpStatusCode.OK) - { - throw new Exception("CompleteGitHubRelease result.StatusCode = " + result.StatusCode); - } - } -} - - -/// gets the resource from the specified url -private string GetResource(string 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"; - - 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 IsRunningOnCircleCI() -{ - return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI")); +#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" +#tool "nuget:?package=GitReleaseNotes" +#addin nuget:?package=Cake.Json +#addin nuget:?package=Newtonsoft.Json +#addin nuget:?package=System.Net.Http +#tool "nuget:?package=ReportGenerator" +#tool "nuget:?package=coveralls.net&version=0.7.0" +#addin Cake.Coveralls&version=0.10.1 + +// compile +var compileConfig = Argument("configuration", "Release"); + +var slnFile = "./Ocelot.sln"; + +// build artifacts +var artifactsDir = Directory("artifacts"); + +// unit testing +var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); +var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; +var minCodeCoverage = 80d; +var coverallsRepoToken = "OCELOT_COVERALLS_TOKEN"; +var coverallsRepo = "https://coveralls.io/github/ThreeMammals/Ocelot"; + +// acceptance testing +var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests"); +var acceptanceTestAssemblies = @"./test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj"; + +// integration testing +var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests"); +var integrationTestAssemblies = @"./test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj"; + +// benchmark testing +var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests"); +var benchmarkTestAssemblies = @"./test/Ocelot.Benchmarks"; + +// packaging +var packagesDir = artifactsDir + Directory("Packages"); +var releaseNotesFile = packagesDir + File("releasenotes.md"); +var artifactsFile = packagesDir + File("artifacts.txt"); + +// stable releases +var tagsUrl = "https://api.github.com/repos/ThreeMammals/ocelot/releases/tags/"; +var nugetFeedStableKey = EnvironmentVariable("OCELOT_NUTGET_API_KEY"); +var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package"; +var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package"; + +// internal build variables - don't change these. +string committedVersion = "0.0.0-dev"; +GitVersion versioning = null; +int releaseId = 0; +string gitHubUsername = "TomPallister"; +string gitHubPassword = Environment.GetEnvironmentVariable("OCELOT_GITHUB_API_KEY"); + +var target = Argument("target", "Default"); + +Information("target is " + target); +Information("Build configuration is " + compileConfig); + +Task("Default") + .IsDependentOn("Build"); + +Task("Build") + .IsDependentOn("RunTests"); + +Task("RunTests") + .IsDependentOn("RunUnitTests") + .IsDependentOn("RunAcceptanceTests") + .IsDependentOn("RunIntegrationTests"); + +Task("Release") + .IsDependentOn("Build") + .IsDependentOn("CreateArtifacts") + .IsDependentOn("PublishGitHubRelease") + .IsDependentOn("PublishToNuget"); + +Task("Compile") + .IsDependentOn("Clean") + .IsDependentOn("Version") + .Does(() => + { + var settings = new DotNetCoreBuildSettings + { + Configuration = compileConfig, + }; + + DotNetCoreBuild(slnFile, settings); + }); + +Task("Clean") + .Does(() => + { + if (DirectoryExists(artifactsDir)) + { + DeleteDirectory(artifactsDir, recursive:true); + } + CreateDirectory(artifactsDir); + }); + +Task("Version") + .Does(() => + { + versioning = GetNuGetVersionForCommit(); + var nugetVersion = versioning.NuGetVersion; + Information("SemVer version number: " + nugetVersion); + + if (IsRunningOnCircleCI()) + { + Information("Persisting version number..."); + PersistVersion(committedVersion, nugetVersion); + } + else + { + Information("We are not running on build server, so we won't persist the version number."); + } + }); + +Task("RunUnitTests") + .IsDependentOn("Compile") + .Does(() => + { + var testSettings = new DotNetCoreTestSettings + { + Configuration = compileConfig, + ResultsDirectory = artifactsForUnitTestsDir, + ArgumentCustomization = args => args + // this create the code coverage report + .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings") + }; + + EnsureDirectoryExists(artifactsForUnitTestsDir); + DotNetCoreTest(unitTestAssemblies, testSettings); + + var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml")); + Information(coverageSummaryFile); + Information(artifactsForUnitTestsDir); + // todo bring back report generator to get a friendly report + // ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir); + // https://github.com/danielpalme/ReportGenerator + + if (IsRunningOnCircleCI()) + { + var repoToken = EnvironmentVariable(coverallsRepoToken); + if (string.IsNullOrEmpty(repoToken)) + { + throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken)); + } + + Information(string.Format("Uploading test coverage to {0}", coverallsRepo)); + CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings() + { + RepoToken = repoToken + }); + } + else + { + Information("We are not running on the build server so we won't publish the coverage report to coveralls.io"); + } + + var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage"); + var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage"); + + Information("Sequence Coverage: " + sequenceCoverage); + + if(double.Parse(sequenceCoverage) < minCodeCoverage) + { + var whereToCheck = !IsRunningOnCircleCI() ? coverallsRepo : artifactsForUnitTestsDir; + throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck)); + }; + }); + +Task("RunAcceptanceTests") + .IsDependentOn("Compile") + .Does(() => + { + var settings = new DotNetCoreTestSettings + { + Configuration = compileConfig, + ArgumentCustomization = args => args + .Append("--no-restore") + .Append("--no-build") + }; + + EnsureDirectoryExists(artifactsForAcceptanceTestsDir); + DotNetCoreTest(acceptanceTestAssemblies, settings); + }); + +Task("RunIntegrationTests") + .IsDependentOn("Compile") + .Does(() => + { + var settings = new DotNetCoreTestSettings + { + Configuration = compileConfig, + ArgumentCustomization = args => args + .Append("--no-restore") + .Append("--no-build") + }; + + EnsureDirectoryExists(artifactsForIntegrationTestsDir); + DotNetCoreTest(integrationTestAssemblies, settings); + }); + +Task("CreateArtifacts") + .IsDependentOn("Compile") + .Does(() => + { + EnsureDirectoryExists(packagesDir); + + CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir); + + // todo fix this for docker build + //GenerateReleaseNotes(releaseNotesFile); + + var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg"); + + foreach(var projectFile in projectFiles) + { + System.IO.File.AppendAllLines(artifactsFile, new[]{ + projectFile.GetFilename().FullPath, + // todo fix this for docker build + //"releaseNotes:releasenotes.md" + }); + } + + var artifacts = System.IO.File + .ReadAllLines(artifactsFile) + .Distinct(); + + foreach(var artifact in artifacts) + { + var codePackage = packagesDir + File(artifact); + + Information("Created package " + codePackage); + } + }); + +Task("PublishGitHubRelease") + .IsDependentOn("CreateArtifacts") + .Does(() => + { + if (IsRunningOnCircleCI()) + { + var path = packagesDir.ToString() + @"/**/*"; + + CreateGitHubRelease(); + + foreach (var file in GetFiles(path)) + { + UploadFileToGitHubRelease(file); + } + + CompleteGitHubRelease(); + } + }); + +Task("EnsureStableReleaseRequirements") + .Does(() => + { + Information("Check if stable release..."); + + if (!IsRunningOnCircleCI()) + { + throw new Exception("Stable release should happen via circleci"); + } + + Information("Release is stable..."); + }); + +Task("DownloadGitHubReleaseArtifacts") + .Does(() => + { + + try + { + EnsureDirectoryExists(packagesDir); + + var releaseUrl = tagsUrl + versioning.NuGetVersion; + + var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)) + .Value("assets_url"); + + var assets = GetResource(assets_url); + + foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets)) + { + var file = packagesDir + File(asset.Value("name")); + DownloadFile(asset.Value("browser_download_url"), file); + } + } + catch(Exception exception) + { + Information("There was an exception " + exception); + throw; + } + }); + +Task("PublishToNuget") + .IsDependentOn("DownloadGitHubReleaseArtifacts") + .Does(() => + { + if (IsRunningOnCircleCI()) + { + PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl); + } + }); + +RunTarget(target); + +/// Gets nuique nuget version for this commit +private GitVersion GetNuGetVersionForCommit() +{ + GitVersion(new GitVersionSettings{ + UpdateAssemblyInfo = false, + OutputType = GitVersionOutput.BuildServer + }); + + return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json }); +} + +/// Updates project version in all of our projects +private void PersistVersion(string committedVersion, string newVersion) +{ + Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, newVersion)); + + var projectFiles = GetFiles("./**/*.csproj"); + + foreach(var projectFile in projectFiles) + { + var file = projectFile.ToString(); + + Information(string.Format("Updating {0}...", file)); + + var updatedProjectFile = System.IO.File.ReadAllText(file) + .Replace(committedVersion, newVersion); + + System.IO.File.WriteAllText(file, updatedProjectFile); + } +} + +/// generates release notes based on issues closed in GitHub since the last release +private void GenerateReleaseNotes(ConvertableFilePath file) +{ + if(!IsRunningOnWindows()) + { + Warning("We are not running on Windows so we cannot generate release notes."); + return; + } + + Information("Generating release notes at " + file); + + var releaseNotesExitCode = StartProcess( + @"tools/GitReleaseNotes/tools/gitreleasenotes.exe", + new ProcessSettings { Arguments = ". /o " + file }); + + if (string.IsNullOrEmpty(System.IO.File.ReadAllText(file))) + { + System.IO.File.WriteAllText(file, "No issues closed since last release"); + } + + if (releaseNotesExitCode != 0) + { + throw new Exception("Failed to generate release notes"); + } +} + +/// Publishes code and symbols packages to nuget feed, based on contents of artifacts file +private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl) +{ + Information("PublishPackages"); + var artifacts = System.IO.File + .ReadAllLines(artifactsFile) + .Distinct(); + + foreach(var artifact in artifacts) + { + var codePackage = packagesDir + File(artifact); + + Information("Pushing package " + codePackage); + + Information("Calling NuGetPush"); + + NuGetPush( + codePackage, + new NuGetPushSettings { + ApiKey = feedApiKey, + Source = codeFeedUrl + }); + } +} + +private void CreateGitHubRelease() +{ + var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"todo: notes coming\", \"draft\": true, \"prerelease\": true }}"; + var content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json"); + + using(var client = new System.Net.Http.HttpClient()) + { + client.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue( + "Basic", Convert.ToBase64String( + System.Text.ASCIIEncoding.ASCII.GetBytes( + $"{gitHubUsername}:{gitHubPassword}"))); + + client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); + + var result = client.PostAsync("https://api.github.com/repos/ThreeMammals/Ocelot/releases", content).Result; + if(result.StatusCode != System.Net.HttpStatusCode.Created) + { + throw new Exception("CreateGitHubRelease result.StatusCode = " + result.StatusCode); + } + var returnValue = result.Content.ReadAsStringAsync().Result; + dynamic test = Newtonsoft.Json.JsonConvert.DeserializeObject(returnValue); + releaseId = test.id; + } +} + +private void UploadFileToGitHubRelease(FilePath file) +{ + var data = System.IO.File.ReadAllBytes(file.FullPath); + var content = new System.Net.Http.ByteArrayContent(data); + content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); + + using(var client = new System.Net.Http.HttpClient()) + { + client.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue( + "Basic", Convert.ToBase64String( + System.Text.ASCIIEncoding.ASCII.GetBytes( + $"{gitHubUsername}:{gitHubPassword}"))); + + client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); + + var result = client.PostAsync($"https://uploads.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}/assets?name={file.GetFilename()}", content).Result; + if(result.StatusCode != System.Net.HttpStatusCode.Created) + { + throw new Exception("UploadFileToGitHubRelease result.StatusCode = " + result.StatusCode); + } + } +} + +private void CompleteGitHubRelease() +{ + var json = $"{{ \"tag_name\": \"{versioning.NuGetVersion}\", \"target_commitish\": \"master\", \"name\": \"{versioning.NuGetVersion}\", \"body\": \"todo: notes coming\", \"draft\": false, \"prerelease\": false }}"; + var request = new System.Net.Http.HttpRequestMessage(new System.Net.Http.HttpMethod("Patch"), $"https://api.github.com/repos/ThreeMammals/Ocelot/releases/{releaseId}"); + request.Content = new System.Net.Http.StringContent(json, System.Text.Encoding.UTF8, "application/json"); + + using(var client = new System.Net.Http.HttpClient()) + { + client.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue( + "Basic", Convert.ToBase64String( + System.Text.ASCIIEncoding.ASCII.GetBytes( + $"{gitHubUsername}:{gitHubPassword}"))); + + client.DefaultRequestHeaders.Add("User-Agent", "Ocelot Release"); + + var result = client.SendAsync(request).Result; + if(result.StatusCode != System.Net.HttpStatusCode.OK) + { + throw new Exception("CompleteGitHubRelease result.StatusCode = " + result.StatusCode); + } + } +} + + +/// gets the resource from the specified url +private string GetResource(string 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"; + + 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 IsRunningOnCircleCI() +{ + return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI")); } \ No newline at end of file diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst index 38ecf808..7983af39 100644 --- a/docs/features/configuration.rst +++ b/docs/features/configuration.rst @@ -62,7 +62,8 @@ Here is an example ReRoute configuration, You don't need to set all of these thi "HttpHandlerOptions": { "AllowAutoRedirect": true, "UseCookieContainer": true, - "UseTracing": true + "UseTracing": true, + "MaxConnectionsPerServer": 100 }, "DangerousAcceptAnyServerCertificateValidator": false } @@ -222,3 +223,8 @@ If you want to ignore SSL warnings / errors set the following in your ReRoute co "DangerousAcceptAnyServerCertificateValidator": true I don't recommend doing this, I suggest creating your own certificate and then getting it trusted by your local / remote machine if you can. + +MaxConnectionsPerServer +^^^^^^^^^^^^^^^^^^^^^^^ + +This controls how many connections the internal HttpClient will open. This can be set at ReRoute or global level. \ No newline at end of file diff --git a/docs/features/servicediscovery.rst b/docs/features/servicediscovery.rst index f36f9f78..f23d2a4a 100644 --- a/docs/features/servicediscovery.rst +++ b/docs/features/servicediscovery.rst @@ -155,6 +155,8 @@ Eureka. One of the services polls Eureka every 30 seconds (default) and gets the When Ocelot asks for a given service it is retrieved from memory so performance is not a big problem. Please note that this code is provided by the Pivotal.Discovery.Client NuGet package so big thanks to them for all the hard work. +Ocelot will use the scheme (http/https) set in Eureka if these values are not provided in ocelot.json + Dynamic Routing ^^^^^^^^^^^^^^^ diff --git a/src/Ocelot.Provider.Eureka/Eureka.cs b/src/Ocelot.Provider.Eureka/Eureka.cs index 8316beac..8a064549 100644 --- a/src/Ocelot.Provider.Eureka/Eureka.cs +++ b/src/Ocelot.Provider.Eureka/Eureka.cs @@ -26,7 +26,7 @@ if (instances != null && instances.Any()) { - services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port), "", "", new List()))); + services.AddRange(instances.Select(i => new Service(i.ServiceId, new ServiceHostAndPort(i.Host, i.Port, i.Uri.Scheme), "", "", new List()))); } return Task.FromResult(services); diff --git a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs index 08e79ad1..4e38100b 100644 --- a/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/HttpHandlerOptionsCreator.cs @@ -18,8 +18,11 @@ { var useTracing = _tracer != null && options.UseTracing; + //be sure that maxConnectionPerServer is in correct range of values + int maxConnectionPerServer = (options.MaxConnectionsPerServer > 0) ? maxConnectionPerServer = options.MaxConnectionsPerServer : maxConnectionPerServer = int.MaxValue; + return new HttpHandlerOptions(options.AllowAutoRedirect, - options.UseCookieContainer, useTracing, options.UseProxy); + options.UseCookieContainer, useTracing, options.UseProxy, maxConnectionPerServer); } } } diff --git a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs index d765af58..b83be428 100644 --- a/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs +++ b/src/Ocelot/Configuration/File/FileHttpHandlerOptions.cs @@ -6,7 +6,8 @@ { AllowAutoRedirect = false; UseCookieContainer = false; - UseProxy = true; + UseProxy = true; + MaxConnectionsPerServer = int.MaxValue; } public bool AllowAutoRedirect { get; set; } @@ -15,6 +16,8 @@ public bool UseTracing { get; set; } - public bool UseProxy { get; set; } + public bool UseProxy { get; set; } + + public int MaxConnectionsPerServer { get; set; } } } diff --git a/src/Ocelot/Configuration/HttpHandlerOptions.cs b/src/Ocelot/Configuration/HttpHandlerOptions.cs index b551287f..e76cc117 100644 --- a/src/Ocelot/Configuration/HttpHandlerOptions.cs +++ b/src/Ocelot/Configuration/HttpHandlerOptions.cs @@ -6,32 +6,44 @@ /// public class HttpHandlerOptions { - public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy) - { - AllowAutoRedirect = allowAutoRedirect; - UseCookieContainer = useCookieContainer; - UseTracing = useTracing; - UseProxy = useProxy; + public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing, bool useProxy, int maxConnectionsPerServer) + { + AllowAutoRedirect = allowAutoRedirect; + UseCookieContainer = useCookieContainer; + UseTracing = useTracing; + UseProxy = useProxy; + MaxConnectionsPerServer = maxConnectionsPerServer; } + /// /// Specify if auto redirect is enabled - /// - public bool AllowAutoRedirect { get; private set; } - + /// + /// AllowAutoRedirect + public bool AllowAutoRedirect { get; private set; } + /// /// Specify is handler has to use a cookie container - /// + /// + /// UseCookieContainer public bool UseCookieContainer { get; private set; } /// /// Specify is handler has to use a opentracing - /// + /// + /// UseTracing public bool UseTracing { get; private set; } /// /// Specify if handler has to use a proxy - /// - public bool UseProxy { get; private set; } + /// + /// UseProxy + public bool UseProxy { get; private set; } + + /// + /// Specify the maximum of concurrent connection to a network endpoint + /// + /// MaxConnectionsPerServer + public int MaxConnectionsPerServer { get; private set; } } } diff --git a/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs b/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs index bf8aa042..8e66094b 100644 --- a/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs +++ b/src/Ocelot/Configuration/HttpHandlerOptionsBuilder.cs @@ -6,6 +6,7 @@ private bool _useCookieContainer; private bool _useTracing; private bool _useProxy; + private int _maxConnectionPerServer; public HttpHandlerOptionsBuilder WithAllowAutoRedirect(bool input) { @@ -30,10 +31,16 @@ _useProxy = useProxy; return this; } + public HttpHandlerOptionsBuilder WithUseMaxConnectionPerServer(int maxConnectionPerServer) + { + _maxConnectionPerServer = maxConnectionPerServer; + return this; + } + public HttpHandlerOptions Build() { - return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing, _useProxy); + return new HttpHandlerOptions(_allowAutoRedirect, _useCookieContainer, _useTracing, _useProxy, _maxConnectionPerServer); } } } diff --git a/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs b/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs index cca50d0b..9a2b67e6 100644 --- a/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs +++ b/src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs @@ -105,7 +105,8 @@ namespace Ocelot.Configuration.Repository public void Dispose() { - _timer.Dispose(); + _timer?.Dispose(); + _timer = null; } } } diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs index add4fe0b..e2f7f24d 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; + using System.Text.RegularExpressions; using System.Threading.Tasks; public class FileConfigurationFluentValidator : AbstractValidator, IConfigurationValidator @@ -35,6 +36,10 @@ .Must((config, reRoute) => HaveServiceDiscoveryProviderRegistered(reRoute, config.GlobalConfiguration.ServiceDiscoveryProvider)) .WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?"); + RuleForEach(configuration => configuration.ReRoutes) + .Must((config, reRoute) => IsPlaceholderNotDuplicatedIn(reRoute.UpstreamPathTemplate)) + .WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicated placeholder"); + RuleFor(configuration => configuration.GlobalConfiguration.ServiceDiscoveryProvider) .Must(HaveServiceDiscoveryProviderRegistered) .WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?"); @@ -109,6 +114,14 @@ return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count; } + private bool IsPlaceholderNotDuplicatedIn(string upstreamPathTemplate) + { + Regex regExPlaceholder = new Regex("{[^}]+}"); + var matches = regExPlaceholder.Matches(upstreamPathTemplate); + var upstreamPathPlaceholders = matches.Select(m => m.Value); + return upstreamPathPlaceholders.Count() == upstreamPathPlaceholders.Distinct().Count(); + } + private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute, List reRoutes) { diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs index d2e53b95..7d19d523 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -37,7 +37,10 @@ namespace Ocelot.DownstreamUrlCreator.Middleware return; } - context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme; + if (!string.IsNullOrEmpty(context.DownstreamReRoute.DownstreamScheme)) + { + context.DownstreamRequest.Scheme = context.DownstreamReRoute.DownstreamScheme; + } if (ServiceFabricRequest(context)) { diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs index 22456430..646af5fe 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs @@ -45,6 +45,11 @@ namespace Ocelot.LoadBalancer.Middleware context.DownstreamRequest.Port = hostAndPort.Data.DownstreamPort; } + if (!string.IsNullOrEmpty(hostAndPort.Data.Scheme)) + { + context.DownstreamRequest.Scheme = hostAndPort.Data.Scheme; + } + try { await _next.Invoke(context); diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index 64df44b1..e7d45857 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -90,7 +90,9 @@ namespace Ocelot.Requester { AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect, UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer, - UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy + UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy, + MaxConnectionsPerServer = context.DownstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer + }; } @@ -101,6 +103,7 @@ namespace Ocelot.Requester AllowAutoRedirect = context.DownstreamReRoute.HttpHandlerOptions.AllowAutoRedirect, UseCookies = context.DownstreamReRoute.HttpHandlerOptions.UseCookieContainer, UseProxy = context.DownstreamReRoute.HttpHandlerOptions.UseProxy, + MaxConnectionsPerServer = context.DownstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer, CookieContainer = new CookieContainer() }; } diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs index 1863a206..7a580872 100644 --- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs +++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs @@ -34,7 +34,7 @@ namespace Ocelot.ServiceDiscovery foreach (var downstreamAddress in reRoute.DownstreamAddresses) { - var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port), string.Empty, string.Empty, new string[0]); + var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port, reRoute.DownstreamScheme), string.Empty, string.Empty, new string[0]); services.Add(service); } diff --git a/src/Ocelot/Values/ServiceHostAndPort.cs b/src/Ocelot/Values/ServiceHostAndPort.cs index 4a8e3748..fff7edba 100644 --- a/src/Ocelot/Values/ServiceHostAndPort.cs +++ b/src/Ocelot/Values/ServiceHostAndPort.cs @@ -8,8 +8,13 @@ DownstreamPort = downstreamPort; } + public ServiceHostAndPort(string downstreamHost, int downstreamPort, string scheme) + : this(downstreamHost, downstreamPort) => Scheme = scheme; + public string DownstreamHost { get; } - public int DownstreamPort { get; } + public int DownstreamPort { get; } + + public string Scheme { get; } } } diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs index 3008fc6b..bfc0c98c 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs @@ -41,14 +41,14 @@ namespace Ocelot.UnitTests.Configuration _internalConfigCreator = new Mock(); _internalConfigCreator.Setup(x => x.Create(It.IsAny())).ReturnsAsync(new OkResponse(_internalConfig)); _poller = new FileConfigurationPoller(_factory.Object, _repo.Object, _config.Object, _internalConfigRepo.Object, _internalConfigCreator.Object); - _poller.StartAsync(new CancellationToken()); } [Fact] public void should_start() { - this.Given(x => ThenTheSetterIsCalled(_fileConfig, 1)) - .BDDfy(); + this.Given(x => GivenPollerHasStarted()) + .Given(x => ThenTheSetterIsCalled(_fileConfig, 1)) + .BDDfy(); } [Fact] @@ -71,7 +71,8 @@ namespace Ocelot.UnitTests.Configuration } }; - this.Given(x => WhenTheConfigIsChanged(newConfig, 0)) + this.Given(x => GivenPollerHasStarted()) + .Given(x => WhenTheConfigIsChanged(newConfig, 0)) .Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1)) .BDDfy(); } @@ -96,7 +97,8 @@ namespace Ocelot.UnitTests.Configuration } }; - this.Given(x => WhenTheConfigIsChanged(newConfig, 10)) + this.Given(x => GivenPollerHasStarted()) + .Given(x => WhenTheConfigIsChanged(newConfig, 10)) .Then(x => ThenTheSetterIsCalled(newConfig, 1)) .BDDfy(); } @@ -121,11 +123,24 @@ namespace Ocelot.UnitTests.Configuration } }; - this.Given(x => WhenProviderErrors()) + this.Given(x => GivenPollerHasStarted()) + .Given(x => WhenProviderErrors()) .Then(x => ThenTheSetterIsCalled(newConfig, 0)) .BDDfy(); } + [Fact] + public void should_dispose_cleanly_without_starting() + { + this.When(x => WhenPollerIsDisposed()) + .BDDfy(); + } + + private void GivenPollerHasStarted() + { + _poller.StartAsync(CancellationToken.None); + } + private void WhenProviderErrors() { _repo @@ -141,6 +156,11 @@ namespace Ocelot.UnitTests.Configuration .ReturnsAsync(new OkResponse(newConfig)); } + private void WhenPollerIsDisposed() + { + _poller.Dispose(); + } + private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times) { var result = WaitFor(4000).Until(() => diff --git a/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs index 4915dc5d..c4f6eb84 100644 --- a/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs @@ -41,7 +41,7 @@ namespace Ocelot.UnitTests.Configuration } }; - var expectedOptions = new HttpHandlerOptions(false, false, false, true); + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); this.Given(x => GivenTheFollowing(fileReRoute)) .When(x => WhenICreateHttpHandlerOptions()) @@ -60,7 +60,7 @@ namespace Ocelot.UnitTests.Configuration } }; - var expectedOptions = new HttpHandlerOptions(false, false, true, true); + var expectedOptions = new HttpHandlerOptions(false, false, true, true, int.MaxValue); this.Given(x => GivenTheFollowing(fileReRoute)) .And(x => GivenARealTracer()) @@ -73,7 +73,7 @@ namespace Ocelot.UnitTests.Configuration public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default() { var fileReRoute = new FileReRoute(); - var expectedOptions = new HttpHandlerOptions(false, false, false, true); + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); this.Given(x => GivenTheFollowing(fileReRoute)) .When(x => WhenICreateHttpHandlerOptions()) @@ -94,7 +94,7 @@ namespace Ocelot.UnitTests.Configuration } }; - var expectedOptions = new HttpHandlerOptions(false, false, false, true); + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); this.Given(x => GivenTheFollowing(fileReRoute)) .When(x => WhenICreateHttpHandlerOptions()) @@ -110,7 +110,7 @@ namespace Ocelot.UnitTests.Configuration HttpHandlerOptions = new FileHttpHandlerOptions() }; - var expectedOptions = new HttpHandlerOptions(false, false, false, true); + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); this.Given(x => GivenTheFollowing(fileReRoute)) .When(x => WhenICreateHttpHandlerOptions()) @@ -129,7 +129,64 @@ namespace Ocelot.UnitTests.Configuration } }; - var expectedOptions = new HttpHandlerOptions(false, false, false, false); + var expectedOptions = new HttpHandlerOptions(false, false, false, false, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileReRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_with_specified_MaxConnectionsPerServer() + { + var fileReRoute = new FileReRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + MaxConnectionsPerServer = 10 + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, 10); + + this.Given(x => GivenTheFollowing(fileReRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_fixing_specified_MaxConnectionsPerServer_range() + { + var fileReRoute = new FileReRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + MaxConnectionsPerServer = -1 + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileReRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_fixing_specified_MaxConnectionsPerServer_range_when_zero() + { + var fileReRoute = new FileReRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + MaxConnectionsPerServer = 0 + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); this.Given(x => GivenTheFollowing(fileReRoute)) .When(x => WhenICreateHttpHandlerOptions()) @@ -154,6 +211,7 @@ namespace Ocelot.UnitTests.Configuration _httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer); _httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing); _httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy); + _httpHandlerOptions.MaxConnectionsPerServer.ShouldBe(expected.MaxConnectionsPerServer); } private void GivenARealTracer() diff --git a/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs b/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs index 943001fc..7055c1ad 100644 --- a/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs @@ -1341,6 +1341,32 @@ .BDDfy(); } + [Fact] + public void configuration_is_invalid_when_placeholder_is_used_twice_in_upstream_path_template() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/bar/{everything}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort() { Host = "a.b.cd" }, + }, + UpstreamPathTemplate = "/foo/bar/{everything}/{everything}", + UpstreamHttpMethod = new List { "Get" }, + }, + }, + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "reRoute /foo/bar/{everything}/{everything} has duplicated placeholder")) + .BDDfy(); + } + private void GivenAConfiguration(FileConfiguration fileConfiguration) { _fileConfiguration = fileConfiguration; diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index ffd52b08..7ed6a02a 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -350,6 +350,36 @@ .BDDfy(); } + [Fact] + public void should_not_replace_by_empty_scheme() + { + var downstreamReRoute = new DownstreamReRouteBuilder() + .WithDownstreamScheme("") + .WithServiceName("Ocelot/OcelotApp") + .WithUseServiceDiscovery(true) + .Build(); + + var downstreamRoute = new DownstreamRoute( + new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(downstreamReRoute) + .Build()); + + var config = new ServiceProviderConfigurationBuilder() + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheDownstreamRequestUriIs("https://localhost:19081?PartitionKind=test&PartitionKey=1")) + .And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:19081/Ocelot/OcelotApp/api/products/1?PartitionKind=test&PartitionKey=1")) + .BDDfy(); + } + private void GivenTheServiceProviderConfigIs(ServiceProviderConfiguration config) { var configuration = new InternalConfiguration(null, null, config, null, null, null, null, null); diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index 8d31572a..2705b3b7 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -1,3 +1,5 @@ +using System; +using System.Linq.Expressions; using Ocelot.Middleware; namespace Ocelot.UnitTests.LoadBalancer @@ -108,6 +110,26 @@ namespace Ocelot.UnitTests.LoadBalancer .BDDfy(); } + [Fact] + public void should_set_scheme() + { + var downstreamRoute = new DownstreamReRouteBuilder() + .WithUpstreamHttpMethod(new List { "Get" }) + .Build(); + + var serviceProviderConfig = new ServiceProviderConfigurationBuilder() + .Build(); + + this.Given(x => x.GivenTheDownStreamUrlIs("http://my.url/abc?q=123")) + .And(x => GivenTheConfigurationIs(serviceProviderConfig)) + .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute, new List())) + .And(x => x.GivenTheLoadBalancerHouseReturns()) + .And(x => x.GivenTheLoadBalancerReturnsOk()) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenAnHostAndPortIsSetOnPipeline()) + .BDDfy(); + } + private void WhenICallTheMiddleware() { _middleware = new LoadBalancingMiddleware(_next, _loggerFactory.Object, _loadBalancerHouse.Object); @@ -135,6 +157,13 @@ namespace Ocelot.UnitTests.LoadBalancer .ReturnsAsync(_getHostAndPortError); } + private void GivenTheLoadBalancerReturnsOk() + { + _loadBalancer + .Setup(x => x.Lease(It.IsAny())) + .ReturnsAsync(new OkResponse(new ServiceHostAndPort("abc", 123, "https"))); + } + private void GivenTheLoadBalancerReturns() { _hostAndPort = new ServiceHostAndPort("127.0.0.1", 80); @@ -186,6 +215,13 @@ namespace Ocelot.UnitTests.LoadBalancer _downstreamContext.Errors.ShouldBe(_getHostAndPortError.Errors); } + private void ThenAnHostAndPortIsSetOnPipeline() + { + _downstreamContext.DownstreamRequest.Host.ShouldBeEquivalentTo("abc"); + _downstreamContext.DownstreamRequest.Port.ShouldBeEquivalentTo(123); + _downstreamContext.DownstreamRequest.Scheme.ShouldBeEquivalentTo("https"); + } + private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri) { _downstreamContext.DownstreamRequest.ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri); diff --git a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs index 2b2340ef..050b2da8 100644 --- a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs @@ -52,7 +52,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) .WithDelegatingHandlers(new List { "FakeDelegatingHandler", @@ -88,7 +88,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) .WithDelegatingHandlers(new List { "FakeDelegatingHandlerTwo", @@ -125,7 +125,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) .WithDelegatingHandlers(new List { "FakeDelegatingHandlerTwo", @@ -161,7 +161,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) .WithDelegatingHandlers(new List { "FakeDelegatingHandler", @@ -195,7 +195,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) .WithLoadBalancerKey("") .Build(); @@ -221,7 +221,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)) .WithDelegatingHandlers(new List { "FakeDelegatingHandler", @@ -249,7 +249,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build(); + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) @@ -269,7 +269,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build(); + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheServiceProviderReturnsNothing()) @@ -289,7 +289,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build(); + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) @@ -309,7 +309,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true)).WithLoadBalancerKey("").Build(); + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); this.Given(x => GivenTheFollowingRequest(reRoute)) .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) @@ -331,7 +331,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) .WithLoadBalancerKey("") .Build(); @@ -361,7 +361,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) .WithLoadBalancerKey("") .Build(); diff --git a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs index 34bf11c1..c8dbd4a2 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs @@ -52,7 +52,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -73,7 +73,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -99,7 +99,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -126,7 +126,7 @@ namespace Ocelot.UnitTests.Requester var reRouteA = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -134,7 +134,7 @@ namespace Ocelot.UnitTests.Requester var reRouteB = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -161,7 +161,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -184,7 +184,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -216,7 +216,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -252,7 +252,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) .WithQosOptions(new QoSOptionsBuilder().Build()) diff --git a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs index 53106d93..43bfa5d5 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs @@ -57,7 +57,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(upstreamTemplate) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -86,7 +86,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(upstreamTemplate) .WithQosOptions(new QoSOptionsBuilder().Build()) @@ -114,7 +114,7 @@ namespace Ocelot.UnitTests.Requester var reRoute = new DownstreamReRouteBuilder() .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true)) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(upstreamTemplate) .WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build())