using System; using System.Collections.Generic; using System.IO; using System.Net; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Ocelot.Configuration.File; using Shouldly; using TestStack.BDDfy; using Xunit; namespace Ocelot.AcceptanceTests { public class RoutingTests : IDisposable { private IWebHost _builder; private readonly Steps _steps; private string _downstreamPath; public RoutingTests() { _steps = new Steps(); } [Fact] public void should_return_response_404_when_no_configuration_at_all() { this.Given(x => _steps.GivenThereIsAConfiguration(new FileConfiguration())) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) .BDDfy(); } [Fact] public void should_return_response_200_with_forward_slash_and_placeholder_only() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/{url}", DownstreamScheme = "http", UpstreamPathTemplate = "/{url}", UpstreamHttpMethod = new List { "Get" }, DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } } } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_response_200_favouring_forward_slash_with_path_route() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/{url}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51880, } }, UpstreamPathTemplate = "/{url}", UpstreamHttpMethod = new List { "Get" }, }, new FileReRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", "/test", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/test")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_response_200_favouring_forward_slash() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/{url}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51880, } }, UpstreamPathTemplate = "/{url}", UpstreamHttpMethod = new List { "Get" }, }, new FileReRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879/", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_response_200_favouring_forward_slash_route_because_it_is_first() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51880, } }, UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, }, new FileReRoute { DownstreamPathTemplate = "/{url}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/{url}", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51880/", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_response_200_with_nothing_and_placeholder_only() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/{url}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/{url}", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_response_200_with_simple_url() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void bug() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/v1/vacancy", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51874, } }, UpstreamPathTemplate = "/vacancy/", UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, ServiceName = "botCore", LoadBalancer = "LeastConnection" }, new FileReRoute { DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51874, } }, UpstreamPathTemplate = "/vacancy/{vacancyId}", UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, ServiceName = "botCore", LoadBalancer = "LeastConnection" } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51874", "/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() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_response_200_when_host_has_trailing_slash() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_ok_when_upstream_url_ends_with_forward_slash_but_template_does_not() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/products", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/products/", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_not_found_when_upstream_url_ends_with_forward_slash_but_template_does_not() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/products", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/products", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) .BDDfy(); } [Fact] public void should_return_not_found() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/products", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, QoSOptions = new FileQoSOptions() { ExceptionsAllowedBeforeBreaking = 3, DurationOfBreak = 5, TimeoutValue = 5000 } } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/products", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) .BDDfy(); } [Fact] public void should_return_response_200_with_complex_url() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products/{productId}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Some Product")) .BDDfy(); } [Fact] public void should_return_response_200_with_complex_url_that_starts_with_placeholder() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/{variantId}/products/{productId}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/{variantId}/products/{productId}", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/23/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("23/products/1")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Some Product")) .BDDfy(); } [Fact] public void should_not_add_trailing_slash_to_downstream_url() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/products/{productId}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/products/1")) .Then(x => ThenTheDownstreamUrlPathShouldBe("/api/products/1")) .BDDfy(); } [Fact] public void should_return_response_201_with_simple_url() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, DownstreamScheme = "http", UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Post" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasContent("postContent")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) .BDDfy(); } [Fact] public void should_return_response_201_with_complex_query_string() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/newThing", UpstreamPathTemplate = "/newThing", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/newThing", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/newThing?DeviceType=IphoneApp&Browser=moonpigIphone&BrowserString=-&CountryCode=123&DeviceName=iPhone 5 (GSM+CDMA)&OperatingSystem=iPhone OS 7.1.2&BrowserVersion=3708AdHoc&ipAddress=-")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } [Fact] public void should_return_response_200_with_placeholder_for_final_url_path() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/{urlPath}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/myApp1Name/api/{urlPath}", UpstreamHttpMethod = new List { "Get" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/api/products/1", 200, "Some Product")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/myApp1Name/api/products/1")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Some Product")) .BDDfy(); } [Fact] public void should_return_response_201_with_simple_url_and_multiple_upstream_http_method() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, DownstreamScheme = "http", UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get", "Post" }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "", 201, string.Empty)) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenThePostHasContent("postContent")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) .BDDfy(); } [Fact] public void should_return_response_200_with_simple_url_and_any_upstream_http_method() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, DownstreamScheme = "http", UpstreamPathTemplate = "/", UpstreamHttpMethod = new List(), } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .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", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51879, } }, UpstreamPathTemplate = "/vacancy/", UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, ServiceName = "botCore", LoadBalancer = "LeastConnection" }, new FileReRoute { DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 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(); } [Fact] public void should_fix_145() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/api/{url}", DownstreamScheme = "http", DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 51899, } }, UpstreamPathTemplate = "/platform/{url}", UpstreamHttpMethod = new List { "Get" }, QoSOptions = new FileQoSOptions { ExceptionsAllowedBeforeBreaking = 3, DurationOfBreak = 10, TimeoutValue = 5000 } } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51899", "/api/swagger/lib/backbone-min.js", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/platform/swagger/lib/backbone-min.js")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => ThenTheDownstreamUrlPathShouldBe("/api/swagger/lib/backbone-min.js")) .BDDfy(); } [Fact] public void should_use_priority() { var configuration = new FileConfiguration { ReRoutes = new List { new FileReRoute { DownstreamPathTemplate = "/goods/{url}", DownstreamScheme = "http", UpstreamPathTemplate = "/goods/{url}", UpstreamHttpMethod = new List { "Get" }, DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 53879, } }, Priority = 0 }, new FileReRoute { DownstreamPathTemplate = "/goods/delete", DownstreamScheme = "http", UpstreamPathTemplate = "/goods/delete", UpstreamHttpMethod = new List { "Get" }, DownstreamHostAndPorts = new List { new FileHostAndPort { Host = "localhost", Port = 52879, } }, } } }; this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:52879/", "/goods/delete", 200, "Hello from Laura")) .And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenOcelotIsRunning()) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/goods/delete")) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .BDDfy(); } private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) { _builder = new WebHostBuilder() .UseUrls(baseUrl) .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .Configure(app => { app.UsePathBase(basePath); app.Run(async context => { _downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; if(_downstreamPath != basePath) { context.Response.StatusCode = statusCode; await context.Response.WriteAsync("downstream path didnt match base path"); } else { context.Response.StatusCode = statusCode; await context.Response.WriteAsync(responseBody); } }); }) .Build(); _builder.Start(); } internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath) { _downstreamPath.ShouldBe(expectedDownstreamPath); } public void Dispose() { _builder?.Dispose(); _steps.Dispose(); } } }