From 3439be89271bb37b170429d69ca74978c941432e Mon Sep 17 00:00:00 2001 From: Tom Pallister Date: Sat, 23 May 2020 20:50:05 +0100 Subject: [PATCH] Rename all ReRoute to Route to move closer to YARP +semver: breaking --- docs/features/authentication.rst | 26 +- docs/features/authorisation.rst | 2 +- docs/features/caching.rst | 2 +- docs/features/claimstransformation.rst | 2 +- docs/features/configuration.rst | 16 +- docs/features/delegatinghandlers.rst | 6 +- docs/features/headerstransformation.rst | 10 +- docs/features/kubernetes.rst | 8 +- docs/features/loadbalancer.rst | 14 +- docs/features/methodtransformation.rst | 4 +- docs/features/qualityofservice.rst | 4 +- docs/features/ratelimiting.rst | 2 +- docs/features/requestaggregation.rst | 30 +- docs/features/requestid.rst | 12 +- docs/features/routing.rst | 32 +- docs/features/servicediscovery.rst | 18 +- docs/features/servicefabric.rst | 4 +- docs/features/tracing.rst | 82 +- docs/features/websockets.rst | 8 +- docs/introduction/gettingstarted.rst | 6 +- helpers.txt | 11 - .../Issue645.postman_collection.json | 298 +- samples/AdministrationApi/README.md | 188 +- samples/AdministrationApi/ocelot.json | 34 +- samples/OcelotBasic/ocelot.json | 2 +- samples/OcelotEureka/ApiGateway/ocelot.json | 44 +- samples/OcelotGraphQL/README.md | 140 +- samples/OcelotGraphQL/ocelot.json | 36 +- samples/OcelotKube/ApiGateway/ocelot.json | 40 +- .../OcelotApplicationApiGateway/ocelot.json | 42 +- .../ConsulProviderFactory.cs | 54 +- .../EurekaProviderFactory.cs | 42 +- .../KubernetesProviderFactory.cs | 74 +- .../OcelotBuilderExtensions.cs | 76 +- src/Ocelot.Provider.Polly/PollyQoSProvider.cs | 118 +- .../Middleware/AuthenticationMiddleware.cs | 10 +- .../Middleware/AuthorisationMiddleware.cs | 28 +- src/Ocelot/Cache/IRegionCreator.cs | 2 +- .../Cache/Middleware/OutputCacheMiddleware.cs | 244 +- src/Ocelot/Cache/RegionCreator.cs | 14 +- .../Middleware/ClaimsToClaimsMiddleware.cs | 10 +- .../Builder/DownstreamReRouteBuilder.cs | 610 ++-- .../Builder/QoSOptionsBuilder.cs | 82 +- .../Configuration/Builder/ReRouteBuilder.cs | 78 - .../Configuration/Builder/RouteBuilder.cs | 78 + ...tionsBuilder.cs => RouteOptionsBuilder.cs} | 92 +- .../Creator/AggregatesCreator.cs | 110 +- .../Creator/AuthenticationOptionsCreator.cs | 8 +- .../Creator/ConfigurationCreator.cs | 122 +- .../Creator/DownstreamAddressesCreator.cs | 6 +- .../Configuration/Creator/DynamicsCreator.cs | 94 +- .../FileInternalConfigurationCreator.cs | 114 +- .../Creator/HeaderFindAndReplaceCreator.cs | 24 +- .../Creator/IAggregatesCreator.cs | 20 +- .../Creator/IAuthenticationOptionsCreator.cs | 4 +- .../Creator/IConfigurationCreator.cs | 20 +- .../Creator/IDownstreamAddressesCreator.cs | 4 +- .../Configuration/Creator/IDynamicsCreator.cs | 20 +- .../Creator/IHeaderFindAndReplaceCreator.cs | 4 +- .../Creator/IHttpHandlerOptionsCreator.cs | 4 +- .../Creator/IReRouteKeyCreator.cs | 9 - .../Creator/IReRouteOptionsCreator.cs | 9 - .../Configuration/Creator/IReRoutesCreator.cs | 20 +- .../Creator/IRequestIdKeyCreator.cs | 2 +- .../Configuration/Creator/IRouteKeyCreator.cs | 9 + .../Creator/IRouteOptionsCreator.cs | 9 + .../IUpstreamTemplatePatternCreator.cs | 4 +- .../Creator/ReRouteKeyCreator.cs | 31 - .../Creator/ReRouteOptionsCreator.cs | 47 - .../Creator/RequestIdKeyCreator.cs | 16 +- .../Configuration/Creator/RouteKeyCreator.cs | 31 + .../Creator/RouteOptionsCreator.cs | 47 + .../{ReRoutesCreator.cs => RoutesCreator.cs} | 334 +- .../Creator/UpstreamTemplatePatternCreator.cs | 186 +- ...ownstreamReRoute.cs => DownstreamRoute.cs} | 234 +- ...RouteConfig.cs => AggregateRouteConfig.cs} | 18 +- ...regateReRoute.cs => FileAggregateRoute.cs} | 42 +- .../Configuration/File/FileConfiguration.cs | 46 +- ...eDynamicReRoute.cs => FileDynamicRoute.cs} | 18 +- .../File/{FileReRoute.cs => FileRoute.cs} | 120 +- src/Ocelot/Configuration/File/IReRoute.cs | 18 +- .../Configuration/IInternalConfiguration.cs | 54 +- .../Configuration/InternalConfiguration.cs | 84 +- .../Configuration/{ReRoute.cs => Route.cs} | 14 +- .../{ReRouteOptions.cs => RouteOptions.cs} | 40 +- .../FileConfigurationFluentValidator.cs | 368 +- .../FileQoSOptionsFluentValidator.cs | 60 +- .../Validator/HostAndPortValidator.cs | 30 +- ...ntValidator.cs => RouteFluentValidator.cs} | 254 +- .../ConfigurationBuilderExtensions.cs | 170 +- .../DependencyInjection/IOcelotBuilder.cs | 4 +- .../DependencyInjection/OcelotBuilder.cs | 26 +- .../ClaimsToDownstreamPathMiddleware.cs | 20 +- ...treamRoute.cs => DownstreamRouteHolder.cs} | 10 +- .../Finder/DownstreamRouteCreator.cs | 279 +- .../Finder/DownstreamRouteFinder.cs | 124 +- .../Finder/DownstreamRouteProviderFactory.cs | 88 +- .../Finder/IDownstreamRouteProvider.cs | 20 +- .../UnableToFindDownstreamRouteError.cs | 2 +- .../DownstreamRouteFinderMiddleware.cs | 2 +- .../DownstreamUrlCreatorMiddleware.cs | 56 +- .../Middleware/ClaimsToHeadersMiddleware.cs | 16 +- .../HttpHeadersTransformationMiddleware.cs | 10 +- .../CookieStickySessionsCreator.cs | 42 +- .../DelegateInvokingLoadBalancerCreator.cs | 68 +- .../LoadBalancers/ILoadBalancerCreator.cs | 24 +- .../LoadBalancers/ILoadBalancerFactory.cs | 20 +- .../LoadBalancers/ILoadBalancerHouse.cs | 20 +- .../LoadBalancers/LeastConnectionCreator.cs | 32 +- .../LoadBalancers/LoadBalancerFactory.cs | 96 +- .../LoadBalancers/LoadBalancerHouse.cs | 140 +- .../LoadBalancers/NoLoadBalancerCreator.cs | 32 +- .../LoadBalancers/RoundRobinCreator.cs | 32 +- .../Middleware/LoadBalancingMiddleware.cs | 16 +- src/Ocelot/Middleware/HttpItemsExtensions.cs | 16 +- .../Multiplexer/IDefinedAggregatorProvider.cs | 2 +- src/Ocelot/Multiplexer/IResponseAggregator.cs | 2 +- .../Multiplexer/IResponseAggregatorFactory.cs | 2 +- .../InMemoryResponseAggregatorFactory.cs | 4 +- .../Multiplexer/MultiplexingMiddleware.cs | 42 +- ...ServiceLocatorDefinedAggregatorProvider.cs | 8 +- .../SimpleJsonResponseAggregator.cs | 6 +- .../UserDefinedResponseAggregator.cs | 4 +- .../ClaimsToQueryStringMiddleware.cs | 16 +- .../Middleware/ClientRateLimitMiddleware.cs | 16 +- src/Ocelot/Request/Mapper/IRequestMapper.cs | 26 +- src/Ocelot/Request/Mapper/RequestMapper.cs | 238 +- .../DownstreamRequestInitialiserMiddleware.cs | 4 +- ...IdMiddleware.cs => RequestIdMiddleware.cs} | 12 +- .../RequestIdMiddlewareExtensions.cs | 2 +- .../DelegatingHandlerHandlerFactory.cs | 212 +- src/Ocelot/Requester/HttpClientBuilder.cs | 46 +- .../Requester/HttpClientHttpRequester.cs | 4 +- .../IDelegatingHandlerHandlerFactory.cs | 26 +- src/Ocelot/Requester/IHttpClientBuilder.cs | 2 +- src/Ocelot/Requester/IHttpClientCache.cs | 4 +- src/Ocelot/Requester/MemoryHttpClientCache.cs | 54 +- .../Middleware/HttpRequesterMiddleware.cs | 2 +- src/Ocelot/Requester/QoS/IQosFactory.cs | 22 +- src/Ocelot/Requester/QoS/QosFactory.cs | 66 +- .../Requester/QosDelegatingHandlerDelegate.cs | 16 +- .../Security/IPSecurity/IPSecurityPolicy.cs | 4 +- src/Ocelot/Security/ISecurityPolicy.cs | 2 +- .../Security/Middleware/SecurityMiddleware.cs | 4 +- .../IServiceDiscoveryProviderFactory.cs | 22 +- .../ServiceDiscoveryFinderDelegate.cs | 16 +- .../ServiceDiscoveryProviderFactory.cs | 132 +- test/Ocelot.AcceptanceTests/AggregateTests.cs | 100 +- .../AuthenticationTests.cs | 20 +- .../AuthorisationTests.cs | 20 +- .../ButterflyTracingTests.cs | 532 +-- test/Ocelot.AcceptanceTests/CachingTests.cs | 16 +- .../CannotStartOcelotTests.cs | 446 +-- .../CaseSensitiveRoutingTests.cs | 68 +- .../ClaimsToDownstreamPathTests.cs | 410 +-- .../ClaimsToHeadersForwardingTests.cs | 4 +- .../ClaimsToQueryStringForwardingTests.cs | 8 +- .../ClientRateLimitTests.cs | 444 +-- .../ConfigurationInConsulTests.cs | 376 +- .../ConsulConfigurationInConsulTests.cs | 974 ++--- .../ConsulWebSocketTests.cs | 704 ++-- test/Ocelot.AcceptanceTests/ContentTests.cs | 364 +- .../CustomMiddlewareTests.cs | 28 +- .../EurekaServiceDiscoveryTests.cs | 566 +-- test/Ocelot.AcceptanceTests/GzipTests.cs | 4 +- test/Ocelot.AcceptanceTests/HeaderTests.cs | 36 +- .../HttpClientCachingTests.cs | 324 +- .../HttpDelegatingHandlersTests.cs | 592 +-- test/Ocelot.AcceptanceTests/HttpTests.cs | 486 +-- .../LoadBalancerTests.cs | 14 +- test/Ocelot.AcceptanceTests/MethodTests.cs | 328 +- test/Ocelot.AcceptanceTests/PollyQoSTests.cs | 38 +- .../ReasonPhraseTests.cs | 152 +- test/Ocelot.AcceptanceTests/RequestIdTests.cs | 16 +- .../ResponseCodeTests.cs | 4 +- .../ReturnsErrorTests.cs | 12 +- test/Ocelot.AcceptanceTests/RoutingTests.cs | 2210 ++++++------ .../RoutingWithQueryStringTests.cs | 542 +-- .../ServiceDiscoveryTests.cs | 1194 +++---- .../ServiceFabricTests.cs | 416 +-- test/Ocelot.AcceptanceTests/SslTests.cs | 8 +- test/Ocelot.AcceptanceTests/StartupTests.cs | 204 +- test/Ocelot.AcceptanceTests/Steps.cs | 2 +- .../StickySessionsTests.cs | 586 +-- .../TwoDownstreamServicesTests.cs | 314 +- .../UpstreamHostTests.cs | 558 +-- test/Ocelot.AcceptanceTests/WebSocketTests.cs | 668 ++-- .../AllTheThingsBenchmarks.cs | 312 +- .../Ocelot.Benchmarks/DictionaryBenchmarks.cs | 153 +- ...wnstreamRouteFinderMiddlewareBenchmarks.cs | 2 +- .../AdministrationTests.cs | 1824 +++++----- .../CacheManagerTests.cs | 440 +-- test/Ocelot.IntegrationTests/HeaderTests.cs | 4 +- test/Ocelot.IntegrationTests/RaftTests.cs | 1026 +++--- .../ThreadSafeHeadersTests.cs | 4 +- test/Ocelot.IntegrationTests/ocelot.json | 114 +- test/Ocelot.ManualTest/ocelot.json | 690 ++-- .../AuthenticationMiddlewareTests.cs | 230 +- .../AuthorisationMiddlewareTests.cs | 6 +- .../Cache/OutputCacheMiddlewareTests.cs | 272 +- .../Cache/RegionCreatorTests.cs | 24 +- .../OutputCacheMiddlewareRealCacheTests.cs | 14 +- .../Claims/ClaimsToClaimsMiddlewareTests.cs | 172 +- .../Configuration/AggregatesCreatorTests.cs | 328 +- .../AuthenticationOptionsCreatorTests.cs | 120 +- .../ConfigurationCreatorTests.cs | 252 +- .../DiskFileConfigurationRepositoryTests.cs | 604 ++-- .../DownstreamAddressesCreatorTests.cs | 234 +- .../Configuration/DynamicsCreatorTests.cs | 296 +- .../FileConfigurationPollerTests.cs | 410 +-- .../FileConfigurationSetterTests.cs | 2 +- .../FileInternalConfigurationCreatorTests.cs | 254 +- .../HeaderFindAndReplaceCreatorTests.cs | 68 +- .../HttpHandlerOptionsCreatorTests.cs | 478 +-- .../InMemoryConfigurationRepositoryTests.cs | 12 +- .../Configuration/QoSOptionsCreatorTests.cs | 20 +- .../RateLimitOptionsCreatorTests.cs | 212 +- .../Configuration/RequestIdKeyCreatorTests.cs | 24 +- ...reatorTests.cs => RouteKeyCreatorTests.cs} | 168 +- ...orTests.cs => RouteOptionsCreatorTests.cs} | 160 +- ...sCreatorTests.cs => RoutesCreatorTests.cs} | 566 +-- .../SecurityOptionsCreatorTests.cs | 136 +- .../UpstreamTemplatePatternCreatorTests.cs | 520 +-- .../FileConfigurationFluentValidatorTests.cs | 3168 ++++++++--------- .../FileQoSOptionsFluentValidatorTests.cs | 206 +- .../Validation/HostAndPortValidatorTests.cs | 154 +- ...rTests.cs => RouteFluentValidatorTests.cs} | 866 ++--- .../ConsulFileConfigurationRepositoryTests.cs | 520 +-- .../Consul/ProviderFactoryTests.cs | 114 +- .../ConfigurationBuilderExtensionsTests.cs | 668 ++-- .../DependencyInjection/OcelotBuilderTests.cs | 794 ++--- .../ClaimsToDownstreamPathMiddlewareTests.cs | 196 +- .../DownstreamRouteCreatorTests.cs | 612 ++-- .../DownstreamRouteFinderMiddlewareTests.cs | 14 +- .../DownstreamRouteFinderTests.cs | 1542 ++++---- .../DownstreamRouteProviderFactoryTests.cs | 328 +- .../DownstreamUrlCreatorMiddlewareTests.cs | 860 ++--- ...eamUrlPathTemplateVariableReplacerTests.cs | 410 +-- .../Eureka/EurekaProviderFactoryTests.cs | 74 +- .../Headers/ClaimsToHeadersMiddlewareTests.cs | 14 +- ...ttpHeadersTransformationMiddlewareTests.cs | 16 +- .../CookieStickySessionsCreatorTests.cs | 148 +- ...elegateInvokingLoadBalancerCreatorTests.cs | 30 +- .../LeastConnectionCreatorTests.cs | 148 +- .../LoadBalancer/LoadBalancerFactoryTests.cs | 42 +- .../LoadBalancer/LoadBalancerHouseTests.cs | 64 +- .../LoadBalancerMiddlewareTests.cs | 16 +- .../NoLoadBalancerCreatorTests.cs | 146 +- .../LoadBalancer/RoundRobinCreatorTests.cs | 146 +- .../DefinedAggregatorProviderTests.cs | 20 +- .../MultiplexingMiddlewareTests.cs | 118 +- .../ResponseAggregatorFactoryTests.cs | 16 +- .../SimpleJsonResponseAggregatorTests.cs | 72 +- .../UserDefinedResponseAggregatorTests.cs | 22 +- .../Polly/OcelotBuilderExtensionsTests.cs | 90 +- .../Polly/PollyQoSProviderTests.cs | 54 +- .../ClaimsToQueryStringMiddlewareTests.cs | 190 +- .../ClientRateLimitMiddlewareTests.cs | 26 +- ...streamRequestInitialiserMiddlewareTests.cs | 10 +- .../Request/Mapper/RequestMapperTests.cs | 968 ++--- ...reTests.cs => RequestIdMiddlewareTests.cs} | 44 +- ...atingHandlerHandlerProviderFactoryTests.cs | 1024 +++--- .../Requester/HttpClientBuilderTests.cs | 888 ++--- .../Requester/HttpClientHttpRequesterTest.cs | 44 +- .../Requester/HttpRequesterMiddlewareTests.cs | 10 +- .../Requester/QoSFactoryTests.cs | 110 +- .../Security/IPSecurityPolicyTests.cs | 25 +- .../Security/SecurityMiddlewareTests.cs | 22 +- .../ServiceDiscoveryProviderFactoryTests.cs | 374 +- 269 files changed, 23587 insertions(+), 23601 deletions(-) delete mode 100644 helpers.txt delete mode 100644 src/Ocelot/Configuration/Builder/ReRouteBuilder.cs create mode 100644 src/Ocelot/Configuration/Builder/RouteBuilder.cs rename src/Ocelot/Configuration/Builder/{ReRouteOptionsBuilder.cs => RouteOptionsBuilder.cs} (54%) delete mode 100644 src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs delete mode 100644 src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs create mode 100644 src/Ocelot/Configuration/Creator/IRouteKeyCreator.cs create mode 100644 src/Ocelot/Configuration/Creator/IRouteOptionsCreator.cs delete mode 100644 src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs delete mode 100644 src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs create mode 100644 src/Ocelot/Configuration/Creator/RouteKeyCreator.cs create mode 100644 src/Ocelot/Configuration/Creator/RouteOptionsCreator.cs rename src/Ocelot/Configuration/Creator/{ReRoutesCreator.cs => RoutesCreator.cs} (58%) rename src/Ocelot/Configuration/{DownstreamReRoute.cs => DownstreamRoute.cs} (95%) rename src/Ocelot/Configuration/File/{AggregateReRouteConfig.cs => AggregateRouteConfig.cs} (61%) rename src/Ocelot/Configuration/File/{FileAggregateReRoute.cs => FileAggregateRoute.cs} (61%) rename src/Ocelot/Configuration/File/{FileDynamicReRoute.cs => FileDynamicRoute.cs} (83%) rename src/Ocelot/Configuration/File/{FileReRoute.cs => FileRoute.cs} (92%) rename src/Ocelot/Configuration/{ReRoute.cs => Route.cs} (65%) rename src/Ocelot/Configuration/{ReRouteOptions.cs => RouteOptions.cs} (75%) rename src/Ocelot/Configuration/Validator/{ReRouteFluentValidator.cs => RouteFluentValidator.cs} (71%) rename src/Ocelot/DownstreamRouteFinder/{DownstreamRoute.cs => DownstreamRouteHolder.cs} (60%) rename src/Ocelot/RequestId/Middleware/{ReRouteRequestIdMiddleware.cs => RequestIdMiddleware.cs} (85%) rename test/Ocelot.UnitTests/Configuration/{ReRouteKeyCreatorTests.cs => RouteKeyCreatorTests.cs} (58%) rename test/Ocelot.UnitTests/Configuration/{ReRouteOptionsCreatorTests.cs => RouteOptionsCreatorTests.cs} (73%) rename test/Ocelot.UnitTests/Configuration/{ReRoutesCreatorTests.cs => RoutesCreatorTests.cs} (52%) rename test/Ocelot.UnitTests/Configuration/Validation/{ReRouteFluentValidatorTests.cs => RouteFluentValidatorTests.cs} (84%) rename test/Ocelot.UnitTests/RequestId/{ReRouteRequestIdMiddlewareTests.cs => RequestIdMiddlewareTests.cs} (83%) diff --git a/docs/features/authentication.rst b/docs/features/authentication.rst index 8e0abd7b6..f935cbacd 100644 --- a/docs/features/authentication.rst +++ b/docs/features/authentication.rst @@ -1,7 +1,7 @@ Authentication ============== -In order to authenticate ReRoutes and subsequently use any of Ocelot's claims based features such as authorisation or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g. +In order to authenticate Routes and subsequently use any of Ocelot's claims based features such as authorisation or modifying the request with values from the token. Users must register authentication services in their Startup.cs as usual but they provide a scheme (authentication provider key) with each registration e.g. .. code-block:: csharp @@ -16,11 +16,11 @@ In order to authenticate ReRoutes and subsequently use any of Ocelot's claims ba } -In this example TestKey is the scheme that this provider has been registered with. We then map this to a ReRoute in the configuration e.g. +In this example TestKey is the scheme that this provider has been registered with. We then map this to a Route in the configuration e.g. .. code-block:: json - "ReRoutes": [{ + "Routes": [{ "DownstreamHostAndPorts": [ { "Host": "localhost", @@ -30,7 +30,7 @@ In this example TestKey is the scheme that this provider has been registered wit "DownstreamPathTemplate": "/", "UpstreamPathTemplate": "/", "UpstreamHttpMethod": ["Post"], - "ReRouteIsCaseSensitive": false, + "RouteIsCaseSensitive": false, "DownstreamScheme": "http", "AuthenticationOptions": { "AuthenticationProviderKey": "TestKey", @@ -38,9 +38,9 @@ In this example TestKey is the scheme that this provider has been registered wit } }] -When Ocelot runs it will look at this ReRoutes AuthenticationOptions.AuthenticationProviderKey and check that there is an Authentication provider registered with the given key. If there isn't then Ocelot will not start up, if there is then the ReRoute will use that provider when it executes. +When Ocelot runs it will look at this Routes AuthenticationOptions.AuthenticationProviderKey and check that there is an Authentication provider registered with the given key. If there isn't then Ocelot will not start up, if there is then the Route will use that provider when it executes. -If a ReRoute is authenticated Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware. If the request fails authentication Ocelot returns a http status code 401. +If a Route is authenticated Ocelot will invoke whatever scheme is associated with it while executing the authentication middleware. If the request fails authentication Ocelot returns a http status code 401. JWT Tokens ^^^^^^^^^^ @@ -63,11 +63,11 @@ If you want to authenticate using JWT tokens maybe from a provider like Auth0 yo services.AddOcelot(); } -Then map the authentication provider key to a ReRoute in your configuration e.g. +Then map the authentication provider key to a Route in your configuration e.g. .. code-block:: json - "ReRoutes": [{ + "Routes": [{ "DownstreamHostAndPorts": [ { "Host": "localhost", @@ -77,7 +77,7 @@ Then map the authentication provider key to a ReRoute in your configuration e.g. "DownstreamPathTemplate": "/", "UpstreamPathTemplate": "/", "UpstreamHttpMethod": ["Post"], - "ReRouteIsCaseSensitive": false, + "RouteIsCaseSensitive": false, "DownstreamScheme": "http", "AuthenticationOptions": { "AuthenticationProviderKey": "TestKey", @@ -111,11 +111,11 @@ In order to use IdentityServer bearer tokens, register your IdentityServer servi services.AddOcelot(); } -Then map the authentication provider key to a ReRoute in your configuration e.g. +Then map the authentication provider key to a Route in your configuration e.g. .. code-block:: json - "ReRoutes": [{ + "Routes": [{ "DownstreamHostAndPorts": [ { "Host": "localhost", @@ -125,7 +125,7 @@ Then map the authentication provider key to a ReRoute in your configuration e.g. "DownstreamPathTemplate": "/", "UpstreamPathTemplate": "/", "UpstreamHttpMethod": ["Post"], - "ReRouteIsCaseSensitive": false, + "RouteIsCaseSensitive": false, "DownstreamScheme": "http", "AuthenticationOptions": { "AuthenticationProviderKey": "TestKey", @@ -176,4 +176,4 @@ Allowed Scopes If you add scopes to AllowedScopes Ocelot will get all the user claims (from the token) of the type scope and make sure that the user has all of the scopes in the list. -This is a way to restrict access to a ReRoute on a per scope basis. +This is a way to restrict access to a Route on a per scope basis. diff --git a/docs/features/authorisation.rst b/docs/features/authorisation.rst index e19e03c4c..ab1bc1b50 100644 --- a/docs/features/authorisation.rst +++ b/docs/features/authorisation.rst @@ -1,7 +1,7 @@ Authorisation ============= -Ocelot supports claims based authorisation which is run post authentication. This means if you have a route you want to authorise you can add the following to you ReRoute configuration. +Ocelot supports claims based authorisation which is run post authentication. This means if you have a route you want to authorise you can add the following to you Route configuration. .. code-block:: json diff --git a/docs/features/caching.rst b/docs/features/caching.rst index 79cb40513..9f3368a63 100644 --- a/docs/features/caching.rst +++ b/docs/features/caching.rst @@ -21,7 +21,7 @@ The second thing you need to do something like the following to your ConfigureSe x.WithDictionaryHandle(); }) -Finally in order to use caching on a route in your ReRoute configuration add this setting. +Finally in order to use caching on a route in your Route configuration add this setting. .. code-block:: json diff --git a/docs/features/claimstransformation.rst b/docs/features/claimstransformation.rst index 15ab05231..d58801e58 100644 --- a/docs/features/claimstransformation.rst +++ b/docs/features/claimstransformation.rst @@ -5,7 +5,7 @@ Ocelot allows the user to access claims and transform them into headers, query s After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorisation middleware is called. After the user is authorised first we call the claims to headers middleware, thenthe claims to query string parameters middleware, and Finally the claims to downstream pathmiddleware. -The syntax for performing the transforms is the same for each process. In the ReRoute configuration a json dictionary is added with a specific name either AddClaimsToRequest, AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate. +The syntax for performing the transforms is the same for each process. In the Route configuration a json dictionary is added with a specific name either AddClaimsToRequest, AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate. Note: I'm not a hotshot programmer so have no idea if this syntax is good... diff --git a/docs/features/configuration.rst b/docs/features/configuration.rst index f658d84d9..b931032ac 100644 --- a/docs/features/configuration.rst +++ b/docs/features/configuration.rst @@ -1,16 +1,16 @@ Configuration ============ -An example configuration can be found `here `_. There are two sections to the configuration. An array of ReRoutes and a GlobalConfiguration. The ReRoutes are the objects that tell Ocelot how to treat an upstream request. The Global configuration is a bit hacky and allows overrides of ReRoute specific settings. It's useful if you don't want to manage lots of ReRoute specific settings. +An example configuration can be found `here `_. There are two sections to the configuration. An array of Routes and a GlobalConfiguration. The Routes are the objects that tell Ocelot how to treat an upstream request. The Global configuration is a bit hacky and allows overrides of Route specific settings. It's useful if you don't want to manage lots of Route specific settings. .. code-block:: json { - "ReRoutes": [], + "Routes": [], "GlobalConfiguration": {} } -Here is an example ReRoute configuration, You don't need to set all of these things but this is everything that is available at the moment: +Here is an example Route configuration, You don't need to set all of these things but this is everything that is available at the moment: .. code-block:: json @@ -31,7 +31,7 @@ Here is an example ReRoute configuration, You don't need to set all of these thi "TtlSeconds": 0, "Region": "" }, - "ReRouteIsCaseSensitive": false, + "RouteIsCaseSensitive": false, "ServiceName": "", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ @@ -112,7 +112,7 @@ Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you In this scenario Ocelot will look for any files that match the pattern (?i)ocelot.([a-zA-Z0-9]*).json and then merge these together. If you want to set the GlobalConfiguration property you must have a file called ocelot.global.json. -The way Ocelot merges the files is basically load them, loop over them, add any ReRoutes, add any AggregateReRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any ReRoutes or AggregateReRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running. +The way Ocelot merges the files is basically load them, loop over them, add any Routes, add any AggregateRoutes and if the file is called ocelot.global.json add the GlobalConfiguration aswell as any Routes or AggregateRoutes. Ocelot will then save the merged configuration to a file called ocelot.json and this will be used as the source of truth while ocelot is running. At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration. This is something to be aware of when you are investigating problems. I would advise always checking what is in ocelot.json if you have any problems. @@ -195,7 +195,7 @@ If you do not set the ConfigurationKey Ocelot will use the string InternalConfig Follow Redirects / Use CookieContainer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior: +Use HttpHandlerOptions in Route configuration to set up HttpHandler behavior: 1. AllowAutoRedirect is a value that indicates whether the request should follow redirection responses. Set it true if the request should automatically follow redirection responses from the Downstream resource; otherwise false. The default value is false. @@ -204,7 +204,7 @@ Use HttpHandlerOptions in ReRoute configuration to set up HttpHandler behavior: SSL Errors ^^^^^^^^^^ -If you want to ignore SSL warnings / errors set the following in your ReRoute config. +If you want to ignore SSL warnings / errors set the following in your Route config. .. code-block:: json @@ -215,7 +215,7 @@ I don't recommend doing this, I suggest creating your own certificate and then g MaxConnectionsPerServer ^^^^^^^^^^^^^^^^^^^^^^^ -This controls how many connections the internal HttpClient will open. This can be set at ReRoute or global level. +This controls how many connections the internal HttpClient will open. This can be set at Route or global level. React to Configuration Changes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/features/delegatinghandlers.rst b/docs/features/delegatinghandlers.rst index 4fc32ede5..aaf325b78 100644 --- a/docs/features/delegatinghandlers.rst +++ b/docs/features/delegatinghandlers.rst @@ -31,8 +31,8 @@ Next you must add the handlers to Ocelot's container like below... .AddDelegatingHandler() .AddDelegatingHandler() -Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of the DelegatingHandler is to be applied to specific ReRoutes via ocelot.json (more on that later). If it is set to true -then it becomes a global handler and will be applied to all ReRoutes. +Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of the DelegatingHandler is to be applied to specific Routes via ocelot.json (more on that later). If it is set to true +then it becomes a global handler and will be applied to all Routes. e.g. @@ -43,7 +43,7 @@ As below... services.AddOcelot() .AddDelegatingHandler(true) -Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers then you must add the following json to the specific ReRoute in ocelot.json. The names in the array must match the class names of your +Finally if you want Route specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers then you must add the following json to the specific Route in ocelot.json. The names in the array must match the class names of your DelegatingHandlers for Ocelot to match them together. .. code-block:: json diff --git a/docs/features/headerstransformation.rst b/docs/features/headerstransformation.rst index 0809d8bf3..a7f9d50a0 100644 --- a/docs/features/headerstransformation.rst +++ b/docs/features/headerstransformation.rst @@ -8,7 +8,7 @@ Add to Request This feature was requestes in `GitHub #313 `_. -If you want to add a header to your upstream request please add the following to a ReRoute in your ocelot.json: +If you want to add a header to your upstream request please add the following to a Route in your ocelot.json: .. code-block:: json @@ -25,7 +25,7 @@ Add to Response This feature was requested in `GitHub #280 `_. -If you want to add a header to your downstream response please add the following to a ReRoute in ocelot.json.. +If you want to add a header to your downstream response please add the following to a Route in ocelot.json.. .. code-block:: json @@ -33,7 +33,7 @@ If you want to add a header to your downstream response please add the following "Uncle": "Bob" }, -In the example above a header with the key Uncle and value Bob would be returned by Ocelot when requesting the specific ReRoute. +In the example above a header with the key Uncle and value Bob would be returned by Ocelot when requesting the specific Route. If you want to return the Butterfly APM trace id then do something like the following.. @@ -57,7 +57,7 @@ The key is "Test" and the value is "http://www.bbc.co.uk/, http://ocelot.com/". Pre Downstream Request ^^^^^^^^^^^^^^^^^^^^^^ -Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This header will be changed before the request downstream and will be sent to the downstream server. +Add the following to a Route in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This header will be changed before the request downstream and will be sent to the downstream server. .. code-block:: json @@ -68,7 +68,7 @@ Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc Post Downstream Request ^^^^^^^^^^^^^^^^^^^^^^^ -Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This transformation will take place after Ocelot has received the response from the downstream service. +Add the following to a Route in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This transformation will take place after Ocelot has received the response from the downstream service. .. code-block:: json diff --git a/docs/features/kubernetes.rst b/docs/features/kubernetes.rst index a756577e2..abab44aef 100644 --- a/docs/features/kubernetes.rst +++ b/docs/features/kubernetes.rst @@ -30,13 +30,13 @@ You can replicate a Permissive. Using RBAC role bindings. .. code-block::bash kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --group=system:serviceaccounts -The following example shows how to set up a ReRoute that will work in kubernetes. The most important thing is the ServiceName which is made up of the kubernetes service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration. +The following example shows how to set up a Route that will work in kubernetes. The most important thing is the ServiceName which is made up of the kubernetes service name. We also need to set up the ServiceDiscoveryProvider in GlobalConfiguration. The example here shows a typical configuration. .. code-block:: json { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/api/values", "DownstreamScheme": "http", @@ -76,13 +76,13 @@ The polling interval is in milliseconds and tells Ocelot how often to call kuber Please note there are tradeoffs here. If you poll kubernetes it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling kubernetes per request. There is no way for Ocelot to work these out for you. -If your downstream service resides in a different namespace you can override the global setting at the ReRoute level by specifying a ServiceNamespace. +If your downstream service resides in a different namespace you can override the global setting at the Route level by specifying a ServiceNamespace. .. code-block:: json { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/api/values", "DownstreamScheme": "http", diff --git a/docs/features/loadbalancer.rst b/docs/features/loadbalancer.rst index 0bd273053..a4ec4ad91 100644 --- a/docs/features/loadbalancer.rst +++ b/docs/features/loadbalancer.rst @@ -1,7 +1,7 @@ Load Balancer ============= -Ocelot can load balance across available downstream services for each ReRoute. This means you can scale your downstream services and Ocelot can use them effectively. +Ocelot can load balance across available downstream services for each Route. This means you can scale your downstream services and Ocelot can use them effectively. The type of load balancer available are: @@ -18,7 +18,7 @@ You must choose in your configuration which load balancer to use. Configuration ^^^^^^^^^^^^^ -The following shows how to set up multiple downstream services for a ReRoute using ocelot.json and then select the LeastConnection load balancer. This is the simplest way to get load balancing set up. +The following shows how to set up multiple downstream services for a Route using ocelot.json and then select the LeastConnection load balancer. This is the simplest way to get load balancing set up. .. code-block:: json @@ -46,7 +46,7 @@ The following shows how to set up multiple downstream services for a ReRoute usi Service Discovery ^^^^^^^^^^^^^^^^^ -The following shows how to set up a ReRoute using service discovery then select the LeastConnection load balancer. +The following shows how to set up a Route using service discovery then select the LeastConnection load balancer. .. code-block:: json @@ -97,7 +97,7 @@ In order to set up CookieStickySessions load balancer you need to do something l The LoadBalancerOptions are Type this needs to be CookieStickySessions, Key this is the key of the cookie you wish to use for the sticky sessions, Expiry this is how long in milliseconds you want to the session to be stuck for. Remember this refreshes on every request which is meant to mimick how sessions work usually. -If you have multiple ReRoutes with the same LoadBalancerOptions then all of those ReRoutes will use the same load balancer for there subsequent requests. This means the sessions will be stuck across ReRoutes. +If you have multiple Routes with the same LoadBalancerOptions then all of those Routes will use the same load balancer for there subsequent requests. This means the sessions will be stuck across Routes. Please note that if you give more than one DownstreamHostAndPort or you are using a Service Discovery provider such as Consul and this returns more than one service then CookieStickySessions uses round robin to select the next server. This is hard coded at the moment but could be changed. @@ -172,7 +172,7 @@ Finally you need to register this class with Ocelot. I have used the most comple .. code-block:: csharp - Func loadBalancerFactoryFunc = (serviceProvider, reRoute, serviceDiscoveryProvider) => new CustomLoadBalancer(serviceDiscoveryProvider.Get); + Func loadBalancerFactoryFunc = (serviceProvider, Route, serviceDiscoveryProvider) => new CustomLoadBalancer(serviceDiscoveryProvider.Get); s.AddOcelot() .AddCustomLoadBalancer(loadBalancerFactoryFunc); @@ -198,11 +198,11 @@ There are numerous extension methods to add a custom load balancer and the inter where T : ILoadBalancer; IOcelotBuilder AddCustomLoadBalancer( - Func loadBalancerFactoryFunc) + Func loadBalancerFactoryFunc) where T : ILoadBalancer; IOcelotBuilder AddCustomLoadBalancer( - Func loadBalancerFactoryFunc) + Func loadBalancerFactoryFunc) where T : ILoadBalancer; When you enable custom load balancers Ocelot looks up your load balancer by its class name when it decides if it should do load balancing. If it finds a match it will use your load balaner to load balance. If Ocelot cannot match the load balancer type in your configuration with the name of registered load balancer class then you will receive a HTTP 500 internal server error. If your load balancer factory throw an exception when Ocelot calls it you will receive a HTTP 500 internal server error. diff --git a/docs/features/methodtransformation.rst b/docs/features/methodtransformation.rst index feecb7fa6..b1fc8cd32 100644 --- a/docs/features/methodtransformation.rst +++ b/docs/features/methodtransformation.rst @@ -3,7 +3,7 @@ HTTP Method Transformation Ocelot allows the user to change the HTTP request method that will be used when making a request to a downstream service. -This achieved by setting the following ReRoute configuration: +This achieved by setting the following Route configuration: .. code-block:: json @@ -23,6 +23,6 @@ This achieved by setting the following ReRoute configuration: ], } -The key property here is DownstreamHttpMethod which is set as POST and the ReRoute will only match on GET as set by UpstreamHttpMethod. +The key property here is DownstreamHttpMethod which is set as POST and the Route will only match on GET as set by UpstreamHttpMethod. This feature can be useful when interacting with downstream apis that only support POST and you want to present some kind of RESTful interface. \ No newline at end of file diff --git a/docs/features/qualityofservice.rst b/docs/features/qualityofservice.rst index b7dfa7f24..ddd4d6892 100644 --- a/docs/features/qualityofservice.rst +++ b/docs/features/qualityofservice.rst @@ -1,7 +1,7 @@ Quality of Service ================== -Ocelot supports one QoS capability at the current time. You can set on a per ReRoute basis if you want to use a circuit breaker when making requests to a downstream service. This uses an awesome +Ocelot supports one QoS capability at the current time. You can set on a per Route basis if you want to use a circuit breaker when making requests to a downstream service. This uses an awesome .NET library called Polly check them out `here `_. The first thing you need to do if you want to use the administration API is bring in the relevant NuGet package.. @@ -19,7 +19,7 @@ Then in your ConfigureServices method .AddPolly(); } -Then add the following section to a ReRoute configuration. +Then add the following section to a Route configuration. .. code-block:: json diff --git a/docs/features/ratelimiting.rst b/docs/features/ratelimiting.rst index ee0abb5ef..895b3e419 100644 --- a/docs/features/ratelimiting.rst +++ b/docs/features/ratelimiting.rst @@ -5,7 +5,7 @@ Thanks to `@catcherwong article `_ and further improvements were made as part of `Issue 298 `_. -In order to set this up you must do something like the following in your ocelot.json. Here we have specified two normal ReRoutes and each one has a Key property. -We then specify an Aggregate that composes the two ReRoutes using their keys in the ReRouteKeys list and says then we have the UpstreamPathTemplate which works like a normal ReRoute. -Obviously you cannot have duplicate UpstreamPathTemplates between ReRoutes and Aggregates. You can use all of Ocelot's normal ReRoute options apart from RequestIdKey (explained in gotchas below). +In order to set this up you must do something like the following in your ocelot.json. Here we have specified two normal Routes and each one has a Key property. +We then specify an Aggregate that composes the two Routes using their keys in the RouteKeys list and says then we have the UpstreamPathTemplate which works like a normal Route. +Obviously you cannot have duplicate UpstreamPathTemplates between Routes and Aggregates. You can use all of Ocelot's normal Route options apart from RequestIdKey (explained in gotchas below). Advanced register your own Aggregators ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ The ocelot.json setup is pretty much the same as the basic aggregation approach .. code-block:: json { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/", "UpstreamPathTemplate": "/laura", @@ -56,7 +56,7 @@ The ocelot.json setup is pretty much the same as the basic aggregation approach ], "Aggregates": [ { - "ReRouteKeys": [ + "RouteKeys": [ "Tom", "Laura" ], @@ -66,7 +66,7 @@ The ocelot.json setup is pretty much the same as the basic aggregation approach ] } -Here we have added an aggregator called FakeDefinedAggregator. Ocelot is going to look for this aggregator when it tries to aggregate this ReRoute. +Here we have added an aggregator called FakeDefinedAggregator. Ocelot is going to look for this aggregator when it tries to aggregate this Route. In order to make the aggregator available we must add the FakeDefinedAggregator to the OcelotBuilder like below. @@ -76,7 +76,7 @@ In order to make the aggregator available we must add the FakeDefinedAggregator .AddOcelot() .AddSingletonDefinedAggregator(); -Now when Ocelot tries to aggregate the ReRoute above it will find the FakeDefinedAggregator in the container and use it to aggregate the ReRoute. +Now when Ocelot tries to aggregate the Route above it will find the FakeDefinedAggregator in the container and use it to aggregate the Route. Because the FakeDefinedAggregator is registered in the container you can add any dependencies it needs into the container like below. .. code-block:: csharp @@ -106,7 +106,7 @@ In order to make an Aggregator you must implement this interface. Task Aggregate(List responses); } -With this feature you can pretty much do whatever you want because the HttpContext objects contain the results of all the aggregate requests. Please note if the HttpClient throws an exception when making a request to a ReRoute in the aggregate then you will not get a HttpContext for it but you would for any that succeed. If it does throw an exception this will be logged. +With this feature you can pretty much do whatever you want because the HttpContext objects contain the results of all the aggregate requests. Please note if the HttpClient throws an exception when making a request to a Route in the aggregate then you will not get a HttpContext for it but you would for any that succeed. If it does throw an exception this will be logged. Basic expecting JSON from Downstream Services ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -114,7 +114,7 @@ Basic expecting JSON from Downstream Services .. code-block:: json { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/", "UpstreamPathTemplate": "/laura", @@ -148,7 +148,7 @@ Basic expecting JSON from Downstream Services ], "Aggregates": [ { - "ReRouteKeys": [ + "RouteKeys": [ "Tom", "Laura" ], @@ -157,16 +157,16 @@ Basic expecting JSON from Downstream Services ] } -You can also set UpstreamHost and ReRouteIsCaseSensitive in the Aggregate configuration. These behave the same as any other ReRoutes. +You can also set UpstreamHost and RouteIsCaseSensitive in the Aggregate configuration. These behave the same as any other Routes. -If the ReRoute /tom returned a body of {"Age": 19} and /laura returned {"Age": 25} the the response after aggregation would be as follows. +If the Route /tom returned a body of {"Age": 19} and /laura returned {"Age": 25} the the response after aggregation would be as follows. .. code-block:: json {"Tom":{"Age": 19},"Laura":{"Age": 25}} At the moment the aggregation is very simple. Ocelot just gets the response from your downstream service and sticks it into a json dictionary -as above. With the ReRoute key being the key of the dictionary and the value the response body from your downstream service. You can see that the object is just +as above. With the Route key being the key of the dictionary and the value the response body from your downstream service. You can see that the object is just JSON without any pretty spaces etc. All headers will be lost from the downstream services response. @@ -179,7 +179,7 @@ It will not change the aggregate response into a 404 even if all the downstreams Gotcha's / Further info ----------------------- -You cannot use ReRoutes with specific RequestIdKeys as this would be crazy complicated to track. +You cannot use Routes with specific RequestIdKeys as this would be crazy complicated to track. Aggregation only supports the GET HTTP Verb. diff --git a/docs/features/requestid.rst b/docs/features/requestid.rst index a723a4b7c..519cc21aa 100644 --- a/docs/features/requestid.rst +++ b/docs/features/requestid.rst @@ -18,19 +18,19 @@ In your ocelot.json set the following in the GlobalConfiguration section. This w "RequestIdKey": "OcRequestId" } -I recommend using the GlobalConfiguration unless you really need it to be ReRoute specific. +I recommend using the GlobalConfiguration unless you really need it to be Route specific. -*ReRoute* +*Route* -If you want to override this for a specific ReRoute add the following to ocelot.json for the specific ReRoute. +If you want to override this for a specific Route add the following to ocelot.json for the specific Route. .. code-block:: json "RequestIdKey": "OcRequestId" -Once Ocelot has identified the incoming requests matching ReRoute object it will set the request id based on the ReRoute configuration. +Once Ocelot has identified the incoming requests matching Route object it will set the request id based on the Route configuration. -This can lead to a small gotcha. If you set a GlobalConfiguration it is possible to get one request id until the ReRoute is identified and then another after that because the request id key can change. This is by design and is the best solution I can think of at the moment. In this case the OcelotLogger will show the request id and previous request id in the logs. +This can lead to a small gotcha. If you set a GlobalConfiguration it is possible to get one request id until the Route is identified and then another after that because the request id key can change. This is by design and is the best solution I can think of at the moment. In this case the OcelotLogger will show the request id and previous request id in the logs. Below is an example of the logging when set at Debug level for a normal request.. @@ -41,7 +41,7 @@ Below is an example of the logging when set at Debug level for a normal request. dbug: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0] requestId: asdf, previousRequestId: no previous request id, message: upstream url path is {upstreamUrlPath}, dbug: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0] - requestId: asdf, previousRequestId: no previous request id, message: downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath}, + requestId: asdf, previousRequestId: no previous request id, message: downstream template is {downstreamRoute.Data.Route.DownstreamPath}, dbug: Ocelot.RateLimit.Middleware.ClientRateLimitMiddleware[0] requestId: asdf, previousRequestId: no previous request id, message: EndpointRateLimiting is not enabled for Ocelot.Values.PathTemplate, dbug: Ocelot.Authorisation.Middleware.AuthorisationMiddleware[0] diff --git a/docs/features/routing.rst b/docs/features/routing.rst index 50399b1b0..30e42a82b 100644 --- a/docs/features/routing.rst +++ b/docs/features/routing.rst @@ -4,16 +4,16 @@ Routing Ocelot's primary functionality is to take incoming http requests and forward them on to a downstream service. Ocelot currently only supports this in the form of another http request (in the future this could be any transport mechanism). -Ocelot's describes the routing of one request to another as a ReRoute. In order to get anything working in Ocelot you need to set up a ReRoute in the configuration. +Ocelot's describes the routing of one request to another as a Route. In order to get anything working in Ocelot you need to set up a Route in the configuration. .. code-block:: json { - "ReRoutes": [ + "Routes": [ ] } -To configure a ReRoute you need to add one to the ReRoutes json array. +To configure a Route you need to add one to the Routes json array. .. code-block:: json @@ -38,7 +38,7 @@ The UpstreamPathTemplate is the URL that Ocelot will use to identify which Downs In Ocelot you can add placeholders for variables to your Templates in the form of {something}. The placeholder variable needs to be present in both the DownstreamPathTemplate and UpstreamPathTemplate properties. When it is Ocelot will attempt to substitute the value in the UpstreamPathTemplate placeholder into the DownstreamPathTemplate for each request Ocelot processes. -You can also do a catch all type of ReRoute e.g. +You can also do a catch all type of Route e.g. .. code-block:: json @@ -60,11 +60,11 @@ This will forward any path + query string combinations to the downstream service The default ReRouting configuration is case insensitive! -In order to change this you can specify on a per ReRoute basis the following setting. +In order to change this you can specify on a per Route basis the following setting. .. code-block:: json - "ReRouteIsCaseSensitive": true + "RouteIsCaseSensitive": true This means that when Ocelot tries to match the incoming upstream url with an upstream template the evaluation will be case sensitive. @@ -91,7 +91,7 @@ If you set up your config like below, all requests will be proxied straight thro "UpstreamHttpMethod": [ "Get" ] } -The catch all has a lower priority than any other ReRoute. If you also have the ReRoute below in your config then Ocelot would match it before the catch all. +The catch all has a lower priority than any other Route. If you also have the Route below in your config then Ocelot would match it before the catch all. .. code-block:: json @@ -111,7 +111,7 @@ The catch all has a lower priority than any other ReRoute. If you also have the Upstream Host ^^^^^^^^^^^^^ -This feature allows you to have ReRoutes based on the upstream host. This works by looking at the host header the client has used and then using this as part of the information we use to identify a ReRoute. +This feature allows you to have Routes based on the upstream host. This works by looking at the host header the client has used and then using this as part of the information we use to identify a Route. In order to use this feature please add the following to your config. @@ -131,16 +131,16 @@ In order to use this feature please add the following to your config. "UpstreamHost": "somedomain.com" } -The ReRoute above will only be matched when the host header value is somedomain.com. +The Route above will only be matched when the host header value is somedomain.com. -If you do not set UpstreamHost on a ReRoute then any host header will match it. This means that if you have two ReRoutes that are the same, apart from the UpstreamHost, where one is null and the other set Ocelot will favour the one that has been set. +If you do not set UpstreamHost on a Route then any host header will match it. This means that if you have two Routes that are the same, apart from the UpstreamHost, where one is null and the other set Ocelot will favour the one that has been set. This feature was requested as part of `Issue 216 `_ . Priority ^^^^^^^^ -You can define the order you want your ReRoutes to match the Upstream HttpRequest by including a "Priority" property in ocelot.json +You can define the order you want your Routes to match the Upstream HttpRequest by including a "Priority" property in ocelot.json See `Issue 270 `_ for reference .. code-block:: json @@ -149,7 +149,7 @@ See `Issue 270 `_ for reference "Priority": 0 } -0 is the lowest priority, Ocelot will always use 0 for /{catchAll} ReRoutes and this is still hardcoded. After that you are free to set any priority you wish. +0 is the lowest priority, Ocelot will always use 0 for /{catchAll} Routes and this is still hardcoded. After that you are free to set any priority you wish. e.g. you could have @@ -169,14 +169,14 @@ and "Priority": 1 } -In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete ReRoute. Previously it would have matched /goods/{catchAll} (because this is the first ReRoute in the list!). +In the example above if you make a request into Ocelot on /goods/delete Ocelot will match /goods/delete Route. Previously it would have matched /goods/{catchAll} (because this is the first Route in the list!). Dynamic Routing ^^^^^^^^^^^^^^^ This feature was requested in `issue 340 `_. -The idea is to enable dynamic routing when using a service discovery provider so you don't have to provide the ReRoute config. See the docs :ref:`service-discovery` if +The idea is to enable dynamic routing when using a service discovery provider so you don't have to provide the Route config. See the docs :ref:`service-discovery` if this sounds interesting to you. Query Strings @@ -187,7 +187,7 @@ Ocelot allows you to specify a query string as part of the DownstreamPathTemplat .. code-block:: json { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", "UpstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates", @@ -214,7 +214,7 @@ Ocelot will also allow you to put query string parameters in the UpstreamPathTem .. code-block:: json { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/api/units/{subscriptionId}/{unitId}/updates", "UpstreamPathTemplate": "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", diff --git a/docs/features/servicediscovery.rst b/docs/features/servicediscovery.rst index 85a0dec5c..94b993cdf 100644 --- a/docs/features/servicediscovery.rst +++ b/docs/features/servicediscovery.rst @@ -4,7 +4,7 @@ Service Discovery ================= Ocelot allows you to specify a service discovery provider and will use this to find the host and port for the downstream service Ocelot is forwarding a request to. At the moment this is only supported in the -GlobalConfiguration section which means the same service discovery provider will be used for all ReRoutes you specify a ServiceName for at ReRoute level. +GlobalConfiguration section which means the same service discovery provider will be used for all Routes you specify a ServiceName for at Route level. Consul ^^^^^^ @@ -34,9 +34,9 @@ Please note the Scheme option defauls to HTTP. It was added in this `PR `_ project. The code for the Ocelot integration -can be found `here `_. - -In order to use the tracing please read the Butterfly documentation. - -In ocelot you need to do the following if you wish to trace a ReRoute. - - ``Install-Package Ocelot.Tracing.Butterfly`` - -In your ConfigureServices method - -.. code-block:: csharp - - services - .AddOcelot() - // this comes from Ocelot.Tracing.Butterfly package - .AddButterfly(option => - { - //this is the url that the butterfly collector server is running on... - option.CollectorUrl = "http://localhost:9618"; - option.Service = "Ocelot"; - }); - -Then in your ocelot.json add the following to the ReRoute you want to trace.. - -.. code-block:: json - - "HttpHandlerOptions": { - "UseTracing": true - }, - -Ocelot will now send tracing information to Butterfly when this ReRoute is called. +Tracing +======= + +This page details how to perform distributed tracing with Ocelot. At the moment we only support Butterfly but other tracers might just work without +anything Ocelot specific. + +Butterfly +^^^^^^^^^ + +Ocelot providers tracing functionality from the excellent `Butterfly `_ project. The code for the Ocelot integration +can be found `here `_. + +In order to use the tracing please read the Butterfly documentation. + +In ocelot you need to do the following if you wish to trace a Route. + + ``Install-Package Ocelot.Tracing.Butterfly`` + +In your ConfigureServices method + +.. code-block:: csharp + + services + .AddOcelot() + // this comes from Ocelot.Tracing.Butterfly package + .AddButterfly(option => + { + //this is the url that the butterfly collector server is running on... + option.CollectorUrl = "http://localhost:9618"; + option.Service = "Ocelot"; + }); + +Then in your ocelot.json add the following to the Route you want to trace.. + +.. code-block:: json + + "HttpHandlerOptions": { + "UseTracing": true + }, + +Ocelot will now send tracing information to Butterfly when this Route is called. diff --git a/docs/features/websockets.rst b/docs/features/websockets.rst index 3d63d3d46..8a1acfdb4 100644 --- a/docs/features/websockets.rst +++ b/docs/features/websockets.rst @@ -15,7 +15,7 @@ In your Configure method you need to tell your application to use WebSockets. app.UseOcelot().Wait(); }) -Then in your ocelot.json add the following to proxy a ReRoute using websockets. +Then in your ocelot.json add the following to proxy a Route using websockets. .. code-block:: json @@ -54,12 +54,12 @@ In your Configure method you need to tell your application to use SignalR. app.UseOcelot().Wait(); }) -Then in your ocelot.json add the following to proxy a ReRoute using SignalR. Note normal Ocelot routing rules apply the main thing is the scheme which is set to "ws". +Then in your ocelot.json add the following to proxy a Route using SignalR. Note normal Ocelot routing rules apply the main thing is the scheme which is set to "ws". .. code-block:: json { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/{catchAll}", "DownstreamScheme": "ws", @@ -84,7 +84,7 @@ Supported 2. Routing 3. Service Discovery -This means that you can set up your downstream services running websockets and either have multiple DownstreamHostAndPorts in your ReRoute config or hook your ReRoute into a service discovery provider and then load balance requests...Which I think is pretty cool :) +This means that you can set up your downstream services running websockets and either have multiple DownstreamHostAndPorts in your Route config or hook your Route into a service discovery provider and then load balance requests...Which I think is pretty cool :) Not Supported ^^^^^^^^^^^^^ diff --git a/docs/introduction/gettingstarted.rst b/docs/introduction/gettingstarted.rst index d861baa50..c22362017 100644 --- a/docs/introduction/gettingstarted.rst +++ b/docs/introduction/gettingstarted.rst @@ -22,7 +22,7 @@ The following is a very basic ocelot.json. It won't do anything but should get O .. code-block:: json { - "ReRoutes": [], + "Routes": [], "GlobalConfiguration": { "BaseUrl": "https://api.mybusiness.com" } @@ -33,7 +33,7 @@ If you want some example that actually does something use the following: .. code-block:: json { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/todos/{id}", "DownstreamScheme": "https", @@ -124,7 +124,7 @@ The following is a very basic ocelot.json. It won't do anything but should get O .. code-block:: json { - "ReRoutes": [], + "Routes": [], "GlobalConfiguration": {} } diff --git a/helpers.txt b/helpers.txt deleted file mode 100644 index 520e5c1f9..000000000 --- a/helpers.txt +++ /dev/null @@ -1,11 +0,0 @@ -var downstreamReRoute = RequestScopedDataRepository.Get("DownstreamReRoute"); -// todo check downstreamReRoute is ok - -var errors = RequestScopedDataRepository.Get>("Errors"); -// todo check errors is ok - -var downstreamResponse = RequestScopedDataRepository.Get("DownstreamResponse"); -// todo check downstreamResponse is ok - -var context = RequestScopedDataRepository.Get("DownstreamContext").Data; -// todo check downstreamcontext is ok \ No newline at end of file diff --git a/samples/AdministrationApi/Issue645.postman_collection.json b/samples/AdministrationApi/Issue645.postman_collection.json index 580419a0b..2cdd868ef 100644 --- a/samples/AdministrationApi/Issue645.postman_collection.json +++ b/samples/AdministrationApi/Issue645.postman_collection.json @@ -1,150 +1,150 @@ -{ - "info": { - "_postman_id": "6234b40a-e363-4c73-8577-1c9074abb951", - "name": "Issue645", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "item": [ - { - "name": "1. GET http://localhost: 55580/administration/.well-known/openid-configuration", - "request": { - "method": "GET", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{AccessToken}}" - } - ], - "body": {}, - "url": { - "raw": "http://localhost:5000/administration/.well-known/openid-configuration", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "5000", - "path": [ - "administration", - ".well-known", - "openid-configuration" - ] - } - }, - "response": [] - }, - { - "name": "3. GET http://localhost: 55580/administration/configuration", - "request": { - "method": "POST", - "header": [ - { - "key": "Authorization", - "value": "Bearer {{AccessToken}}" - }, - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\r\n \"reRoutes\": [\r\n {\r\n \"downstreamPathTemplate\": \"/{everything}\",\r\n \"upstreamPathTemplate\": \"/templates/{everything}\",\r\n \"upstreamHttpMethod\": [\r\n \"GET\"\r\n ],\r\n \"addHeadersToRequest\": {},\r\n \"upstreamHeaderTransform\": {},\r\n \"downstreamHeaderTransform\": {},\r\n \"addClaimsToRequest\": {},\r\n \"routeClaimsRequirement\": {},\r\n \"addQueriesToRequest\": {},\r\n \"requestIdKey\": null,\r\n \"fileCacheOptions\": {\r\n \"ttlSeconds\": 0,\r\n \"region\": null\r\n },\r\n \"reRouteIsCaseSensitive\": false,\r\n \"downstreamScheme\": \"http\",\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"rateLimitOptions\": {\r\n \"clientWhitelist\": [],\r\n \"enableRateLimiting\": false,\r\n \"period\": null,\r\n \"periodTimespan\": 0,\r\n \"limit\": 0\r\n },\r\n \"authenticationOptions\": {\r\n \"authenticationProviderKey\": null,\r\n \"allowedScopes\": []\r\n },\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n },\r\n \"downstreamHostAndPorts\": [\r\n {\r\n \"host\": \"localhost\",\r\n \"port\": 50689\r\n }\r\n ],\r\n \"upstreamHost\": null,\r\n \"key\": null,\r\n \"delegatingHandlers\": [],\r\n \"priority\": 1,\r\n \"timeout\": 0,\r\n \"dangerousAcceptAnyServerCertificateValidator\": false\r\n }\r\n ],\r\n \"aggregates\": [],\r\n \"globalConfiguration\": {\r\n \"requestIdKey\": \"Request-Id\",\r\n \"rateLimitOptions\": {\r\n \"clientIdHeader\": \"ClientId\",\r\n \"quotaExceededMessage\": null,\r\n \"rateLimitCounterPrefix\": \"ocelot\",\r\n \"disableRateLimitHeaders\": false,\r\n \"httpStatusCode\": 429\r\n },\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"baseUrl\": \"http://localhost:55580\",\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"downstreamScheme\": null,\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n }\r\n }\r\n}" - }, - "url": { - "raw": "http://localhost:5000/administration/configuration", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "5000", - "path": [ - "administration", - "configuration" - ] - } - }, - "response": [] - }, - { - "name": "2. POST http://localhost: 55580/administration/connect/token", - "event": [ - { - "listen": "test", - "script": { - "type": "text/javascript", - "exec": [ - "var jsonData = JSON.parse(responseBody);", - "postman.setGlobalVariable(\"AccessToken\", jsonData.access_token);", - "postman.setGlobalVariable(\"RefreshToken\", jsonData.refresh_token);" - ] - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "formdata", - "formdata": [ - { - "key": "client_id", - "value": "admin", - "type": "text" - }, - { - "key": "client_secret", - "value": "secret", - "type": "text" - }, - { - "key": "scope", - "value": "admin", - "type": "text" - }, - { - "key": "grant_type", - "value": "client_credentials", - "type": "text" - } - ] - }, - "url": { - "raw": "http://localhost:5000/administration/connect/token", - "protocol": "http", - "host": [ - "localhost" - ], - "port": "5000", - "path": [ - "administration", - "connect", - "token" - ] - } - }, - "response": [] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "id": "0f60e7b3-e4f1-4458-bbc4-fc4809e86b2d", - "type": "text/javascript", - "exec": [ - "" - ] - } - }, - { - "listen": "test", - "script": { - "id": "1279a2cf-b771-4a86-9dfa-302b240fac62", - "type": "text/javascript", - "exec": [ - "" - ] - } - } - ] +{ + "info": { + "_postman_id": "6234b40a-e363-4c73-8577-1c9074abb951", + "name": "Issue645", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "1. GET http://localhost: 55580/administration/.well-known/openid-configuration", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{AccessToken}}" + } + ], + "body": {}, + "url": { + "raw": "http://localhost:5000/administration/.well-known/openid-configuration", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "administration", + ".well-known", + "openid-configuration" + ] + } + }, + "response": [] + }, + { + "name": "3. GET http://localhost: 55580/administration/configuration", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{AccessToken}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\r\n \"routes\": [\r\n {\r\n \"downstreamPathTemplate\": \"/{everything}\",\r\n \"upstreamPathTemplate\": \"/templates/{everything}\",\r\n \"upstreamHttpMethod\": [\r\n \"GET\"\r\n ],\r\n \"addHeadersToRequest\": {},\r\n \"upstreamHeaderTransform\": {},\r\n \"downstreamHeaderTransform\": {},\r\n \"addClaimsToRequest\": {},\r\n \"routeClaimsRequirement\": {},\r\n \"addQueriesToRequest\": {},\r\n \"requestIdKey\": null,\r\n \"fileCacheOptions\": {\r\n \"ttlSeconds\": 0,\r\n \"region\": null\r\n },\r\n \"routeIsCaseSensitive\": false,\r\n \"downstreamScheme\": \"http\",\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"rateLimitOptions\": {\r\n \"clientWhitelist\": [],\r\n \"enableRateLimiting\": false,\r\n \"period\": null,\r\n \"periodTimespan\": 0,\r\n \"limit\": 0\r\n },\r\n \"authenticationOptions\": {\r\n \"authenticationProviderKey\": null,\r\n \"allowedScopes\": []\r\n },\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n },\r\n \"downstreamHostAndPorts\": [\r\n {\r\n \"host\": \"localhost\",\r\n \"port\": 50689\r\n }\r\n ],\r\n \"upstreamHost\": null,\r\n \"key\": null,\r\n \"delegatingHandlers\": [],\r\n \"priority\": 1,\r\n \"timeout\": 0,\r\n \"dangerousAcceptAnyServerCertificateValidator\": false\r\n }\r\n ],\r\n \"aggregates\": [],\r\n \"globalConfiguration\": {\r\n \"requestIdKey\": \"Request-Id\",\r\n \"rateLimitOptions\": {\r\n \"clientIdHeader\": \"ClientId\",\r\n \"quotaExceededMessage\": null,\r\n \"rateLimitCounterPrefix\": \"ocelot\",\r\n \"disableRateLimitHeaders\": false,\r\n \"httpStatusCode\": 429\r\n },\r\n \"qoSOptions\": {\r\n \"exceptionsAllowedBeforeBreaking\": 0,\r\n \"durationOfBreak\": 0,\r\n \"timeoutValue\": 0\r\n },\r\n \"baseUrl\": \"http://localhost:55580\",\r\n \"loadBalancerOptions\": {\r\n \"type\": null,\r\n \"key\": null,\r\n \"expiry\": 0\r\n },\r\n \"downstreamScheme\": null,\r\n \"httpHandlerOptions\": {\r\n \"allowAutoRedirect\": false,\r\n \"useCookieContainer\": false,\r\n \"useTracing\": false,\r\n \"useProxy\": true\r\n }\r\n }\r\n}" + }, + "url": { + "raw": "http://localhost:5000/administration/configuration", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "administration", + "configuration" + ] + } + }, + "response": [] + }, + { + "name": "2. POST http://localhost: 55580/administration/connect/token", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var jsonData = JSON.parse(responseBody);", + "postman.setGlobalVariable(\"AccessToken\", jsonData.access_token);", + "postman.setGlobalVariable(\"RefreshToken\", jsonData.refresh_token);" + ] + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "client_id", + "value": "admin", + "type": "text" + }, + { + "key": "client_secret", + "value": "secret", + "type": "text" + }, + { + "key": "scope", + "value": "admin", + "type": "text" + }, + { + "key": "grant_type", + "value": "client_credentials", + "type": "text" + } + ] + }, + "url": { + "raw": "http://localhost:5000/administration/connect/token", + "protocol": "http", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "administration", + "connect", + "token" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "0f60e7b3-e4f1-4458-bbc4-fc4809e86b2d", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "1279a2cf-b771-4a86-9dfa-302b240fac62", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] } \ No newline at end of file diff --git a/samples/AdministrationApi/README.md b/samples/AdministrationApi/README.md index 36ed2a677..42a01b2f4 100644 --- a/samples/AdministrationApi/README.md +++ b/samples/AdministrationApi/README.md @@ -1,94 +1,94 @@ -```json -{ - "reRoutes": [ - { - "downstreamPathTemplate": "/{everything}", - "upstreamPathTemplate": "/templates/{everything}", - "upstreamHttpMethod": [ - "GET" - ], - "addHeadersToRequest": {}, - "upstreamHeaderTransform": {}, - "downstreamHeaderTransform": {}, - "addClaimsToRequest": {}, - "routeClaimsRequirement": {}, - "addQueriesToRequest": {}, - "requestIdKey": null, - "fileCacheOptions": { - "ttlSeconds": 0, - "region": null - }, - "reRouteIsCaseSensitive": false, - "downstreamScheme": "http", - "qoSOptions": { - "exceptionsAllowedBeforeBreaking": 0, - "durationOfBreak": 0, - "timeoutValue": 0 - }, - "loadBalancerOptions": { - "type": null, - "key": null, - "expiry": 0 - }, - "rateLimitOptions": { - "clientWhitelist": [], - "enableRateLimiting": false, - "period": null, - "periodTimespan": 0, - "limit": 0 - }, - "authenticationOptions": { - "authenticationProviderKey": null, - "allowedScopes": [] - }, - "httpHandlerOptions": { - "allowAutoRedirect": false, - "useCookieContainer": false, - "useTracing": false, - "useProxy": true - }, - "downstreamHostAndPorts": [ - { - "host": "localhost", - "port": 50689 - } - ], - "upstreamHost": null, - "key": null, - "delegatingHandlers": [], - "priority": 1, - "timeout": 0, - "dangerousAcceptAnyServerCertificateValidator": false - } - ], - "aggregates": [], - "globalConfiguration": { - "requestIdKey": "Request-Id", - "rateLimitOptions": { - "clientIdHeader": "ClientId", - "quotaExceededMessage": null, - "rateLimitCounterPrefix": "ocelot", - "disableRateLimitHeaders": false, - "httpStatusCode": 429 - }, - "qoSOptions": { - "exceptionsAllowedBeforeBreaking": 0, - "durationOfBreak": 0, - "timeoutValue": 0 - }, - "baseUrl": "http://localhost:55580", - "loadBalancerOptions": { - "type": null, - "key": null, - "expiry": 0 - }, - "downstreamScheme": null, - "httpHandlerOptions": { - "allowAutoRedirect": false, - "useCookieContainer": false, - "useTracing": false, - "useProxy": true - } - } -} -``` +```json +{ + "routes": [ + { + "downstreamPathTemplate": "/{everything}", + "upstreamPathTemplate": "/templates/{everything}", + "upstreamHttpMethod": [ + "GET" + ], + "addHeadersToRequest": {}, + "upstreamHeaderTransform": {}, + "downstreamHeaderTransform": {}, + "addClaimsToRequest": {}, + "routeClaimsRequirement": {}, + "addQueriesToRequest": {}, + "requestIdKey": null, + "fileCacheOptions": { + "ttlSeconds": 0, + "region": null + }, + "routeIsCaseSensitive": false, + "downstreamScheme": "http", + "qoSOptions": { + "exceptionsAllowedBeforeBreaking": 0, + "durationOfBreak": 0, + "timeoutValue": 0 + }, + "loadBalancerOptions": { + "type": null, + "key": null, + "expiry": 0 + }, + "rateLimitOptions": { + "clientWhitelist": [], + "enableRateLimiting": false, + "period": null, + "periodTimespan": 0, + "limit": 0 + }, + "authenticationOptions": { + "authenticationProviderKey": null, + "allowedScopes": [] + }, + "httpHandlerOptions": { + "allowAutoRedirect": false, + "useCookieContainer": false, + "useTracing": false, + "useProxy": true + }, + "downstreamHostAndPorts": [ + { + "host": "localhost", + "port": 50689 + } + ], + "upstreamHost": null, + "key": null, + "delegatingHandlers": [], + "priority": 1, + "timeout": 0, + "dangerousAcceptAnyServerCertificateValidator": false + } + ], + "aggregates": [], + "globalConfiguration": { + "requestIdKey": "Request-Id", + "rateLimitOptions": { + "clientIdHeader": "ClientId", + "quotaExceededMessage": null, + "rateLimitCounterPrefix": "ocelot", + "disableRateLimitHeaders": false, + "httpStatusCode": 429 + }, + "qoSOptions": { + "exceptionsAllowedBeforeBreaking": 0, + "durationOfBreak": 0, + "timeoutValue": 0 + }, + "baseUrl": "http://localhost:55580", + "loadBalancerOptions": { + "type": null, + "key": null, + "expiry": 0 + }, + "downstreamScheme": null, + "httpHandlerOptions": { + "allowAutoRedirect": false, + "useCookieContainer": false, + "useTracing": false, + "useProxy": true + } + } +} +``` diff --git a/samples/AdministrationApi/ocelot.json b/samples/AdministrationApi/ocelot.json index d16dcebfc..0fa4143ff 100644 --- a/samples/AdministrationApi/ocelot.json +++ b/samples/AdministrationApi/ocelot.json @@ -1,18 +1,18 @@ -{ - "ReRoutes": [ - { - "DownstreamPathTemplate": "/service/stats/collected", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 5100 - } - ], - "UpstreamPathTemplate": "/api/stats/collected" - } - ], - "GlobalConfiguration": { - "BaseUrl": "http://localhost:5000" - } +{ + "Routes": [ + { + "DownstreamPathTemplate": "/service/stats/collected", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 5100 + } + ], + "UpstreamPathTemplate": "/api/stats/collected" + } + ], + "GlobalConfiguration": { + "BaseUrl": "http://localhost:5000" + } } \ No newline at end of file diff --git a/samples/OcelotBasic/ocelot.json b/samples/OcelotBasic/ocelot.json index 7a1759ffa..9051b76f4 100644 --- a/samples/OcelotBasic/ocelot.json +++ b/samples/OcelotBasic/ocelot.json @@ -1,5 +1,5 @@ { - "ReRoutes": [ + "Routes": [ { "DownstreamPathTemplate": "/todos/{id}", "DownstreamScheme": "https", diff --git a/samples/OcelotEureka/ApiGateway/ocelot.json b/samples/OcelotEureka/ApiGateway/ocelot.json index 963160f9b..5a69973de 100644 --- a/samples/OcelotEureka/ApiGateway/ocelot.json +++ b/samples/OcelotEureka/ApiGateway/ocelot.json @@ -1,22 +1,22 @@ -{ - "ReRoutes": [ - { - "DownstreamPathTemplate": "/api/Category", - "DownstreamScheme": "http", - "UpstreamPathTemplate": "/Category", - "ServiceName": "ncore-rat", - "UpstreamHttpMethod": [ "Get" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10000, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - } - ], - "GlobalConfiguration": { - "RequestIdKey": "OcRequestId", - "AdministrationPath": "/administration", - "ServiceDiscoveryProvider": { "Type": "Eureka" } - } -} +{ + "Routes": [ + { + "DownstreamPathTemplate": "/api/Category", + "DownstreamScheme": "http", + "UpstreamPathTemplate": "/Category", + "ServiceName": "ncore-rat", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10000, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + } + ], + "GlobalConfiguration": { + "RequestIdKey": "OcRequestId", + "AdministrationPath": "/administration", + "ServiceDiscoveryProvider": { "Type": "Eureka" } + } +} diff --git a/samples/OcelotGraphQL/README.md b/samples/OcelotGraphQL/README.md index b2101d568..ed6c16600 100644 --- a/samples/OcelotGraphQL/README.md +++ b/samples/OcelotGraphQL/README.md @@ -1,71 +1,71 @@ -# Ocelot using GraphQL example - -Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together. -I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorisation / authentication or I would -bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that. - -## Example - -If you run this project with - -$ dotnet run - -Use postman or something to make the following requests and you can see Ocelot and GraphQL in action together... - -GET http://localhost:5000/graphql?query={ hero(id: 4) { id name } } - -RESPONSE -```json - { - "data": { - "hero": { - "id": 4, - "name": "Tom Pallister" - } - } - } -``` - -POST http://localhost:5000/graphql - -BODY -```json - { hero(id: 4) { id name } } -``` - -RESPONSE -```json - { - "data": { - "hero": { - "id": 4, - "name": "Tom Pallister" - } - } - } -``` - -## Notes - -Please note this project never goes out to another service, it just gets the data for GraphQL in memory. You would need to add the details of your GraphQL server in ocelot.json e.g. - -```json -{ - "ReRoutes": [ - { - "DownstreamPathTemplate": "/graphql", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "yourgraphqlhost.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/graphql", - "DelegatingHandlers": [ - "GraphQlDelegatingHandler" - ] - } - ] - } +# Ocelot using GraphQL example + +Loads of people keep asking me if Ocelot will every support GraphQL, in my mind Ocelot and GraphQL are two different things that can work together. +I would not try and implement GraphQL in Ocelot instead I would either have Ocelot in front of GraphQL to handle things like authorisation / authentication or I would +bring in the awesome [graphql-dotnet](https://github.com/graphql-dotnet/graphql-dotnet) library and use it in a [DelegatingHandler](http://ocelot.readthedocs.io/en/latest/features/delegatinghandlers.html). This way you could have Ocelot and GraphQL without the extra hop to GraphQL. This same is an example of how to do that. + +## Example + +If you run this project with + +$ dotnet run + +Use postman or something to make the following requests and you can see Ocelot and GraphQL in action together... + +GET http://localhost:5000/graphql?query={ hero(id: 4) { id name } } + +RESPONSE +```json + { + "data": { + "hero": { + "id": 4, + "name": "Tom Pallister" + } + } + } +``` + +POST http://localhost:5000/graphql + +BODY +```json + { hero(id: 4) { id name } } +``` + +RESPONSE +```json + { + "data": { + "hero": { + "id": 4, + "name": "Tom Pallister" + } + } + } +``` + +## Notes + +Please note this project never goes out to another service, it just gets the data for GraphQL in memory. You would need to add the details of your GraphQL server in ocelot.json e.g. + +```json +{ + "Routes": [ + { + "DownstreamPathTemplate": "/graphql", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "yourgraphqlhost.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/graphql", + "DelegatingHandlers": [ + "GraphQlDelegatingHandler" + ] + } + ] + } ``` \ No newline at end of file diff --git a/samples/OcelotGraphQL/ocelot.json b/samples/OcelotGraphQL/ocelot.json index 115006f9f..c716bf258 100644 --- a/samples/OcelotGraphQL/ocelot.json +++ b/samples/OcelotGraphQL/ocelot.json @@ -1,19 +1,19 @@ -{ - "ReRoutes": [ - { - "DownstreamPathTemplate": "/", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/graphql", - "DelegatingHandlers": [ - "GraphQlDelegatingHandler" - ] - } - ] - } +{ + "Routes": [ + { + "DownstreamPathTemplate": "/", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/graphql", + "DelegatingHandlers": [ + "GraphQlDelegatingHandler" + ] + } + ] + } \ No newline at end of file diff --git a/samples/OcelotKube/ApiGateway/ocelot.json b/samples/OcelotKube/ApiGateway/ocelot.json index ec70503e9..6a28b9eec 100644 --- a/samples/OcelotKube/ApiGateway/ocelot.json +++ b/samples/OcelotKube/ApiGateway/ocelot.json @@ -1,20 +1,20 @@ -{ - "ReRoutes": [ - { - "DownstreamPathTemplate": "/api/values", - "DownstreamScheme": "http", - "UpstreamPathTemplate": "/values", - "ServiceName": "downstreamservice", - "UpstreamHttpMethod": [ "Get" ] - } - ], - "GlobalConfiguration": { - "ServiceDiscoveryProvider": { - "Host": "192.168.0.13", - "Port": 443, - "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG", - "Namespace": "dev", - "Type": "kube" - } - } -} +{ + "Routes": [ + { + "DownstreamPathTemplate": "/api/values", + "DownstreamScheme": "http", + "UpstreamPathTemplate": "/values", + "ServiceName": "downstreamservice", + "UpstreamHttpMethod": [ "Get" ] + } + ], + "GlobalConfiguration": { + "ServiceDiscoveryProvider": { + "Host": "192.168.0.13", + "Port": 443, + "Token": "txpc696iUhbVoudg164r93CxDTrKRVWG", + "Namespace": "dev", + "Type": "kube" + } + } +} diff --git a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json index 8a6792438..b541e95c4 100644 --- a/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json +++ b/samples/OcelotServiceFabric/src/OcelotApplicationApiGateway/ocelot.json @@ -1,21 +1,21 @@ -{ - "ReRoutes": [ - { - "DownstreamPathTemplate": "/api/values", - "UpstreamPathTemplate": "/EquipmentInterfaces", - "UpstreamHttpMethod": [ - "Get" - ], - "DownstreamScheme": "http", - "ServiceName": "OcelotServiceApplication/OcelotApplicationService" - } - ], - "GlobalConfiguration": { - "RequestIdKey": "OcRequestId", - "ServiceDiscoveryProvider": { - "Host": "localhost", - "Port": 19081, - "Type": "ServiceFabric" - } - } -} +{ + "Routes": [ + { + "DownstreamPathTemplate": "/api/values", + "UpstreamPathTemplate": "/EquipmentInterfaces", + "UpstreamHttpMethod": [ + "Get" + ], + "DownstreamScheme": "http", + "ServiceName": "OcelotServiceApplication/OcelotApplicationService" + } + ], + "GlobalConfiguration": { + "RequestIdKey": "OcRequestId", + "ServiceDiscoveryProvider": { + "Host": "localhost", + "Port": 19081, + "Type": "ServiceFabric" + } + } +} diff --git a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs index a70fbbeb9..ac1f2dfb0 100644 --- a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs +++ b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs @@ -1,27 +1,27 @@ -namespace Ocelot.Provider.Consul -{ - using Logging; - using Microsoft.Extensions.DependencyInjection; - using ServiceDiscovery; - - public static class ConsulProviderFactory - { - public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) => - { - var factory = provider.GetService(); - - var consulFactory = provider.GetService(); - - var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Scheme, config.Host, config.Port, reRoute.ServiceName, config.Token); - - var consulServiceDiscoveryProvider = new Consul(consulRegistryConfiguration, factory, consulFactory); - - if (config.Type?.ToLower() == "pollconsul") - { - return new PollConsul(config.PollingInterval, factory, consulServiceDiscoveryProvider); - } - - return consulServiceDiscoveryProvider; - }; - } -} +namespace Ocelot.Provider.Consul +{ + using Logging; + using Microsoft.Extensions.DependencyInjection; + using ServiceDiscovery; + + public static class ConsulProviderFactory + { + public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) => + { + var factory = provider.GetService(); + + var consulFactory = provider.GetService(); + + var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Scheme, config.Host, config.Port, route.ServiceName, config.Token); + + var consulServiceDiscoveryProvider = new Consul(consulRegistryConfiguration, factory, consulFactory); + + if (config.Type?.ToLower() == "pollconsul") + { + return new PollConsul(config.PollingInterval, factory, consulServiceDiscoveryProvider); + } + + return consulServiceDiscoveryProvider; + }; + } +} diff --git a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs index d9bba1791..550a83a3b 100644 --- a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs +++ b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs @@ -1,21 +1,21 @@ -namespace Ocelot.Provider.Eureka -{ - using Microsoft.Extensions.DependencyInjection; - using ServiceDiscovery; - using Steeltoe.Common.Discovery; - - public static class EurekaProviderFactory - { - public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) => - { - var client = provider.GetService(); - - if (config.Type?.ToLower() == "eureka" && client != null) - { - return new Eureka(reRoute.ServiceName, client); - } - - return null; - }; - } -} +namespace Ocelot.Provider.Eureka +{ + using Microsoft.Extensions.DependencyInjection; + using ServiceDiscovery; + using Steeltoe.Common.Discovery; + + public static class EurekaProviderFactory + { + public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) => + { + var client = provider.GetService(); + + if (config.Type?.ToLower() == "eureka" && client != null) + { + return new Eureka(route.ServiceName, client); + } + + return null; + }; + } +} diff --git a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs index 2918451a2..0927ed6dc 100644 --- a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs +++ b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs @@ -1,37 +1,37 @@ -using KubeClient; -using Microsoft.Extensions.DependencyInjection; -using Ocelot.Logging; -using Ocelot.ServiceDiscovery; -using System; -using Ocelot.Configuration; - -namespace Ocelot.Provider.Kubernetes -{ - public static class KubernetesProviderFactory - { - public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) => - { - var factory = provider.GetService(); - return GetKubeProvider(provider, config, reRoute, factory); - }; - - private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetKubeProvider(IServiceProvider provider, ServiceProviderConfiguration config, DownstreamReRoute reRoute, IOcelotLoggerFactory factory) - { - var kubeClient = provider.GetService(); - - var k8sRegistryConfiguration = new KubeRegistryConfiguration() - { - KeyOfServiceInK8s = reRoute.ServiceName, - KubeNamespace = string.IsNullOrEmpty(reRoute.ServiceNamespace) ? config.Namespace : reRoute.ServiceNamespace - }; - - var k8sServiceDiscoveryProvider = new KubernetesServiceDiscoveryProvider(k8sRegistryConfiguration, factory, kubeClient); - - if (config.Type?.ToLower() == "pollkube") - { - return new PollKubernetes(config.PollingInterval, factory, k8sServiceDiscoveryProvider); - } - return k8sServiceDiscoveryProvider; - } - } -} +using KubeClient; +using Microsoft.Extensions.DependencyInjection; +using Ocelot.Logging; +using Ocelot.ServiceDiscovery; +using System; +using Ocelot.Configuration; + +namespace Ocelot.Provider.Kubernetes +{ + public static class KubernetesProviderFactory + { + public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) => + { + var factory = provider.GetService(); + return GetKubeProvider(provider, config, route, factory); + }; + + private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetKubeProvider(IServiceProvider provider, ServiceProviderConfiguration config, DownstreamRoute route, IOcelotLoggerFactory factory) + { + var kubeClient = provider.GetService(); + + var k8sRegistryConfiguration = new KubeRegistryConfiguration() + { + KeyOfServiceInK8s = route.ServiceName, + KubeNamespace = string.IsNullOrEmpty(route.ServiceNamespace) ? config.Namespace : route.ServiceNamespace + }; + + var k8sServiceDiscoveryProvider = new KubernetesServiceDiscoveryProvider(k8sRegistryConfiguration, factory, kubeClient); + + if (config.Type?.ToLower() == "pollkube") + { + return new PollKubernetes(config.PollingInterval, factory, k8sServiceDiscoveryProvider); + } + return k8sServiceDiscoveryProvider; + } + } +} diff --git a/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs index f753d9d36..08c892d3d 100644 --- a/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs +++ b/src/Ocelot.Provider.Polly/OcelotBuilderExtensions.cs @@ -1,38 +1,38 @@ -namespace Ocelot.Provider.Polly -{ - using Configuration; - using DependencyInjection; - using Errors; - using global::Polly.CircuitBreaker; - using global::Polly.Timeout; - using Logging; - using Microsoft.Extensions.DependencyInjection; - using Requester; - using System; - using System.Collections.Generic; - using System.Net.Http; - using System.Threading.Tasks; - - public static class OcelotBuilderExtensions - { - public static IOcelotBuilder AddPolly(this IOcelotBuilder builder) - { - var errorMapping = new Dictionary> - { - {typeof(TaskCanceledException), e => new RequestTimedOutError(e)}, - {typeof(TimeoutRejectedException), e => new RequestTimedOutError(e)}, - {typeof(BrokenCircuitException), e => new RequestTimedOutError(e)} - }; - - builder.Services.AddSingleton(errorMapping); - - DelegatingHandler QosDelegatingHandlerDelegate(DownstreamReRoute reRoute, IOcelotLoggerFactory logger) - { - return new PollyCircuitBreakingDelegatingHandler(new PollyQoSProvider(reRoute, logger), logger); - } - - builder.Services.AddSingleton((QosDelegatingHandlerDelegate)QosDelegatingHandlerDelegate); - return builder; - } - } -} +namespace Ocelot.Provider.Polly +{ + using Configuration; + using DependencyInjection; + using Errors; + using global::Polly.CircuitBreaker; + using global::Polly.Timeout; + using Logging; + using Microsoft.Extensions.DependencyInjection; + using Requester; + using System; + using System.Collections.Generic; + using System.Net.Http; + using System.Threading.Tasks; + + public static class OcelotBuilderExtensions + { + public static IOcelotBuilder AddPolly(this IOcelotBuilder builder) + { + var errorMapping = new Dictionary> + { + {typeof(TaskCanceledException), e => new RequestTimedOutError(e)}, + {typeof(TimeoutRejectedException), e => new RequestTimedOutError(e)}, + {typeof(BrokenCircuitException), e => new RequestTimedOutError(e)} + }; + + builder.Services.AddSingleton(errorMapping); + + DelegatingHandler QosDelegatingHandlerDelegate(DownstreamRoute route, IOcelotLoggerFactory logger) + { + return new PollyCircuitBreakingDelegatingHandler(new PollyQoSProvider(route, logger), logger); + } + + builder.Services.AddSingleton((QosDelegatingHandlerDelegate)QosDelegatingHandlerDelegate); + return builder; + } + } +} diff --git a/src/Ocelot.Provider.Polly/PollyQoSProvider.cs b/src/Ocelot.Provider.Polly/PollyQoSProvider.cs index 3f2c6720e..def8c7d85 100644 --- a/src/Ocelot.Provider.Polly/PollyQoSProvider.cs +++ b/src/Ocelot.Provider.Polly/PollyQoSProvider.cs @@ -1,59 +1,59 @@ -namespace Ocelot.Provider.Polly -{ - using global::Polly; - using global::Polly.CircuitBreaker; - using global::Polly.Timeout; - using Ocelot.Configuration; - using Ocelot.Logging; - using System; - using System.Net.Http; - - public class PollyQoSProvider - { - private readonly AsyncCircuitBreakerPolicy _circuitBreakerPolicy; - private readonly AsyncTimeoutPolicy _timeoutPolicy; - private readonly IOcelotLogger _logger; - - public PollyQoSProvider(DownstreamReRoute reRoute, IOcelotLoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger(); - - Enum.TryParse(reRoute.QosOptions.TimeoutStrategy, out TimeoutStrategy strategy); - - _timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(reRoute.QosOptions.TimeoutValue), strategy); - - if (reRoute.QosOptions.ExceptionsAllowedBeforeBreaking > 0) - { - _circuitBreakerPolicy = Policy - .Handle() - .Or() - .Or() - .CircuitBreakerAsync( - exceptionsAllowedBeforeBreaking: reRoute.QosOptions.ExceptionsAllowedBeforeBreaking, - durationOfBreak: TimeSpan.FromMilliseconds(reRoute.QosOptions.DurationOfBreak), - onBreak: (ex, breakDelay) => - { - _logger.LogError( - ".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex); - }, - onReset: () => - { - _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again."); - }, - onHalfOpen: () => - { - _logger.LogDebug(".Breaker logging: Half-open; next call is a trial."); - } - ); - } - else - { - _circuitBreakerPolicy = null; - } - - CircuitBreaker = new CircuitBreaker(_circuitBreakerPolicy, _timeoutPolicy); - } - - public CircuitBreaker CircuitBreaker { get; } - } -} +namespace Ocelot.Provider.Polly +{ + using global::Polly; + using global::Polly.CircuitBreaker; + using global::Polly.Timeout; + using Ocelot.Configuration; + using Ocelot.Logging; + using System; + using System.Net.Http; + + public class PollyQoSProvider + { + private readonly AsyncCircuitBreakerPolicy _circuitBreakerPolicy; + private readonly AsyncTimeoutPolicy _timeoutPolicy; + private readonly IOcelotLogger _logger; + + public PollyQoSProvider(DownstreamRoute route, IOcelotLoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + + Enum.TryParse(route.QosOptions.TimeoutStrategy, out TimeoutStrategy strategy); + + _timeoutPolicy = Policy.TimeoutAsync(TimeSpan.FromMilliseconds(route.QosOptions.TimeoutValue), strategy); + + if (route.QosOptions.ExceptionsAllowedBeforeBreaking > 0) + { + _circuitBreakerPolicy = Policy + .Handle() + .Or() + .Or() + .CircuitBreakerAsync( + exceptionsAllowedBeforeBreaking: route.QosOptions.ExceptionsAllowedBeforeBreaking, + durationOfBreak: TimeSpan.FromMilliseconds(route.QosOptions.DurationOfBreak), + onBreak: (ex, breakDelay) => + { + _logger.LogError( + ".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex); + }, + onReset: () => + { + _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again."); + }, + onHalfOpen: () => + { + _logger.LogDebug(".Breaker logging: Half-open; next call is a trial."); + } + ); + } + else + { + _circuitBreakerPolicy = null; + } + + CircuitBreaker = new CircuitBreaker(_circuitBreakerPolicy, _timeoutPolicy); + } + + public CircuitBreaker CircuitBreaker { get; } + } +} diff --git a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs index 67a8ff310..2eba74293 100644 --- a/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs +++ b/src/Ocelot/Authentication/Middleware/AuthenticationMiddleware.cs @@ -21,13 +21,13 @@ public AuthenticationMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - if (httpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(downstreamReRoute)) + if (httpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(downstreamRoute)) { Logger.LogInformation($"{httpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated"); - var result = await httpContext.AuthenticateAsync(downstreamReRoute.AuthenticationOptions.AuthenticationProviderKey); + var result = await httpContext.AuthenticateAsync(downstreamRoute.AuthenticationOptions.AuthenticationProviderKey); httpContext.User = result.Principal; @@ -54,9 +54,9 @@ public async Task Invoke(HttpContext httpContext) } } - private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute) + private static bool IsAuthenticatedRoute(DownstreamRoute route) { - return reRoute.IsAuthenticated; + return route.IsAuthenticated; } } } diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs index 09b55644c..0d02c62ed 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs +++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs @@ -27,13 +27,13 @@ public AuthorisationMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - if (!IsOptionsHttpMethod(httpContext) && IsAuthenticatedRoute(downstreamReRoute)) + if (!IsOptionsHttpMethod(httpContext) && IsAuthenticatedRoute(downstreamRoute)) { Logger.LogInformation("route is authenticated scopes must be checked"); - var authorised = _scopesAuthoriser.Authorise(httpContext.User, downstreamReRoute.AuthenticationOptions.AllowedScopes); + var authorised = _scopesAuthoriser.Authorise(httpContext.User, downstreamRoute.AuthenticationOptions.AllowedScopes); if (authorised.IsError) { @@ -52,15 +52,15 @@ public async Task Invoke(HttpContext httpContext) Logger.LogWarning("user scopes is not authorised setting pipeline error"); httpContext.Items.SetError(new UnauthorisedError( - $"{httpContext.User.Identity.Name} unable to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}")); + $"{httpContext.User.Identity.Name} unable to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}")); } } - if (!IsOptionsHttpMethod(httpContext) && IsAuthorisedRoute(downstreamReRoute)) + if (!IsOptionsHttpMethod(httpContext) && IsAuthorisedRoute(downstreamRoute)) { Logger.LogInformation("route is authorised"); - var authorised = _claimsAuthoriser.Authorise(httpContext.User, downstreamReRoute.RouteClaimsRequirement, httpContext.Items.TemplatePlaceholderNameAndValues()); + var authorised = _claimsAuthoriser.Authorise(httpContext.User, downstreamRoute.RouteClaimsRequirement, httpContext.Items.TemplatePlaceholderNameAndValues()); if (authorised.IsError) { @@ -72,19 +72,19 @@ public async Task Invoke(HttpContext httpContext) if (IsAuthorised(authorised)) { - Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorised for {downstreamReRoute.UpstreamPathTemplate.OriginalValue}."); + Logger.LogInformation($"{httpContext.User.Identity.Name} has succesfully been authorised for {downstreamRoute.UpstreamPathTemplate.OriginalValue}."); await _next.Invoke(httpContext); } else { - Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorised to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error"); + Logger.LogWarning($"{httpContext.User.Identity.Name} is not authorised to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}. Setting pipeline error"); - httpContext.Items.SetError(new UnauthorisedError($"{httpContext.User.Identity.Name} is not authorised to access {downstreamReRoute.UpstreamPathTemplate.OriginalValue}")); + httpContext.Items.SetError(new UnauthorisedError($"{httpContext.User.Identity.Name} is not authorised to access {downstreamRoute.UpstreamPathTemplate.OriginalValue}")); } } else { - Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised"); + Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} route does not require user to be authorised"); await _next.Invoke(httpContext); } } @@ -94,14 +94,14 @@ private static bool IsAuthorised(Response authorised) return authorised.Data; } - private static bool IsAuthenticatedRoute(DownstreamReRoute reRoute) + private static bool IsAuthenticatedRoute(DownstreamRoute route) { - return reRoute.IsAuthenticated; + return route.IsAuthenticated; } - private static bool IsAuthorisedRoute(DownstreamReRoute reRoute) + private static bool IsAuthorisedRoute(DownstreamRoute route) { - return reRoute.IsAuthorised; + return route.IsAuthorised; } private static bool IsOptionsHttpMethod(HttpContext httpContext) diff --git a/src/Ocelot/Cache/IRegionCreator.cs b/src/Ocelot/Cache/IRegionCreator.cs index f35c9a250..4277ed687 100644 --- a/src/Ocelot/Cache/IRegionCreator.cs +++ b/src/Ocelot/Cache/IRegionCreator.cs @@ -4,6 +4,6 @@ namespace Ocelot.Cache { public interface IRegionCreator { - string Create(FileReRoute reRoute); + string Create(FileRoute route); } } \ No newline at end of file diff --git a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs index 407393d96..ea544d2b5 100644 --- a/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs +++ b/src/Ocelot/Cache/Middleware/OutputCacheMiddleware.cs @@ -1,129 +1,129 @@ -namespace Ocelot.Cache.Middleware -{ - using Ocelot.Logging; - using Ocelot.Middleware; - using System; - using System.IO; - using System.Linq; - using System.Net.Http; - using System.Threading.Tasks; +namespace Ocelot.Cache.Middleware +{ + using Ocelot.Logging; + using Ocelot.Middleware; + using System; + using System.IO; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Ocelot.DownstreamRouteFinder.Middleware; - public class OutputCacheMiddleware : OcelotMiddleware - { - private readonly RequestDelegate _next; - private readonly IOcelotCache _outputCache; - private readonly ICacheKeyGenerator _cacheGenerator; - - public OutputCacheMiddleware(RequestDelegate next, - IOcelotLoggerFactory loggerFactory, - IOcelotCache outputCache, - ICacheKeyGenerator cacheGenerator) - : base(loggerFactory.CreateLogger()) - { - _next = next; - _outputCache = outputCache; - _cacheGenerator = cacheGenerator; - } - - public async Task Invoke(HttpContext httpContext) - { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); - - if (!downstreamReRoute.IsCached) - { - await _next.Invoke(httpContext); - return; + public class OutputCacheMiddleware : OcelotMiddleware + { + private readonly RequestDelegate _next; + private readonly IOcelotCache _outputCache; + private readonly ICacheKeyGenerator _cacheGenerator; + + public OutputCacheMiddleware(RequestDelegate next, + IOcelotLoggerFactory loggerFactory, + IOcelotCache outputCache, + ICacheKeyGenerator cacheGenerator) + : base(loggerFactory.CreateLogger()) + { + _next = next; + _outputCache = outputCache; + _cacheGenerator = cacheGenerator; + } + + public async Task Invoke(HttpContext httpContext) + { + var downstreamRoute = httpContext.Items.DownstreamRoute(); + + if (!downstreamRoute.IsCached) + { + await _next.Invoke(httpContext); + return; } - var downstreamRequest = httpContext.Items.DownstreamRequest(); - - var downstreamUrlKey = $"{downstreamRequest.Method}-{downstreamRequest.OriginalString}"; - string downStreamRequestCacheKey = _cacheGenerator.GenerateRequestCacheKey(downstreamRequest); - - Logger.LogDebug($"Started checking cache for {downstreamUrlKey}"); - - var cached = _outputCache.Get(downStreamRequestCacheKey, downstreamReRoute.CacheOptions.Region); - - if (cached != null) - { - Logger.LogDebug($"cache entry exists for {downstreamUrlKey}"); - - var response = CreateHttpResponseMessage(cached); - SetHttpResponseMessageThisRequest(httpContext, response); - - Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}"); - - return; - } - - Logger.LogDebug($"no resonse cached for {downstreamUrlKey}"); - - await _next.Invoke(httpContext); - - if (httpContext.Items.Errors().Count > 0) - { - Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}"); - - return; + var downstreamRequest = httpContext.Items.DownstreamRequest(); + + var downstreamUrlKey = $"{downstreamRequest.Method}-{downstreamRequest.OriginalString}"; + string downStreamRequestCacheKey = _cacheGenerator.GenerateRequestCacheKey(downstreamRequest); + + Logger.LogDebug($"Started checking cache for {downstreamUrlKey}"); + + var cached = _outputCache.Get(downStreamRequestCacheKey, downstreamRoute.CacheOptions.Region); + + if (cached != null) + { + Logger.LogDebug($"cache entry exists for {downstreamUrlKey}"); + + var response = CreateHttpResponseMessage(cached); + SetHttpResponseMessageThisRequest(httpContext, response); + + Logger.LogDebug($"finished returned cached response for {downstreamUrlKey}"); + + return; + } + + Logger.LogDebug($"no resonse cached for {downstreamUrlKey}"); + + await _next.Invoke(httpContext); + + if (httpContext.Items.Errors().Count > 0) + { + Logger.LogDebug($"there was a pipeline error for {downstreamUrlKey}"); + + return; } - var downstreamResponse = httpContext.Items.DownstreamResponse(); - - cached = await CreateCachedResponse(downstreamResponse); - - _outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(downstreamReRoute.CacheOptions.TtlSeconds), downstreamReRoute.CacheOptions.Region); - - Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}"); - } - - private void SetHttpResponseMessageThisRequest(HttpContext context, - DownstreamResponse response) - { - context.Items.UpsertDownstreamResponse(response); - } - - internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached) - { - if (cached == null) - { - return null; - } - - var content = new MemoryStream(Convert.FromBase64String(cached.Body)); - - var streamContent = new StreamContent(content); - - foreach (var header in cached.ContentHeaders) - { - streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value); - } - - return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList(), cached.ReasonPhrase); - } - - internal async Task CreateCachedResponse(DownstreamResponse response) - { - if (response == null) - { - return null; - } - - var statusCode = response.StatusCode; - var headers = response.Headers.ToDictionary(v => v.Key, v => v.Values); - string body = null; - - if (response.Content != null) - { - var content = await response.Content.ReadAsByteArrayAsync(); - body = Convert.ToBase64String(content); - } - - var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value); - - var cached = new CachedResponse(statusCode, headers, body, contentHeaders, response.ReasonPhrase); - return cached; - } - } -} + var downstreamResponse = httpContext.Items.DownstreamResponse(); + + cached = await CreateCachedResponse(downstreamResponse); + + _outputCache.Add(downStreamRequestCacheKey, cached, TimeSpan.FromSeconds(downstreamRoute.CacheOptions.TtlSeconds), downstreamRoute.CacheOptions.Region); + + Logger.LogDebug($"finished response added to cache for {downstreamUrlKey}"); + } + + private void SetHttpResponseMessageThisRequest(HttpContext context, + DownstreamResponse response) + { + context.Items.UpsertDownstreamResponse(response); + } + + internal DownstreamResponse CreateHttpResponseMessage(CachedResponse cached) + { + if (cached == null) + { + return null; + } + + var content = new MemoryStream(Convert.FromBase64String(cached.Body)); + + var streamContent = new StreamContent(content); + + foreach (var header in cached.ContentHeaders) + { + streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + + return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList(), cached.ReasonPhrase); + } + + internal async Task CreateCachedResponse(DownstreamResponse response) + { + if (response == null) + { + return null; + } + + var statusCode = response.StatusCode; + var headers = response.Headers.ToDictionary(v => v.Key, v => v.Values); + string body = null; + + if (response.Content != null) + { + var content = await response.Content.ReadAsByteArrayAsync(); + body = Convert.ToBase64String(content); + } + + var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value); + + var cached = new CachedResponse(statusCode, headers, body, contentHeaders, response.ReasonPhrase); + return cached; + } + } +} diff --git a/src/Ocelot/Cache/RegionCreator.cs b/src/Ocelot/Cache/RegionCreator.cs index cc1a93592..050d6f2f2 100644 --- a/src/Ocelot/Cache/RegionCreator.cs +++ b/src/Ocelot/Cache/RegionCreator.cs @@ -5,18 +5,18 @@ namespace Ocelot.Cache { public class RegionCreator : IRegionCreator { - public string Create(FileReRoute reRoute) + public string Create(FileRoute route) { - if (!string.IsNullOrEmpty(reRoute?.FileCacheOptions?.Region)) + if (!string.IsNullOrEmpty(route?.FileCacheOptions?.Region)) { - return reRoute?.FileCacheOptions?.Region; + return route?.FileCacheOptions?.Region; } - var methods = string.Join("", reRoute.UpstreamHttpMethod.Select(m => m)); + var methods = string.Join("", route.UpstreamHttpMethod.Select(m => m)); + + var region = $"{methods}{route.UpstreamPathTemplate.Replace("/", "")}"; - var region = $"{methods}{reRoute.UpstreamPathTemplate.Replace("/", "")}"; - return region; } } -} +} diff --git a/src/Ocelot/Claims/Middleware/ClaimsToClaimsMiddleware.cs b/src/Ocelot/Claims/Middleware/ClaimsToClaimsMiddleware.cs index 4b7b73fb0..780736b21 100644 --- a/src/Ocelot/Claims/Middleware/ClaimsToClaimsMiddleware.cs +++ b/src/Ocelot/Claims/Middleware/ClaimsToClaimsMiddleware.cs @@ -1,7 +1,7 @@ namespace Ocelot.Claims.Middleware { - using Microsoft.AspNetCore.Http; - using Ocelot.DownstreamRouteFinder.Middleware; + using Microsoft.AspNetCore.Http; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Logging; using Ocelot.Middleware; using System.Linq; @@ -23,13 +23,13 @@ public ClaimsToClaimsMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - if (downstreamReRoute.ClaimsToClaims.Any()) + if (downstreamRoute.ClaimsToClaims.Any()) { Logger.LogDebug("this route has instructions to convert claims to other claims"); - var result = _addClaimsToRequest.SetClaimsOnContext(downstreamReRoute.ClaimsToClaims, httpContext); + var result = _addClaimsToRequest.SetClaimsOnContext(downstreamRoute.ClaimsToClaims, httpContext); if (result.IsError) { diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index 710497951..b51c33db8 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -1,305 +1,305 @@ -using Ocelot.Configuration.Creator; -using Ocelot.Values; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; - -namespace Ocelot.Configuration.Builder -{ - public class DownstreamReRouteBuilder - { - private AuthenticationOptions _authenticationOptions; - private string _loadBalancerKey; - private string _downstreamPathTemplate; - private UpstreamPathTemplate _upstreamTemplatePattern; - private List _upstreamHttpMethod; - private bool _isAuthenticated; - private List _claimsToHeaders; - private List _claimToClaims; - private Dictionary _routeClaimRequirement; - private bool _isAuthorised; - private List _claimToQueries; - private List _claimToDownstreamPath; - private string _requestIdHeaderKey; - private bool _isCached; - private CacheOptions _fileCacheOptions; - private string _downstreamScheme; - private LoadBalancerOptions _loadBalancerOptions; - private QoSOptions _qosOptions; - private HttpHandlerOptions _httpHandlerOptions; - private bool _enableRateLimiting; - private RateLimitOptions _rateLimitOptions; - private bool _useServiceDiscovery; - private string _serviceName; - private string _serviceNamespace; - private List _upstreamHeaderFindAndReplace; - private List _downstreamHeaderFindAndReplace; - private readonly List _downstreamAddresses; - private string _key; - private List _delegatingHandlers; - private List _addHeadersToDownstream; - private List _addHeadersToUpstream; - private bool _dangerousAcceptAnyServerCertificateValidator; - private SecurityOptions _securityOptions; - private string _downstreamHttpMethod; - private Version _downstreamHttpVersion; - - public DownstreamReRouteBuilder() - { - _downstreamAddresses = new List(); - _delegatingHandlers = new List(); - _addHeadersToDownstream = new List(); - _addHeadersToUpstream = new List(); - } - - public DownstreamReRouteBuilder WithDownstreamAddresses(List downstreamAddresses) - { - _downstreamAddresses.AddRange(downstreamAddresses); - return this; - } - - public DownstreamReRouteBuilder WithDownStreamHttpMethod(string method) - { - _downstreamHttpMethod = method; - return this; - } - - public DownstreamReRouteBuilder WithLoadBalancerOptions(LoadBalancerOptions loadBalancerOptions) - { - _loadBalancerOptions = loadBalancerOptions; - return this; - } - - public DownstreamReRouteBuilder WithDownstreamScheme(string downstreamScheme) - { - _downstreamScheme = downstreamScheme; - return this; - } - - public DownstreamReRouteBuilder WithDownstreamPathTemplate(string input) - { - _downstreamPathTemplate = input; - return this; - } - - public DownstreamReRouteBuilder WithUpstreamPathTemplate(UpstreamPathTemplate input) - { - _upstreamTemplatePattern = input; - return this; - } - - public DownstreamReRouteBuilder WithUpstreamHttpMethod(List input) - { - _upstreamHttpMethod = (input.Count == 0) ? new List() : input.Select(x => new HttpMethod(x.Trim())).ToList(); - return this; - } - - public DownstreamReRouteBuilder WithIsAuthenticated(bool input) - { - _isAuthenticated = input; - return this; - } - - public DownstreamReRouteBuilder WithIsAuthorised(bool input) - { - _isAuthorised = input; - return this; - } - - public DownstreamReRouteBuilder WithRequestIdKey(string input) - { - _requestIdHeaderKey = input; - return this; - } - - public DownstreamReRouteBuilder WithClaimsToHeaders(List input) - { - _claimsToHeaders = input; - return this; - } - - public DownstreamReRouteBuilder WithClaimsToClaims(List input) - { - _claimToClaims = input; - return this; - } - - public DownstreamReRouteBuilder WithRouteClaimsRequirement(Dictionary input) - { - _routeClaimRequirement = input; - return this; - } - - public DownstreamReRouteBuilder WithClaimsToQueries(List input) - { - _claimToQueries = input; - return this; - } - - public DownstreamReRouteBuilder WithClaimsToDownstreamPath(List input) - { - _claimToDownstreamPath = input; - return this; - } - - public DownstreamReRouteBuilder WithIsCached(bool input) - { - _isCached = input; - return this; - } - - public DownstreamReRouteBuilder WithCacheOptions(CacheOptions input) - { - _fileCacheOptions = input; - return this; - } - - public DownstreamReRouteBuilder WithQosOptions(QoSOptions input) - { - _qosOptions = input; - return this; - } - - public DownstreamReRouteBuilder WithLoadBalancerKey(string loadBalancerKey) - { - _loadBalancerKey = loadBalancerKey; - return this; - } - - public DownstreamReRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions) - { - _authenticationOptions = authenticationOptions; - return this; - } - - public DownstreamReRouteBuilder WithEnableRateLimiting(bool input) - { - _enableRateLimiting = input; - return this; - } - - public DownstreamReRouteBuilder WithRateLimitOptions(RateLimitOptions input) - { - _rateLimitOptions = input; - return this; - } - - public DownstreamReRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input) - { - _httpHandlerOptions = input; - return this; - } - - public DownstreamReRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery) - { - _useServiceDiscovery = useServiceDiscovery; - return this; - } - - public DownstreamReRouteBuilder WithServiceName(string serviceName) - { - _serviceName = serviceName; - return this; - } - - public DownstreamReRouteBuilder WithServiceNamespace(string serviceNamespace) - { - _serviceNamespace = serviceNamespace; - return this; - } - - public DownstreamReRouteBuilder WithUpstreamHeaderFindAndReplace(List upstreamHeaderFindAndReplace) - { - _upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace; - return this; - } - - public DownstreamReRouteBuilder WithDownstreamHeaderFindAndReplace(List downstreamHeaderFindAndReplace) - { - _downstreamHeaderFindAndReplace = downstreamHeaderFindAndReplace; - return this; - } - - public DownstreamReRouteBuilder WithKey(string key) - { - _key = key; - return this; - } - - public DownstreamReRouteBuilder WithDelegatingHandlers(List delegatingHandlers) - { - _delegatingHandlers = delegatingHandlers; - return this; - } - - public DownstreamReRouteBuilder WithAddHeadersToDownstream(List addHeadersToDownstream) - { - _addHeadersToDownstream = addHeadersToDownstream; - return this; - } - - public DownstreamReRouteBuilder WithAddHeadersToUpstream(List addHeadersToUpstream) - { - _addHeadersToUpstream = addHeadersToUpstream; - return this; - } - - public DownstreamReRouteBuilder WithDangerousAcceptAnyServerCertificateValidator(bool dangerousAcceptAnyServerCertificateValidator) - { - _dangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator; - return this; - } - - public DownstreamReRouteBuilder WithSecurityOptions(SecurityOptions securityOptions) - { - _securityOptions = securityOptions; - return this; - } - - public DownstreamReRouteBuilder WithDownstreamHttpVersion(Version downstreamHttpVersion) - { - _downstreamHttpVersion = downstreamHttpVersion; - return this; - } - - public DownstreamReRoute Build() - { - return new DownstreamReRoute( - _key, - _upstreamTemplatePattern, - _upstreamHeaderFindAndReplace, - _downstreamHeaderFindAndReplace, - _downstreamAddresses, - _serviceName, - _serviceNamespace, - _httpHandlerOptions, - _useServiceDiscovery, - _enableRateLimiting, - _qosOptions, - _downstreamScheme, - _requestIdHeaderKey, - _isCached, - _fileCacheOptions, - _loadBalancerOptions, - _rateLimitOptions, - _routeClaimRequirement, - _claimToQueries, - _claimsToHeaders, - _claimToClaims, - _claimToDownstreamPath, - _isAuthenticated, - _isAuthorised, - _authenticationOptions, - new DownstreamPathTemplate(_downstreamPathTemplate), - _loadBalancerKey, - _delegatingHandlers, - _addHeadersToDownstream, - _addHeadersToUpstream, - _dangerousAcceptAnyServerCertificateValidator, - _securityOptions, - _downstreamHttpMethod, - _downstreamHttpVersion); - } - } -} +using Ocelot.Configuration.Creator; +using Ocelot.Values; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; + +namespace Ocelot.Configuration.Builder +{ + public class DownstreamRouteBuilder + { + private AuthenticationOptions _authenticationOptions; + private string _loadBalancerKey; + private string _downstreamPathTemplate; + private UpstreamPathTemplate _upstreamTemplatePattern; + private List _upstreamHttpMethod; + private bool _isAuthenticated; + private List _claimsToHeaders; + private List _claimToClaims; + private Dictionary _routeClaimRequirement; + private bool _isAuthorised; + private List _claimToQueries; + private List _claimToDownstreamPath; + private string _requestIdHeaderKey; + private bool _isCached; + private CacheOptions _fileCacheOptions; + private string _downstreamScheme; + private LoadBalancerOptions _loadBalancerOptions; + private QoSOptions _qosOptions; + private HttpHandlerOptions _httpHandlerOptions; + private bool _enableRateLimiting; + private RateLimitOptions _rateLimitOptions; + private bool _useServiceDiscovery; + private string _serviceName; + private string _serviceNamespace; + private List _upstreamHeaderFindAndReplace; + private List _downstreamHeaderFindAndReplace; + private readonly List _downstreamAddresses; + private string _key; + private List _delegatingHandlers; + private List _addHeadersToDownstream; + private List _addHeadersToUpstream; + private bool _dangerousAcceptAnyServerCertificateValidator; + private SecurityOptions _securityOptions; + private string _downstreamHttpMethod; + private Version _downstreamHttpVersion; + + public DownstreamRouteBuilder() + { + _downstreamAddresses = new List(); + _delegatingHandlers = new List(); + _addHeadersToDownstream = new List(); + _addHeadersToUpstream = new List(); + } + + public DownstreamRouteBuilder WithDownstreamAddresses(List downstreamAddresses) + { + _downstreamAddresses.AddRange(downstreamAddresses); + return this; + } + + public DownstreamRouteBuilder WithDownStreamHttpMethod(string method) + { + _downstreamHttpMethod = method; + return this; + } + + public DownstreamRouteBuilder WithLoadBalancerOptions(LoadBalancerOptions loadBalancerOptions) + { + _loadBalancerOptions = loadBalancerOptions; + return this; + } + + public DownstreamRouteBuilder WithDownstreamScheme(string downstreamScheme) + { + _downstreamScheme = downstreamScheme; + return this; + } + + public DownstreamRouteBuilder WithDownstreamPathTemplate(string input) + { + _downstreamPathTemplate = input; + return this; + } + + public DownstreamRouteBuilder WithUpstreamPathTemplate(UpstreamPathTemplate input) + { + _upstreamTemplatePattern = input; + return this; + } + + public DownstreamRouteBuilder WithUpstreamHttpMethod(List input) + { + _upstreamHttpMethod = (input.Count == 0) ? new List() : input.Select(x => new HttpMethod(x.Trim())).ToList(); + return this; + } + + public DownstreamRouteBuilder WithIsAuthenticated(bool input) + { + _isAuthenticated = input; + return this; + } + + public DownstreamRouteBuilder WithIsAuthorised(bool input) + { + _isAuthorised = input; + return this; + } + + public DownstreamRouteBuilder WithRequestIdKey(string input) + { + _requestIdHeaderKey = input; + return this; + } + + public DownstreamRouteBuilder WithClaimsToHeaders(List input) + { + _claimsToHeaders = input; + return this; + } + + public DownstreamRouteBuilder WithClaimsToClaims(List input) + { + _claimToClaims = input; + return this; + } + + public DownstreamRouteBuilder WithRouteClaimsRequirement(Dictionary input) + { + _routeClaimRequirement = input; + return this; + } + + public DownstreamRouteBuilder WithClaimsToQueries(List input) + { + _claimToQueries = input; + return this; + } + + public DownstreamRouteBuilder WithClaimsToDownstreamPath(List input) + { + _claimToDownstreamPath = input; + return this; + } + + public DownstreamRouteBuilder WithIsCached(bool input) + { + _isCached = input; + return this; + } + + public DownstreamRouteBuilder WithCacheOptions(CacheOptions input) + { + _fileCacheOptions = input; + return this; + } + + public DownstreamRouteBuilder WithQosOptions(QoSOptions input) + { + _qosOptions = input; + return this; + } + + public DownstreamRouteBuilder WithLoadBalancerKey(string loadBalancerKey) + { + _loadBalancerKey = loadBalancerKey; + return this; + } + + public DownstreamRouteBuilder WithAuthenticationOptions(AuthenticationOptions authenticationOptions) + { + _authenticationOptions = authenticationOptions; + return this; + } + + public DownstreamRouteBuilder WithEnableRateLimiting(bool input) + { + _enableRateLimiting = input; + return this; + } + + public DownstreamRouteBuilder WithRateLimitOptions(RateLimitOptions input) + { + _rateLimitOptions = input; + return this; + } + + public DownstreamRouteBuilder WithHttpHandlerOptions(HttpHandlerOptions input) + { + _httpHandlerOptions = input; + return this; + } + + public DownstreamRouteBuilder WithUseServiceDiscovery(bool useServiceDiscovery) + { + _useServiceDiscovery = useServiceDiscovery; + return this; + } + + public DownstreamRouteBuilder WithServiceName(string serviceName) + { + _serviceName = serviceName; + return this; + } + + public DownstreamRouteBuilder WithServiceNamespace(string serviceNamespace) + { + _serviceNamespace = serviceNamespace; + return this; + } + + public DownstreamRouteBuilder WithUpstreamHeaderFindAndReplace(List upstreamHeaderFindAndReplace) + { + _upstreamHeaderFindAndReplace = upstreamHeaderFindAndReplace; + return this; + } + + public DownstreamRouteBuilder WithDownstreamHeaderFindAndReplace(List downstreamHeaderFindAndReplace) + { + _downstreamHeaderFindAndReplace = downstreamHeaderFindAndReplace; + return this; + } + + public DownstreamRouteBuilder WithKey(string key) + { + _key = key; + return this; + } + + public DownstreamRouteBuilder WithDelegatingHandlers(List delegatingHandlers) + { + _delegatingHandlers = delegatingHandlers; + return this; + } + + public DownstreamRouteBuilder WithAddHeadersToDownstream(List addHeadersToDownstream) + { + _addHeadersToDownstream = addHeadersToDownstream; + return this; + } + + public DownstreamRouteBuilder WithAddHeadersToUpstream(List addHeadersToUpstream) + { + _addHeadersToUpstream = addHeadersToUpstream; + return this; + } + + public DownstreamRouteBuilder WithDangerousAcceptAnyServerCertificateValidator(bool dangerousAcceptAnyServerCertificateValidator) + { + _dangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator; + return this; + } + + public DownstreamRouteBuilder WithSecurityOptions(SecurityOptions securityOptions) + { + _securityOptions = securityOptions; + return this; + } + + public DownstreamRouteBuilder WithDownstreamHttpVersion(Version downstreamHttpVersion) + { + _downstreamHttpVersion = downstreamHttpVersion; + return this; + } + + public DownstreamRoute Build() + { + return new DownstreamRoute( + _key, + _upstreamTemplatePattern, + _upstreamHeaderFindAndReplace, + _downstreamHeaderFindAndReplace, + _downstreamAddresses, + _serviceName, + _serviceNamespace, + _httpHandlerOptions, + _useServiceDiscovery, + _enableRateLimiting, + _qosOptions, + _downstreamScheme, + _requestIdHeaderKey, + _isCached, + _fileCacheOptions, + _loadBalancerOptions, + _rateLimitOptions, + _routeClaimRequirement, + _claimToQueries, + _claimsToHeaders, + _claimToClaims, + _claimToDownstreamPath, + _isAuthenticated, + _isAuthorised, + _authenticationOptions, + new DownstreamPathTemplate(_downstreamPathTemplate), + _loadBalancerKey, + _delegatingHandlers, + _addHeadersToDownstream, + _addHeadersToUpstream, + _dangerousAcceptAnyServerCertificateValidator, + _securityOptions, + _downstreamHttpMethod, + _downstreamHttpVersion); + } + } +} diff --git a/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs index 413163dbc..717918485 100644 --- a/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs @@ -1,42 +1,42 @@ -namespace Ocelot.Configuration.Builder -{ - public class QoSOptionsBuilder - { - private int _exceptionsAllowedBeforeBreaking; - - private int _durationOfBreak; - - private int _timeoutValue; - - private string _key; - - public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking) - { - _exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; - return this; - } - - public QoSOptionsBuilder WithDurationOfBreak(int durationOfBreak) - { - _durationOfBreak = durationOfBreak; - return this; - } - - public QoSOptionsBuilder WithTimeoutValue(int timeoutValue) - { - _timeoutValue = timeoutValue; - return this; - } - - public QoSOptionsBuilder WithKey(string input) - { - _key = input; - return this; - } - - public QoSOptions Build() - { - return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue, _key); - } - } +namespace Ocelot.Configuration.Builder +{ + public class QoSOptionsBuilder + { + private int _exceptionsAllowedBeforeBreaking; + + private int _durationOfBreak; + + private int _timeoutValue; + + private string _key; + + public QoSOptionsBuilder WithExceptionsAllowedBeforeBreaking(int exceptionsAllowedBeforeBreaking) + { + _exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking; + return this; + } + + public QoSOptionsBuilder WithDurationOfBreak(int durationOfBreak) + { + _durationOfBreak = durationOfBreak; + return this; + } + + public QoSOptionsBuilder WithTimeoutValue(int timeoutValue) + { + _timeoutValue = timeoutValue; + return this; + } + + public QoSOptionsBuilder WithKey(string input) + { + _key = input; + return this; + } + + public QoSOptions Build() + { + return new QoSOptions(_exceptionsAllowedBeforeBreaking, _durationOfBreak, _timeoutValue, _key); + } + } } diff --git a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs deleted file mode 100644 index 4716c03af..000000000 --- a/src/Ocelot/Configuration/Builder/ReRouteBuilder.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace Ocelot.Configuration.Builder -{ - using Ocelot.Configuration.File; - using Ocelot.Values; - using System.Collections.Generic; - using System.Linq; - using System.Net.Http; - - public class ReRouteBuilder - { - private UpstreamPathTemplate _upstreamTemplatePattern; - private List _upstreamHttpMethod; - private string _upstreamHost; - private List _downstreamReRoutes; - private List _downstreamReRoutesConfig; - private string _aggregator; - - public ReRouteBuilder() - { - _downstreamReRoutes = new List(); - _downstreamReRoutesConfig = new List(); - } - - public ReRouteBuilder WithDownstreamReRoute(DownstreamReRoute value) - { - _downstreamReRoutes.Add(value); - return this; - } - - public ReRouteBuilder WithDownstreamReRoutes(List value) - { - _downstreamReRoutes = value; - return this; - } - - public ReRouteBuilder WithUpstreamHost(string upstreamAddresses) - { - _upstreamHost = upstreamAddresses; - return this; - } - - public ReRouteBuilder WithUpstreamPathTemplate(UpstreamPathTemplate input) - { - _upstreamTemplatePattern = input; - return this; - } - - public ReRouteBuilder WithUpstreamHttpMethod(List input) - { - _upstreamHttpMethod = (input.Count == 0) ? new List() : input.Select(x => new HttpMethod(x.Trim())).ToList(); - return this; - } - - public ReRouteBuilder WithAggregateReRouteConfig(List aggregateReRouteConfigs) - { - _downstreamReRoutesConfig = aggregateReRouteConfigs; - return this; - } - - public ReRouteBuilder WithAggregator(string aggregator) - { - _aggregator = aggregator; - return this; - } - - public ReRoute Build() - { - return new ReRoute( - _downstreamReRoutes, - _downstreamReRoutesConfig, - _upstreamHttpMethod, - _upstreamTemplatePattern, - _upstreamHost, - _aggregator - ); - } - } -} diff --git a/src/Ocelot/Configuration/Builder/RouteBuilder.cs b/src/Ocelot/Configuration/Builder/RouteBuilder.cs new file mode 100644 index 000000000..1194b5ea7 --- /dev/null +++ b/src/Ocelot/Configuration/Builder/RouteBuilder.cs @@ -0,0 +1,78 @@ +namespace Ocelot.Configuration.Builder +{ + using Ocelot.Configuration.File; + using Ocelot.Values; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + + public class RouteBuilder + { + private UpstreamPathTemplate _upstreamTemplatePattern; + private List _upstreamHttpMethod; + private string _upstreamHost; + private List _downstreamRoutes; + private List _downstreamRoutesConfig; + private string _aggregator; + + public RouteBuilder() + { + _downstreamRoutes = new List(); + _downstreamRoutesConfig = new List(); + } + + public RouteBuilder WithDownstreamRoute(DownstreamRoute value) + { + _downstreamRoutes.Add(value); + return this; + } + + public RouteBuilder WithDownstreamRoutes(List value) + { + _downstreamRoutes = value; + return this; + } + + public RouteBuilder WithUpstreamHost(string upstreamAddresses) + { + _upstreamHost = upstreamAddresses; + return this; + } + + public RouteBuilder WithUpstreamPathTemplate(UpstreamPathTemplate input) + { + _upstreamTemplatePattern = input; + return this; + } + + public RouteBuilder WithUpstreamHttpMethod(List input) + { + _upstreamHttpMethod = (input.Count == 0) ? new List() : input.Select(x => new HttpMethod(x.Trim())).ToList(); + return this; + } + + public RouteBuilder WithAggregateRouteConfig(List aggregateRouteConfigs) + { + _downstreamRoutesConfig = aggregateRouteConfigs; + return this; + } + + public RouteBuilder WithAggregator(string aggregator) + { + _aggregator = aggregator; + return this; + } + + public Route Build() + { + return new Route( + _downstreamRoutes, + _downstreamRoutesConfig, + _upstreamHttpMethod, + _upstreamTemplatePattern, + _upstreamHost, + _aggregator + ); + } + } +} diff --git a/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/RouteOptionsBuilder.cs similarity index 54% rename from src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs rename to src/Ocelot/Configuration/Builder/RouteOptionsBuilder.cs index 53fc54c79..ef1cdcd32 100644 --- a/src/Ocelot/Configuration/Builder/ReRouteOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/RouteOptionsBuilder.cs @@ -1,46 +1,46 @@ -namespace Ocelot.Configuration.Builder -{ - public class ReRouteOptionsBuilder - { - private bool _isAuthenticated; - private bool _isAuthorised; - private bool _isCached; - private bool _enableRateLimiting; - private bool _useServiceDiscovery; - - public ReRouteOptionsBuilder WithIsCached(bool isCached) - { - _isCached = isCached; - return this; - } - - public ReRouteOptionsBuilder WithIsAuthenticated(bool isAuthenticated) - { - _isAuthenticated = isAuthenticated; - return this; - } - - public ReRouteOptionsBuilder WithIsAuthorised(bool isAuthorised) - { - _isAuthorised = isAuthorised; - return this; - } - - public ReRouteOptionsBuilder WithRateLimiting(bool enableRateLimiting) - { - _enableRateLimiting = enableRateLimiting; - return this; - } - - public ReRouteOptionsBuilder WithUseServiceDiscovery(bool useServiceDiscovery) - { - _useServiceDiscovery = useServiceDiscovery; - return this; - } - - public ReRouteOptions Build() - { - return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting, _useServiceDiscovery); - } - } -} +namespace Ocelot.Configuration.Builder +{ + public class RouteOptionsBuilder + { + private bool _isAuthenticated; + private bool _isAuthorised; + private bool _isCached; + private bool _enableRateLimiting; + private bool _useServiceDiscovery; + + public RouteOptionsBuilder WithIsCached(bool isCached) + { + _isCached = isCached; + return this; + } + + public RouteOptionsBuilder WithIsAuthenticated(bool isAuthenticated) + { + _isAuthenticated = isAuthenticated; + return this; + } + + public RouteOptionsBuilder WithIsAuthorised(bool isAuthorised) + { + _isAuthorised = isAuthorised; + return this; + } + + public RouteOptionsBuilder WithRateLimiting(bool enableRateLimiting) + { + _enableRateLimiting = enableRateLimiting; + return this; + } + + public RouteOptionsBuilder WithUseServiceDiscovery(bool useServiceDiscovery) + { + _useServiceDiscovery = useServiceDiscovery; + return this; + } + + public RouteOptions Build() + { + return new RouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting, _useServiceDiscovery); + } + } +} diff --git a/src/Ocelot/Configuration/Creator/AggregatesCreator.cs b/src/Ocelot/Configuration/Creator/AggregatesCreator.cs index 1dd8fea51..704d6fe7c 100644 --- a/src/Ocelot/Configuration/Creator/AggregatesCreator.cs +++ b/src/Ocelot/Configuration/Creator/AggregatesCreator.cs @@ -1,55 +1,55 @@ -namespace Ocelot.Configuration.Creator -{ - using Builder; - using File; - using System.Collections.Generic; - using System.Linq; - - public class AggregatesCreator : IAggregatesCreator - { - private readonly IUpstreamTemplatePatternCreator _creator; - - public AggregatesCreator(IUpstreamTemplatePatternCreator creator) - { - _creator = creator; - } - - public List Create(FileConfiguration fileConfiguration, List reRoutes) - { - return fileConfiguration.Aggregates - .Select(aggregate => SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration)) - .Where(aggregate => aggregate != null) - .ToList(); - } - - private ReRoute SetUpAggregateReRoute(IEnumerable reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration) - { - var applicableReRoutes = new List(); - var allReRoutes = reRoutes.SelectMany(x => x.DownstreamReRoute); - - foreach (var reRouteKey in aggregateReRoute.ReRouteKeys) - { - var selec = allReRoutes.FirstOrDefault(q => q.Key == reRouteKey); - if (selec == null) - { - return null; - } - - applicableReRoutes.Add(selec); - } - - var upstreamTemplatePattern = _creator.Create(aggregateReRoute); - - var reRoute = new ReRouteBuilder() - .WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod) - .WithUpstreamPathTemplate(upstreamTemplatePattern) - .WithDownstreamReRoutes(applicableReRoutes) - .WithAggregateReRouteConfig(aggregateReRoute.ReRouteKeysConfig) - .WithUpstreamHost(aggregateReRoute.UpstreamHost) - .WithAggregator(aggregateReRoute.Aggregator) - .Build(); - - return reRoute; - } - } -} +namespace Ocelot.Configuration.Creator +{ + using Builder; + using File; + using System.Collections.Generic; + using System.Linq; + + public class AggregatesCreator : IAggregatesCreator + { + private readonly IUpstreamTemplatePatternCreator _creator; + + public AggregatesCreator(IUpstreamTemplatePatternCreator creator) + { + _creator = creator; + } + + public List Create(FileConfiguration fileConfiguration, List routes) + { + return fileConfiguration.Aggregates + .Select(aggregate => SetUpAggregateRoute(routes, aggregate, fileConfiguration.GlobalConfiguration)) + .Where(aggregate => aggregate != null) + .ToList(); + } + + private Route SetUpAggregateRoute(IEnumerable routes, FileAggregateRoute aggregateRoute, FileGlobalConfiguration globalConfiguration) + { + var applicableRoutes = new List(); + var allRoutes = routes.SelectMany(x => x.DownstreamRoute); + + foreach (var routeKey in aggregateRoute.RouteKeys) + { + var selec = allRoutes.FirstOrDefault(q => q.Key == routeKey); + if (selec == null) + { + return null; + } + + applicableRoutes.Add(selec); + } + + var upstreamTemplatePattern = _creator.Create(aggregateRoute); + + var route = new RouteBuilder() + .WithUpstreamHttpMethod(aggregateRoute.UpstreamHttpMethod) + .WithUpstreamPathTemplate(upstreamTemplatePattern) + .WithDownstreamRoutes(applicableRoutes) + .WithAggregateRouteConfig(aggregateRoute.RouteKeysConfig) + .WithUpstreamHost(aggregateRoute.UpstreamHost) + .WithAggregator(aggregateRoute.Aggregator) + .Build(); + + return route; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs b/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs index 69f4f49d2..275d6d90d 100644 --- a/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs @@ -4,9 +4,9 @@ namespace Ocelot.Configuration.Creator { public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator { - public AuthenticationOptions Create(FileReRoute reRoute) + public AuthenticationOptions Create(FileRoute route) { - return new AuthenticationOptions(reRoute.AuthenticationOptions.AllowedScopes, reRoute.AuthenticationOptions.AuthenticationProviderKey); - } + return new AuthenticationOptions(route.AuthenticationOptions.AllowedScopes, route.AuthenticationOptions.AuthenticationProviderKey); + } } -} +} diff --git a/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs index b3cced348..5547710f0 100644 --- a/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/ConfigurationCreator.cs @@ -1,61 +1,61 @@ -namespace Ocelot.Configuration.Creator -{ - using DependencyInjection; - using File; - using Microsoft.Extensions.DependencyInjection; - using System; - using System.Collections.Generic; - - public class ConfigurationCreator : IConfigurationCreator - { - private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator; - private readonly IQoSOptionsCreator _qosOptionsCreator; - private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; - private readonly IAdministrationPath _adminPath; - private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator; - private readonly IVersionCreator _versionCreator; - - public ConfigurationCreator( - IServiceProviderConfigurationCreator serviceProviderConfigCreator, - IQoSOptionsCreator qosOptionsCreator, - IHttpHandlerOptionsCreator httpHandlerOptionsCreator, - IServiceProvider serviceProvider, - ILoadBalancerOptionsCreator loadBalancerOptionsCreator, - IVersionCreator versionCreator - ) - { - _adminPath = serviceProvider.GetService(); - _loadBalancerOptionsCreator = loadBalancerOptionsCreator; - _serviceProviderConfigCreator = serviceProviderConfigCreator; - _qosOptionsCreator = qosOptionsCreator; - _httpHandlerOptionsCreator = httpHandlerOptionsCreator; - _versionCreator = versionCreator; - } - - public InternalConfiguration Create(FileConfiguration fileConfiguration, List reRoutes) - { - var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration); - - var lbOptions = _loadBalancerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.LoadBalancerOptions); - - var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions); - - var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions); - - var adminPath = _adminPath != null ? _adminPath.Path : null; - - var version = _versionCreator.Create(fileConfiguration.GlobalConfiguration.DownstreamHttpVersion); - - return new InternalConfiguration(reRoutes, - adminPath, - serviceProviderConfiguration, - fileConfiguration.GlobalConfiguration.RequestIdKey, - lbOptions, - fileConfiguration.GlobalConfiguration.DownstreamScheme, - qosOptions, - httpHandlerOptions, - version - ); - } - } -} +namespace Ocelot.Configuration.Creator +{ + using DependencyInjection; + using File; + using Microsoft.Extensions.DependencyInjection; + using System; + using System.Collections.Generic; + + public class ConfigurationCreator : IConfigurationCreator + { + private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator; + private readonly IQoSOptionsCreator _qosOptionsCreator; + private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; + private readonly IAdministrationPath _adminPath; + private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator; + private readonly IVersionCreator _versionCreator; + + public ConfigurationCreator( + IServiceProviderConfigurationCreator serviceProviderConfigCreator, + IQoSOptionsCreator qosOptionsCreator, + IHttpHandlerOptionsCreator httpHandlerOptionsCreator, + IServiceProvider serviceProvider, + ILoadBalancerOptionsCreator loadBalancerOptionsCreator, + IVersionCreator versionCreator + ) + { + _adminPath = serviceProvider.GetService(); + _loadBalancerOptionsCreator = loadBalancerOptionsCreator; + _serviceProviderConfigCreator = serviceProviderConfigCreator; + _qosOptionsCreator = qosOptionsCreator; + _httpHandlerOptionsCreator = httpHandlerOptionsCreator; + _versionCreator = versionCreator; + } + + public InternalConfiguration Create(FileConfiguration fileConfiguration, List routes) + { + var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration); + + var lbOptions = _loadBalancerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.LoadBalancerOptions); + + var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions); + + var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions); + + var adminPath = _adminPath != null ? _adminPath.Path : null; + + var version = _versionCreator.Create(fileConfiguration.GlobalConfiguration.DownstreamHttpVersion); + + return new InternalConfiguration(routes, + adminPath, + serviceProviderConfiguration, + fileConfiguration.GlobalConfiguration.RequestIdKey, + lbOptions, + fileConfiguration.GlobalConfiguration.DownstreamScheme, + qosOptions, + httpHandlerOptions, + version + ); + } + } +} diff --git a/src/Ocelot/Configuration/Creator/DownstreamAddressesCreator.cs b/src/Ocelot/Configuration/Creator/DownstreamAddressesCreator.cs index 78718c84e..6c3e8b0c6 100644 --- a/src/Ocelot/Configuration/Creator/DownstreamAddressesCreator.cs +++ b/src/Ocelot/Configuration/Creator/DownstreamAddressesCreator.cs @@ -6,9 +6,9 @@ namespace Ocelot.Configuration.Creator { public class DownstreamAddressesCreator : IDownstreamAddressesCreator { - public List Create(FileReRoute reRoute) + public List Create(FileRoute route) { - return reRoute.DownstreamHostAndPorts.Select(hostAndPort => new DownstreamHostAndPort(hostAndPort.Host, hostAndPort.Port)).ToList(); + return route.DownstreamHostAndPorts.Select(hostAndPort => new DownstreamHostAndPort(hostAndPort.Host, hostAndPort.Port)).ToList(); } } -} +} diff --git a/src/Ocelot/Configuration/Creator/DynamicsCreator.cs b/src/Ocelot/Configuration/Creator/DynamicsCreator.cs index 57b86746f..9d5fa19d4 100644 --- a/src/Ocelot/Configuration/Creator/DynamicsCreator.cs +++ b/src/Ocelot/Configuration/Creator/DynamicsCreator.cs @@ -1,47 +1,47 @@ -namespace Ocelot.Configuration.Creator -{ - using Builder; - using File; - using System.Collections.Generic; - using System.Linq; - - public class DynamicsCreator : IDynamicsCreator - { - private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; - private readonly IVersionCreator _versionCreator; - - public DynamicsCreator(IRateLimitOptionsCreator rateLimitOptionsCreator, IVersionCreator versionCreator) - { - _rateLimitOptionsCreator = rateLimitOptionsCreator; - _versionCreator = versionCreator; - } - - public List Create(FileConfiguration fileConfiguration) - { - return fileConfiguration.DynamicReRoutes - .Select(dynamic => SetUpDynamicReRoute(dynamic, fileConfiguration.GlobalConfiguration)) - .ToList(); - } - - private ReRoute SetUpDynamicReRoute(FileDynamicReRoute fileDynamicReRoute, FileGlobalConfiguration globalConfiguration) - { - var rateLimitOption = _rateLimitOptionsCreator - .Create(fileDynamicReRoute.RateLimitRule, globalConfiguration); - - var version = _versionCreator.Create(fileDynamicReRoute.DownstreamHttpVersion); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithEnableRateLimiting(rateLimitOption.EnableRateLimiting) - .WithRateLimitOptions(rateLimitOption) - .WithServiceName(fileDynamicReRoute.ServiceName) - .WithDownstreamHttpVersion(version) - .Build(); - - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .Build(); - - return reRoute; - } - } -} +namespace Ocelot.Configuration.Creator +{ + using Builder; + using File; + using System.Collections.Generic; + using System.Linq; + + public class DynamicsCreator : IDynamicsCreator + { + private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; + private readonly IVersionCreator _versionCreator; + + public DynamicsCreator(IRateLimitOptionsCreator rateLimitOptionsCreator, IVersionCreator versionCreator) + { + _rateLimitOptionsCreator = rateLimitOptionsCreator; + _versionCreator = versionCreator; + } + + public List Create(FileConfiguration fileConfiguration) + { + return fileConfiguration.DynamicRoutes + .Select(dynamic => SetUpDynamicRoute(dynamic, fileConfiguration.GlobalConfiguration)) + .ToList(); + } + + private Route SetUpDynamicRoute(FileDynamicRoute fileDynamicRoute, FileGlobalConfiguration globalConfiguration) + { + var rateLimitOption = _rateLimitOptionsCreator + .Create(fileDynamicRoute.RateLimitRule, globalConfiguration); + + var version = _versionCreator.Create(fileDynamicRoute.DownstreamHttpVersion); + + var downstreamRoute = new DownstreamRouteBuilder() + .WithEnableRateLimiting(rateLimitOption.EnableRateLimiting) + .WithRateLimitOptions(rateLimitOption) + .WithServiceName(fileDynamicRoute.ServiceName) + .WithDownstreamHttpVersion(version) + .Build(); + + var route = new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .Build(); + + return route; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs index 1a12cab53..82b581317 100644 --- a/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/FileInternalConfigurationCreator.cs @@ -1,57 +1,57 @@ -namespace Ocelot.Configuration.Creator -{ - using File; - using Responses; - using System.Linq; - using System.Threading.Tasks; - using Validator; - - public class FileInternalConfigurationCreator : IInternalConfigurationCreator - { - private readonly IConfigurationValidator _configurationValidator; - private readonly IConfigurationCreator _configCreator; - private readonly IDynamicsCreator _dynamicsCreator; - private readonly IReRoutesCreator _reRoutesCreator; - private readonly IAggregatesCreator _aggregatesCreator; - - public FileInternalConfigurationCreator( - IConfigurationValidator configurationValidator, - IReRoutesCreator reRoutesCreator, - IAggregatesCreator aggregatesCreator, - IDynamicsCreator dynamicsCreator, - IConfigurationCreator configCreator - ) - { - _configCreator = configCreator; - _dynamicsCreator = dynamicsCreator; - _aggregatesCreator = aggregatesCreator; - _reRoutesCreator = reRoutesCreator; - _configurationValidator = configurationValidator; - } - - public async Task> Create(FileConfiguration fileConfiguration) - { - var response = await _configurationValidator.IsValid(fileConfiguration); - - if (response.Data.IsError) - { - return new ErrorResponse(response.Data.Errors); - } - - var reRoutes = _reRoutesCreator.Create(fileConfiguration); - - var aggregates = _aggregatesCreator.Create(fileConfiguration, reRoutes); - - var dynamicReRoute = _dynamicsCreator.Create(fileConfiguration); - - var mergedReRoutes = reRoutes - .Union(aggregates) - .Union(dynamicReRoute) - .ToList(); - - var config = _configCreator.Create(fileConfiguration, mergedReRoutes); - - return new OkResponse(config); - } - } -} +namespace Ocelot.Configuration.Creator +{ + using File; + using Responses; + using System.Linq; + using System.Threading.Tasks; + using Validator; + + public class FileInternalConfigurationCreator : IInternalConfigurationCreator + { + private readonly IConfigurationValidator _configurationValidator; + private readonly IConfigurationCreator _configCreator; + private readonly IDynamicsCreator _dynamicsCreator; + private readonly IRoutesCreator _routesCreator; + private readonly IAggregatesCreator _aggregatesCreator; + + public FileInternalConfigurationCreator( + IConfigurationValidator configurationValidator, + IRoutesCreator routesCreator, + IAggregatesCreator aggregatesCreator, + IDynamicsCreator dynamicsCreator, + IConfigurationCreator configCreator + ) + { + _configCreator = configCreator; + _dynamicsCreator = dynamicsCreator; + _aggregatesCreator = aggregatesCreator; + _routesCreator = routesCreator; + _configurationValidator = configurationValidator; + } + + public async Task> Create(FileConfiguration fileConfiguration) + { + var response = await _configurationValidator.IsValid(fileConfiguration); + + if (response.Data.IsError) + { + return new ErrorResponse(response.Data.Errors); + } + + var routes = _routesCreator.Create(fileConfiguration); + + var aggregates = _aggregatesCreator.Create(fileConfiguration, routes); + + var dynamicRoute = _dynamicsCreator.Create(fileConfiguration); + + var mergedRoutes = routes + .Union(aggregates) + .Union(dynamicRoute) + .ToList(); + + var config = _configCreator.Create(fileConfiguration, mergedRoutes); + + return new OkResponse(config); + } + } +} diff --git a/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs b/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs index fdf05450b..950f91b7f 100644 --- a/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs +++ b/src/Ocelot/Configuration/Creator/HeaderFindAndReplaceCreator.cs @@ -18,12 +18,12 @@ public HeaderFindAndReplaceCreator(IPlaceholders placeholders, IOcelotLoggerFact _placeholders = placeholders; } - public HeaderTransformations Create(FileReRoute fileReRoute) + public HeaderTransformations Create(FileRoute fileRoute) { var upstream = new List(); var addHeadersToUpstream = new List(); - foreach (var input in fileReRoute.UpstreamHeaderTransform) + foreach (var input in fileRoute.UpstreamHeaderTransform) { if (input.Value.Contains(",")) { @@ -44,9 +44,9 @@ public HeaderTransformations Create(FileReRoute fileReRoute) } var downstream = new List(); - var addHeadersToDownstream = new List(); - - foreach (var input in fileReRoute.DownstreamHeaderTransform) + var addHeadersToDownstream = new List(); + + foreach (var input in fileRoute.DownstreamHeaderTransform) { if (input.Value.Contains(",")) { @@ -64,8 +64,8 @@ public HeaderTransformations Create(FileReRoute fileReRoute) { addHeadersToDownstream.Add(new AddHeader(input.Key, input.Value)); } - } - + } + return new HeaderTransformations(upstream, downstream, addHeadersToDownstream, addHeadersToUpstream); } @@ -78,8 +78,8 @@ private Response Map(KeyValuePair input) var startOfPlaceholder = replace.IndexOf("{"); if (startOfPlaceholder > -1) { - var endOfPlaceholder = replace.IndexOf("}", startOfPlaceholder); - + var endOfPlaceholder = replace.IndexOf("}", startOfPlaceholder); + var placeholder = replace.Substring(startOfPlaceholder, startOfPlaceholder + (endOfPlaceholder + 1)); var value = _placeholders.Get(placeholder); @@ -92,9 +92,9 @@ private Response Map(KeyValuePair input) replace = replace.Replace(placeholder, value.Data); } - var hAndr = new HeaderFindAndReplace(input.Key, findAndReplace[0], replace, 0); - + var hAndr = new HeaderFindAndReplace(input.Key, findAndReplace[0], replace, 0); + return new OkResponse(hAndr); } } -} +} diff --git a/src/Ocelot/Configuration/Creator/IAggregatesCreator.cs b/src/Ocelot/Configuration/Creator/IAggregatesCreator.cs index 05fe2177b..07559cddb 100644 --- a/src/Ocelot/Configuration/Creator/IAggregatesCreator.cs +++ b/src/Ocelot/Configuration/Creator/IAggregatesCreator.cs @@ -1,10 +1,10 @@ -using Ocelot.Configuration.File; -using System.Collections.Generic; - -namespace Ocelot.Configuration.Creator -{ - public interface IAggregatesCreator - { - List Create(FileConfiguration fileConfiguration, List reRoutes); - } -} +using Ocelot.Configuration.File; +using System.Collections.Generic; + +namespace Ocelot.Configuration.Creator +{ + public interface IAggregatesCreator + { + List Create(FileConfiguration fileConfiguration, List routes); + } +} diff --git a/src/Ocelot/Configuration/Creator/IAuthenticationOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IAuthenticationOptionsCreator.cs index 95636af5a..6edf1c63a 100644 --- a/src/Ocelot/Configuration/Creator/IAuthenticationOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/IAuthenticationOptionsCreator.cs @@ -4,6 +4,6 @@ namespace Ocelot.Configuration.Creator { public interface IAuthenticationOptionsCreator { - AuthenticationOptions Create(FileReRoute reRoute); + AuthenticationOptions Create(FileRoute route); } -} +} diff --git a/src/Ocelot/Configuration/Creator/IConfigurationCreator.cs b/src/Ocelot/Configuration/Creator/IConfigurationCreator.cs index 8aa42d0e0..373b943ff 100644 --- a/src/Ocelot/Configuration/Creator/IConfigurationCreator.cs +++ b/src/Ocelot/Configuration/Creator/IConfigurationCreator.cs @@ -1,10 +1,10 @@ -using Ocelot.Configuration.File; -using System.Collections.Generic; - -namespace Ocelot.Configuration.Creator -{ - public interface IConfigurationCreator - { - InternalConfiguration Create(FileConfiguration fileConfiguration, List reRoutes); - } -} +using Ocelot.Configuration.File; +using System.Collections.Generic; + +namespace Ocelot.Configuration.Creator +{ + public interface IConfigurationCreator + { + InternalConfiguration Create(FileConfiguration fileConfiguration, List routes); + } +} diff --git a/src/Ocelot/Configuration/Creator/IDownstreamAddressesCreator.cs b/src/Ocelot/Configuration/Creator/IDownstreamAddressesCreator.cs index e60812900..0f805ba12 100644 --- a/src/Ocelot/Configuration/Creator/IDownstreamAddressesCreator.cs +++ b/src/Ocelot/Configuration/Creator/IDownstreamAddressesCreator.cs @@ -5,6 +5,6 @@ namespace Ocelot.Configuration.Creator { public interface IDownstreamAddressesCreator { - List Create(FileReRoute reRoute); + List Create(FileRoute route); } -} +} diff --git a/src/Ocelot/Configuration/Creator/IDynamicsCreator.cs b/src/Ocelot/Configuration/Creator/IDynamicsCreator.cs index 715db0984..fe7bd0e34 100644 --- a/src/Ocelot/Configuration/Creator/IDynamicsCreator.cs +++ b/src/Ocelot/Configuration/Creator/IDynamicsCreator.cs @@ -1,10 +1,10 @@ -using Ocelot.Configuration.File; -using System.Collections.Generic; - -namespace Ocelot.Configuration.Creator -{ - public interface IDynamicsCreator - { - List Create(FileConfiguration fileConfiguration); - } -} +using Ocelot.Configuration.File; +using System.Collections.Generic; + +namespace Ocelot.Configuration.Creator +{ + public interface IDynamicsCreator + { + List Create(FileConfiguration fileConfiguration); + } +} diff --git a/src/Ocelot/Configuration/Creator/IHeaderFindAndReplaceCreator.cs b/src/Ocelot/Configuration/Creator/IHeaderFindAndReplaceCreator.cs index 496e17bf5..a37b1cca6 100644 --- a/src/Ocelot/Configuration/Creator/IHeaderFindAndReplaceCreator.cs +++ b/src/Ocelot/Configuration/Creator/IHeaderFindAndReplaceCreator.cs @@ -4,6 +4,6 @@ namespace Ocelot.Configuration.Creator { public interface IHeaderFindAndReplaceCreator { - HeaderTransformations Create(FileReRoute fileReRoute); + HeaderTransformations Create(FileRoute fileRoute); } -} +} diff --git a/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs index 76a9bc94a..21ecabade 100644 --- a/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/IHttpHandlerOptionsCreator.cs @@ -7,6 +7,6 @@ namespace Ocelot.Configuration.Creator /// public interface IHttpHandlerOptionsCreator { - HttpHandlerOptions Create(FileHttpHandlerOptions fileReRoute); + HttpHandlerOptions Create(FileHttpHandlerOptions fileRoute); } -} +} diff --git a/src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs b/src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs deleted file mode 100644 index 05f78354f..000000000 --- a/src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Ocelot.Configuration.File; - -namespace Ocelot.Configuration.Creator -{ - public interface IReRouteKeyCreator - { - string Create(FileReRoute fileReRoute); - } -} diff --git a/src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs deleted file mode 100644 index 1d5761d5e..000000000 --- a/src/Ocelot/Configuration/Creator/IReRouteOptionsCreator.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Ocelot.Configuration.File; - -namespace Ocelot.Configuration.Creator -{ - public interface IReRouteOptionsCreator - { - ReRouteOptions Create(FileReRoute fileReRoute); - } -} diff --git a/src/Ocelot/Configuration/Creator/IReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/IReRoutesCreator.cs index 18b26528a..1c3586e94 100644 --- a/src/Ocelot/Configuration/Creator/IReRoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/IReRoutesCreator.cs @@ -1,10 +1,10 @@ -using Ocelot.Configuration.File; -using System.Collections.Generic; - -namespace Ocelot.Configuration.Creator -{ - public interface IReRoutesCreator - { - List Create(FileConfiguration fileConfiguration); - } -} +using Ocelot.Configuration.File; +using System.Collections.Generic; + +namespace Ocelot.Configuration.Creator +{ + public interface IRoutesCreator + { + List Create(FileConfiguration fileConfiguration); + } +} diff --git a/src/Ocelot/Configuration/Creator/IRequestIdKeyCreator.cs b/src/Ocelot/Configuration/Creator/IRequestIdKeyCreator.cs index 898a653bb..779f5ba0b 100644 --- a/src/Ocelot/Configuration/Creator/IRequestIdKeyCreator.cs +++ b/src/Ocelot/Configuration/Creator/IRequestIdKeyCreator.cs @@ -4,6 +4,6 @@ namespace Ocelot.Configuration.Creator { public interface IRequestIdKeyCreator { - string Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration); + string Create(FileRoute fileRoute, FileGlobalConfiguration globalConfiguration); } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/Creator/IRouteKeyCreator.cs b/src/Ocelot/Configuration/Creator/IRouteKeyCreator.cs new file mode 100644 index 000000000..f94413181 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IRouteKeyCreator.cs @@ -0,0 +1,9 @@ +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface IRouteKeyCreator + { + string Create(FileRoute fileRoute); + } +} diff --git a/src/Ocelot/Configuration/Creator/IRouteOptionsCreator.cs b/src/Ocelot/Configuration/Creator/IRouteOptionsCreator.cs new file mode 100644 index 000000000..5cb38cc13 --- /dev/null +++ b/src/Ocelot/Configuration/Creator/IRouteOptionsCreator.cs @@ -0,0 +1,9 @@ +using Ocelot.Configuration.File; + +namespace Ocelot.Configuration.Creator +{ + public interface IRouteOptionsCreator + { + RouteOptions Create(FileRoute fileRoute); + } +} diff --git a/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs index aaec76345..9c27a823f 100644 --- a/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs +++ b/src/Ocelot/Configuration/Creator/IUpstreamTemplatePatternCreator.cs @@ -5,6 +5,6 @@ namespace Ocelot.Configuration.Creator { public interface IUpstreamTemplatePatternCreator { - UpstreamPathTemplate Create(IReRoute reRoute); + UpstreamPathTemplate Create(IRoute route); } -} +} diff --git a/src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs b/src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs deleted file mode 100644 index 44be1afa4..000000000 --- a/src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Ocelot.Configuration.File; -using Ocelot.LoadBalancer.LoadBalancers; -using System.Linq; - -namespace Ocelot.Configuration.Creator -{ - public class ReRouteKeyCreator : IReRouteKeyCreator - { - public string Create(FileReRoute fileReRoute) - { - if (IsStickySession(fileReRoute)) - { - return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}"; - } - - return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}|{string.Join(",", fileReRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}"; - } - - private bool IsStickySession(FileReRoute fileReRoute) - { - if (!string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Type) - && !string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Key) - && fileReRoute.LoadBalancerOptions.Type == nameof(CookieStickySessions)) - { - return true; - } - - return false; - } - } -} diff --git a/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs b/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs deleted file mode 100644 index ce710c8e0..000000000 --- a/src/Ocelot/Configuration/Creator/ReRouteOptionsCreator.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Ocelot.Configuration.Builder; -using Ocelot.Configuration.File; - -namespace Ocelot.Configuration.Creator -{ - public class ReRouteOptionsCreator : IReRouteOptionsCreator - { - public ReRouteOptions Create(FileReRoute fileReRoute) - { - var isAuthenticated = IsAuthenticated(fileReRoute); - var isAuthorised = IsAuthorised(fileReRoute); - var isCached = IsCached(fileReRoute); - var enableRateLimiting = IsEnableRateLimiting(fileReRoute); - var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName); - - var options = new ReRouteOptionsBuilder() - .WithIsAuthenticated(isAuthenticated) - .WithIsAuthorised(isAuthorised) - .WithIsCached(isCached) - .WithRateLimiting(enableRateLimiting) - .WithUseServiceDiscovery(useServiceDiscovery) - .Build(); - - return options; - } - - private static bool IsEnableRateLimiting(FileReRoute fileReRoute) - { - return (fileReRoute.RateLimitOptions != null && fileReRoute.RateLimitOptions.EnableRateLimiting) ? true : false; - } - - private bool IsAuthenticated(FileReRoute fileReRoute) - { - return !string.IsNullOrEmpty(fileReRoute.AuthenticationOptions?.AuthenticationProviderKey); - } - - private bool IsAuthorised(FileReRoute fileReRoute) - { - return fileReRoute.RouteClaimsRequirement?.Count > 0; - } - - private bool IsCached(FileReRoute fileReRoute) - { - return fileReRoute.FileCacheOptions.TtlSeconds > 0; - } - } -} diff --git a/src/Ocelot/Configuration/Creator/RequestIdKeyCreator.cs b/src/Ocelot/Configuration/Creator/RequestIdKeyCreator.cs index 8d4a81fea..d03712313 100644 --- a/src/Ocelot/Configuration/Creator/RequestIdKeyCreator.cs +++ b/src/Ocelot/Configuration/Creator/RequestIdKeyCreator.cs @@ -4,15 +4,15 @@ namespace Ocelot.Configuration.Creator { public class RequestIdKeyCreator : IRequestIdKeyCreator { - public string Create(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) + public string Create(FileRoute fileRoute, FileGlobalConfiguration globalConfiguration) { - var reRouteId = !string.IsNullOrEmpty(fileReRoute.RequestIdKey); - - var requestIdKey = reRouteId - ? fileReRoute.RequestIdKey - : globalConfiguration.RequestIdKey; - + var routeId = !string.IsNullOrEmpty(fileRoute.RequestIdKey); + + var requestIdKey = routeId + ? fileRoute.RequestIdKey + : globalConfiguration.RequestIdKey; + return requestIdKey; } } -} +} diff --git a/src/Ocelot/Configuration/Creator/RouteKeyCreator.cs b/src/Ocelot/Configuration/Creator/RouteKeyCreator.cs new file mode 100644 index 000000000..9cf9a891f --- /dev/null +++ b/src/Ocelot/Configuration/Creator/RouteKeyCreator.cs @@ -0,0 +1,31 @@ +using Ocelot.Configuration.File; +using Ocelot.LoadBalancer.LoadBalancers; +using System.Linq; + +namespace Ocelot.Configuration.Creator +{ + public class RouteKeyCreator : IRouteKeyCreator + { + public string Create(FileRoute fileRoute) + { + if (IsStickySession(fileRoute)) + { + return $"{nameof(CookieStickySessions)}:{fileRoute.LoadBalancerOptions.Key}"; + } + + return $"{fileRoute.UpstreamPathTemplate}|{string.Join(",", fileRoute.UpstreamHttpMethod)}|{string.Join(",", fileRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}"; + } + + private bool IsStickySession(FileRoute fileRoute) + { + if (!string.IsNullOrEmpty(fileRoute.LoadBalancerOptions.Type) + && !string.IsNullOrEmpty(fileRoute.LoadBalancerOptions.Key) + && fileRoute.LoadBalancerOptions.Type == nameof(CookieStickySessions)) + { + return true; + } + + return false; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/RouteOptionsCreator.cs b/src/Ocelot/Configuration/Creator/RouteOptionsCreator.cs new file mode 100644 index 000000000..506bd32aa --- /dev/null +++ b/src/Ocelot/Configuration/Creator/RouteOptionsCreator.cs @@ -0,0 +1,47 @@ +namespace Ocelot.Configuration.Creator +{ + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.File; + + public class RouteOptionsCreator : IRouteOptionsCreator + { + public RouteOptions Create(FileRoute fileRoute) + { + var isAuthenticated = IsAuthenticated(fileRoute); + var isAuthorised = IsAuthorised(fileRoute); + var isCached = IsCached(fileRoute); + var enableRateLimiting = IsEnableRateLimiting(fileRoute); + var useServiceDiscovery = !string.IsNullOrEmpty(fileRoute.ServiceName); + + var options = new RouteOptionsBuilder() + .WithIsAuthenticated(isAuthenticated) + .WithIsAuthorised(isAuthorised) + .WithIsCached(isCached) + .WithRateLimiting(enableRateLimiting) + .WithUseServiceDiscovery(useServiceDiscovery) + .Build(); + + return options; + } + + private static bool IsEnableRateLimiting(FileRoute fileRoute) + { + return (fileRoute.RateLimitOptions != null && fileRoute.RateLimitOptions.EnableRateLimiting) ? true : false; + } + + private bool IsAuthenticated(FileRoute fileRoute) + { + return !string.IsNullOrEmpty(fileRoute.AuthenticationOptions?.AuthenticationProviderKey); + } + + private bool IsAuthorised(FileRoute fileRoute) + { + return fileRoute.RouteClaimsRequirement?.Count > 0; + } + + private bool IsCached(FileRoute fileRoute) + { + return fileRoute.FileCacheOptions.TtlSeconds > 0; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/RoutesCreator.cs similarity index 58% rename from src/Ocelot/Configuration/Creator/ReRoutesCreator.cs rename to src/Ocelot/Configuration/Creator/RoutesCreator.cs index e307b6d34..a5b348624 100644 --- a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/RoutesCreator.cs @@ -1,167 +1,167 @@ -namespace Ocelot.Configuration.Creator -{ - using Builder; - using Cache; - using File; - using System.Collections.Generic; - using System.Linq; - - public class ReRoutesCreator : IReRoutesCreator - { - private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator; - private readonly IClaimsToThingCreator _claimsToThingCreator; - private readonly IAuthenticationOptionsCreator _authOptionsCreator; - private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator; - private readonly IRequestIdKeyCreator _requestIdKeyCreator; - private readonly IQoSOptionsCreator _qosOptionsCreator; - private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator; - private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; - private readonly IRegionCreator _regionCreator; - private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; - private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator; - private readonly IDownstreamAddressesCreator _downstreamAddressesCreator; - private readonly IReRouteKeyCreator _reRouteKeyCreator; - private readonly ISecurityOptionsCreator _securityOptionsCreator; - private readonly IVersionCreator _versionCreator; - - public ReRoutesCreator( - IClaimsToThingCreator claimsToThingCreator, - IAuthenticationOptionsCreator authOptionsCreator, - IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator, - IRequestIdKeyCreator requestIdKeyCreator, - IQoSOptionsCreator qosOptionsCreator, - IReRouteOptionsCreator fileReRouteOptionsCreator, - IRateLimitOptionsCreator rateLimitOptionsCreator, - IRegionCreator regionCreator, - IHttpHandlerOptionsCreator httpHandlerOptionsCreator, - IHeaderFindAndReplaceCreator headerFAndRCreator, - IDownstreamAddressesCreator downstreamAddressesCreator, - ILoadBalancerOptionsCreator loadBalancerOptionsCreator, - IReRouteKeyCreator reRouteKeyCreator, - ISecurityOptionsCreator securityOptionsCreator, - IVersionCreator versionCreator - ) - { - _reRouteKeyCreator = reRouteKeyCreator; - _loadBalancerOptionsCreator = loadBalancerOptionsCreator; - _downstreamAddressesCreator = downstreamAddressesCreator; - _headerFAndRCreator = headerFAndRCreator; - _regionCreator = regionCreator; - _rateLimitOptionsCreator = rateLimitOptionsCreator; - _requestIdKeyCreator = requestIdKeyCreator; - _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator; - _authOptionsCreator = authOptionsCreator; - _claimsToThingCreator = claimsToThingCreator; - _qosOptionsCreator = qosOptionsCreator; - _fileReRouteOptionsCreator = fileReRouteOptionsCreator; - _httpHandlerOptionsCreator = httpHandlerOptionsCreator; - _loadBalancerOptionsCreator = loadBalancerOptionsCreator; - _securityOptionsCreator = securityOptionsCreator; - _versionCreator = versionCreator; - } - - public List Create(FileConfiguration fileConfiguration) - { - return fileConfiguration.ReRoutes - .Select(reRoute => - { - var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration); - return SetUpReRoute(reRoute, downstreamReRoute); - }) - .ToList(); - } - - private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration) - { - var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute); - - var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration); - - var reRouteKey = _reRouteKeyCreator.Create(fileReRoute); - - var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); - - var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute); - - var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest); - - var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest); - - var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest); - - var claimsToDownstreamPath = _claimsToThingCreator.Create(fileReRoute.ChangeDownstreamPathTemplate); - - var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod); - - var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute.RateLimitOptions, globalConfiguration); - - var region = _regionCreator.Create(fileReRoute); - - var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions); - - var hAndRs = _headerFAndRCreator.Create(fileReRoute); - - var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute); - - var lbOptions = _loadBalancerOptionsCreator.Create(fileReRoute.LoadBalancerOptions); - - var securityOptions = _securityOptionsCreator.Create(fileReRoute.SecurityOptions); - - var downstreamHttpVersion = _versionCreator.Create(fileReRoute.DownstreamHttpVersion); - - var reRoute = new DownstreamReRouteBuilder() - .WithKey(fileReRoute.Key) - .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) - .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) - .WithUpstreamPathTemplate(upstreamTemplatePattern) - .WithIsAuthenticated(fileReRouteOptions.IsAuthenticated) - .WithAuthenticationOptions(authOptionsForRoute) - .WithClaimsToHeaders(claimsToHeaders) - .WithClaimsToClaims(claimsToClaims) - .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) - .WithIsAuthorised(fileReRouteOptions.IsAuthorised) - .WithClaimsToQueries(claimsToQueries) - .WithClaimsToDownstreamPath(claimsToDownstreamPath) - .WithRequestIdKey(requestIdKey) - .WithIsCached(fileReRouteOptions.IsCached) - .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region)) - .WithDownstreamScheme(fileReRoute.DownstreamScheme) - .WithLoadBalancerOptions(lbOptions) - .WithDownstreamAddresses(downstreamAddresses) - .WithLoadBalancerKey(reRouteKey) - .WithQosOptions(qosOptions) - .WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting) - .WithRateLimitOptions(rateLimitOption) - .WithHttpHandlerOptions(httpHandlerOptions) - .WithServiceName(fileReRoute.ServiceName) - .WithServiceNamespace(fileReRoute.ServiceNamespace) - .WithUseServiceDiscovery(fileReRouteOptions.UseServiceDiscovery) - .WithUpstreamHeaderFindAndReplace(hAndRs.Upstream) - .WithDownstreamHeaderFindAndReplace(hAndRs.Downstream) - .WithDelegatingHandlers(fileReRoute.DelegatingHandlers) - .WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream) - .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream) - .WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator) - .WithSecurityOptions(securityOptions) - .WithDownstreamHttpVersion(downstreamHttpVersion) - .WithDownStreamHttpMethod(fileReRoute.DownstreamHttpMethod) - .Build(); - - return reRoute; - } - - private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes) - { - var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute); - - var reRoute = new ReRouteBuilder() - .WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod) - .WithUpstreamPathTemplate(upstreamTemplatePattern) - .WithDownstreamReRoute(downstreamReRoutes) - .WithUpstreamHost(fileReRoute.UpstreamHost) - .Build(); - - return reRoute; - } - } -} +namespace Ocelot.Configuration.Creator +{ + using Ocelot.Configuration.Builder; + using Ocelot.Cache; + using Ocelot.Configuration.File; + using System.Collections.Generic; + using System.Linq; + + public class RoutesCreator : IRoutesCreator + { + private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator; + private readonly IClaimsToThingCreator _claimsToThingCreator; + private readonly IAuthenticationOptionsCreator _authOptionsCreator; + private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator; + private readonly IRequestIdKeyCreator _requestIdKeyCreator; + private readonly IQoSOptionsCreator _qosOptionsCreator; + private readonly IRouteOptionsCreator _fileRouteOptionsCreator; + private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator; + private readonly IRegionCreator _regionCreator; + private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; + private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator; + private readonly IDownstreamAddressesCreator _downstreamAddressesCreator; + private readonly IRouteKeyCreator _routeKeyCreator; + private readonly ISecurityOptionsCreator _securityOptionsCreator; + private readonly IVersionCreator _versionCreator; + + public RoutesCreator( + IClaimsToThingCreator claimsToThingCreator, + IAuthenticationOptionsCreator authOptionsCreator, + IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator, + IRequestIdKeyCreator requestIdKeyCreator, + IQoSOptionsCreator qosOptionsCreator, + IRouteOptionsCreator fileRouteOptionsCreator, + IRateLimitOptionsCreator rateLimitOptionsCreator, + IRegionCreator regionCreator, + IHttpHandlerOptionsCreator httpHandlerOptionsCreator, + IHeaderFindAndReplaceCreator headerFAndRCreator, + IDownstreamAddressesCreator downstreamAddressesCreator, + ILoadBalancerOptionsCreator loadBalancerOptionsCreator, + IRouteKeyCreator routeKeyCreator, + ISecurityOptionsCreator securityOptionsCreator, + IVersionCreator versionCreator + ) + { + _routeKeyCreator = routeKeyCreator; + _loadBalancerOptionsCreator = loadBalancerOptionsCreator; + _downstreamAddressesCreator = downstreamAddressesCreator; + _headerFAndRCreator = headerFAndRCreator; + _regionCreator = regionCreator; + _rateLimitOptionsCreator = rateLimitOptionsCreator; + _requestIdKeyCreator = requestIdKeyCreator; + _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator; + _authOptionsCreator = authOptionsCreator; + _claimsToThingCreator = claimsToThingCreator; + _qosOptionsCreator = qosOptionsCreator; + _fileRouteOptionsCreator = fileRouteOptionsCreator; + _httpHandlerOptionsCreator = httpHandlerOptionsCreator; + _loadBalancerOptionsCreator = loadBalancerOptionsCreator; + _securityOptionsCreator = securityOptionsCreator; + _versionCreator = versionCreator; + } + + public List Create(FileConfiguration fileConfiguration) + { + return fileConfiguration.Routes + .Select(route => + { + var downstreamRoute = SetUpDownstreamRoute(route, fileConfiguration.GlobalConfiguration); + return SetUpRoute(route, downstreamRoute); + }) + .ToList(); + } + + private DownstreamRoute SetUpDownstreamRoute(FileRoute fileRoute, FileGlobalConfiguration globalConfiguration) + { + var fileRouteOptions = _fileRouteOptionsCreator.Create(fileRoute); + + var requestIdKey = _requestIdKeyCreator.Create(fileRoute, globalConfiguration); + + var routeKey = _routeKeyCreator.Create(fileRoute); + + var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileRoute); + + var authOptionsForRoute = _authOptionsCreator.Create(fileRoute); + + var claimsToHeaders = _claimsToThingCreator.Create(fileRoute.AddHeadersToRequest); + + var claimsToClaims = _claimsToThingCreator.Create(fileRoute.AddClaimsToRequest); + + var claimsToQueries = _claimsToThingCreator.Create(fileRoute.AddQueriesToRequest); + + var claimsToDownstreamPath = _claimsToThingCreator.Create(fileRoute.ChangeDownstreamPathTemplate); + + var qosOptions = _qosOptionsCreator.Create(fileRoute.QoSOptions, fileRoute.UpstreamPathTemplate, fileRoute.UpstreamHttpMethod); + + var rateLimitOption = _rateLimitOptionsCreator.Create(fileRoute.RateLimitOptions, globalConfiguration); + + var region = _regionCreator.Create(fileRoute); + + var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileRoute.HttpHandlerOptions); + + var hAndRs = _headerFAndRCreator.Create(fileRoute); + + var downstreamAddresses = _downstreamAddressesCreator.Create(fileRoute); + + var lbOptions = _loadBalancerOptionsCreator.Create(fileRoute.LoadBalancerOptions); + + var securityOptions = _securityOptionsCreator.Create(fileRoute.SecurityOptions); + + var downstreamHttpVersion = _versionCreator.Create(fileRoute.DownstreamHttpVersion); + + var route = new DownstreamRouteBuilder() + .WithKey(fileRoute.Key) + .WithDownstreamPathTemplate(fileRoute.DownstreamPathTemplate) + .WithUpstreamHttpMethod(fileRoute.UpstreamHttpMethod) + .WithUpstreamPathTemplate(upstreamTemplatePattern) + .WithIsAuthenticated(fileRouteOptions.IsAuthenticated) + .WithAuthenticationOptions(authOptionsForRoute) + .WithClaimsToHeaders(claimsToHeaders) + .WithClaimsToClaims(claimsToClaims) + .WithRouteClaimsRequirement(fileRoute.RouteClaimsRequirement) + .WithIsAuthorised(fileRouteOptions.IsAuthorised) + .WithClaimsToQueries(claimsToQueries) + .WithClaimsToDownstreamPath(claimsToDownstreamPath) + .WithRequestIdKey(requestIdKey) + .WithIsCached(fileRouteOptions.IsCached) + .WithCacheOptions(new CacheOptions(fileRoute.FileCacheOptions.TtlSeconds, region)) + .WithDownstreamScheme(fileRoute.DownstreamScheme) + .WithLoadBalancerOptions(lbOptions) + .WithDownstreamAddresses(downstreamAddresses) + .WithLoadBalancerKey(routeKey) + .WithQosOptions(qosOptions) + .WithEnableRateLimiting(fileRouteOptions.EnableRateLimiting) + .WithRateLimitOptions(rateLimitOption) + .WithHttpHandlerOptions(httpHandlerOptions) + .WithServiceName(fileRoute.ServiceName) + .WithServiceNamespace(fileRoute.ServiceNamespace) + .WithUseServiceDiscovery(fileRouteOptions.UseServiceDiscovery) + .WithUpstreamHeaderFindAndReplace(hAndRs.Upstream) + .WithDownstreamHeaderFindAndReplace(hAndRs.Downstream) + .WithDelegatingHandlers(fileRoute.DelegatingHandlers) + .WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream) + .WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream) + .WithDangerousAcceptAnyServerCertificateValidator(fileRoute.DangerousAcceptAnyServerCertificateValidator) + .WithSecurityOptions(securityOptions) + .WithDownstreamHttpVersion(downstreamHttpVersion) + .WithDownStreamHttpMethod(fileRoute.DownstreamHttpMethod) + .Build(); + + return route; + } + + private Route SetUpRoute(FileRoute fileRoute, DownstreamRoute downstreamRoutes) + { + var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileRoute); + + var route = new RouteBuilder() + .WithUpstreamHttpMethod(fileRoute.UpstreamHttpMethod) + .WithUpstreamPathTemplate(upstreamTemplatePattern) + .WithDownstreamRoute(downstreamRoutes) + .WithUpstreamHost(fileRoute.UpstreamHost) + .Build(); + + return route; + } + } +} diff --git a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs index 7de660b7d..668e42e21 100644 --- a/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs +++ b/src/Ocelot/Configuration/Creator/UpstreamTemplatePatternCreator.cs @@ -1,93 +1,93 @@ -using Ocelot.Configuration.File; -using Ocelot.Values; -using System.Collections.Generic; - -namespace Ocelot.Configuration.Creator -{ - public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator - { - private const string RegExMatchOneOrMoreOfEverything = ".+"; - private const string RegExMatchOneOrMoreOfEverythingUntilNextForwardSlash = "[^/]+"; - private const string RegExMatchEndString = "$"; - private const string RegExIgnoreCase = "(?i)"; - private const string RegExForwardSlashOnly = "^/$"; - private const string RegExForwardSlashAndOnePlaceHolder = "^/.*"; - - public UpstreamPathTemplate Create(IReRoute reRoute) - { - var upstreamTemplate = reRoute.UpstreamPathTemplate; - - var placeholders = new List(); - - for (var i = 0; i < upstreamTemplate.Length; i++) - { - if (IsPlaceHolder(upstreamTemplate, i)) - { - var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i); - var difference = postitionOfPlaceHolderClosingBracket - i + 1; - var placeHolderName = upstreamTemplate.Substring(i, difference); - placeholders.Add(placeHolderName); - - //hack to handle /{url} case - if (ForwardSlashAndOnePlaceHolder(upstreamTemplate, placeholders, postitionOfPlaceHolderClosingBracket)) - { - return new UpstreamPathTemplate(RegExForwardSlashAndOnePlaceHolder, 0, false, reRoute.UpstreamPathTemplate); - } - } - } - - var containsQueryString = false; - - if (upstreamTemplate.Contains("?")) - { - containsQueryString = true; - upstreamTemplate = upstreamTemplate.Replace("?", "\\?"); - } - - for (int i = 0; i < placeholders.Count; i++) - { - var indexOfPlaceholder = upstreamTemplate.IndexOf(placeholders[i]); - var indexOfNextForwardSlash = upstreamTemplate.IndexOf("/", indexOfPlaceholder); - if (indexOfNextForwardSlash < indexOfPlaceholder || (containsQueryString && upstreamTemplate.IndexOf("?") < upstreamTemplate.IndexOf(placeholders[i]))) - { - upstreamTemplate = upstreamTemplate.Replace(placeholders[i], RegExMatchOneOrMoreOfEverything); - } - else - { - upstreamTemplate = upstreamTemplate.Replace(placeholders[i], RegExMatchOneOrMoreOfEverythingUntilNextForwardSlash); - } - } - - if (upstreamTemplate == "/") - { - return new UpstreamPathTemplate(RegExForwardSlashOnly, reRoute.Priority, containsQueryString, reRoute.UpstreamPathTemplate); - } - - if (upstreamTemplate.EndsWith("/")) - { - upstreamTemplate = upstreamTemplate.Remove(upstreamTemplate.Length - 1, 1) + "(/|)"; - } - - var route = reRoute.ReRouteIsCaseSensitive - ? $"^{upstreamTemplate}{RegExMatchEndString}" - : $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}"; - - return new UpstreamPathTemplate(route, reRoute.Priority, containsQueryString, reRoute.UpstreamPathTemplate); - } - - private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List placeholders, int postitionOfPlaceHolderClosingBracket) - { - if (upstreamTemplate.Substring(0, 2) == "/{" && placeholders.Count == 1 && upstreamTemplate.Length == postitionOfPlaceHolderClosingBracket + 1) - { - return true; - } - - return false; - } - - private bool IsPlaceHolder(string upstreamTemplate, int i) - { - return upstreamTemplate[i] == '{'; - } - } -} +using Ocelot.Configuration.File; +using Ocelot.Values; +using System.Collections.Generic; + +namespace Ocelot.Configuration.Creator +{ + public class UpstreamTemplatePatternCreator : IUpstreamTemplatePatternCreator + { + private const string RegExMatchOneOrMoreOfEverything = ".+"; + private const string RegExMatchOneOrMoreOfEverythingUntilNextForwardSlash = "[^/]+"; + private const string RegExMatchEndString = "$"; + private const string RegExIgnoreCase = "(?i)"; + private const string RegExForwardSlashOnly = "^/$"; + private const string RegExForwardSlashAndOnePlaceHolder = "^/.*"; + + public UpstreamPathTemplate Create(IRoute route) + { + var upstreamTemplate = route.UpstreamPathTemplate; + + var placeholders = new List(); + + for (var i = 0; i < upstreamTemplate.Length; i++) + { + if (IsPlaceHolder(upstreamTemplate, i)) + { + var postitionOfPlaceHolderClosingBracket = upstreamTemplate.IndexOf('}', i); + var difference = postitionOfPlaceHolderClosingBracket - i + 1; + var placeHolderName = upstreamTemplate.Substring(i, difference); + placeholders.Add(placeHolderName); + + //hack to handle /{url} case + if (ForwardSlashAndOnePlaceHolder(upstreamTemplate, placeholders, postitionOfPlaceHolderClosingBracket)) + { + return new UpstreamPathTemplate(RegExForwardSlashAndOnePlaceHolder, 0, false, route.UpstreamPathTemplate); + } + } + } + + var containsQueryString = false; + + if (upstreamTemplate.Contains("?")) + { + containsQueryString = true; + upstreamTemplate = upstreamTemplate.Replace("?", "\\?"); + } + + for (int i = 0; i < placeholders.Count; i++) + { + var indexOfPlaceholder = upstreamTemplate.IndexOf(placeholders[i]); + var indexOfNextForwardSlash = upstreamTemplate.IndexOf("/", indexOfPlaceholder); + if (indexOfNextForwardSlash < indexOfPlaceholder || (containsQueryString && upstreamTemplate.IndexOf("?") < upstreamTemplate.IndexOf(placeholders[i]))) + { + upstreamTemplate = upstreamTemplate.Replace(placeholders[i], RegExMatchOneOrMoreOfEverything); + } + else + { + upstreamTemplate = upstreamTemplate.Replace(placeholders[i], RegExMatchOneOrMoreOfEverythingUntilNextForwardSlash); + } + } + + if (upstreamTemplate == "/") + { + return new UpstreamPathTemplate(RegExForwardSlashOnly, route.Priority, containsQueryString, route.UpstreamPathTemplate); + } + + if (upstreamTemplate.EndsWith("/")) + { + upstreamTemplate = upstreamTemplate.Remove(upstreamTemplate.Length - 1, 1) + "(/|)"; + } + + var template = route.RouteIsCaseSensitive + ? $"^{upstreamTemplate}{RegExMatchEndString}" + : $"^{RegExIgnoreCase}{upstreamTemplate}{RegExMatchEndString}"; + + return new UpstreamPathTemplate(template, route.Priority, containsQueryString, route.UpstreamPathTemplate); + } + + private bool ForwardSlashAndOnePlaceHolder(string upstreamTemplate, List placeholders, int postitionOfPlaceHolderClosingBracket) + { + if (upstreamTemplate.Substring(0, 2) == "/{" && placeholders.Count == 1 && upstreamTemplate.Length == postitionOfPlaceHolderClosingBracket + 1) + { + return true; + } + + return false; + } + + private bool IsPlaceHolder(string upstreamTemplate, int i) + { + return upstreamTemplate[i] == '{'; + } + } +} diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamRoute.cs similarity index 95% rename from src/Ocelot/Configuration/DownstreamReRoute.cs rename to src/Ocelot/Configuration/DownstreamRoute.cs index be044539d..8dd9f30dd 100644 --- a/src/Ocelot/Configuration/DownstreamReRoute.cs +++ b/src/Ocelot/Configuration/DownstreamRoute.cs @@ -1,117 +1,117 @@ -namespace Ocelot.Configuration -{ - using Creator; - using System; - using System.Collections.Generic; - using Values; - - public class DownstreamReRoute - { - public DownstreamReRoute( - string key, - UpstreamPathTemplate upstreamPathTemplate, - List upstreamHeadersFindAndReplace, - List downstreamHeadersFindAndReplace, - List downstreamAddresses, - string serviceName, - string serviceNamespace, - HttpHandlerOptions httpHandlerOptions, - bool useServiceDiscovery, - bool enableEndpointEndpointRateLimiting, - QoSOptions qosOptions, - string downstreamScheme, - string requestIdKey, - bool isCached, - CacheOptions cacheOptions, - LoadBalancerOptions loadBalancerOptions, - RateLimitOptions rateLimitOptions, - Dictionary routeClaimsRequirement, - List claimsToQueries, - List claimsToHeaders, - List claimsToClaims, - List claimsToPath, - bool isAuthenticated, - bool isAuthorised, - AuthenticationOptions authenticationOptions, - DownstreamPathTemplate downstreamPathTemplate, - string loadBalancerKey, - List delegatingHandlers, - List addHeadersToDownstream, - List addHeadersToUpstream, - bool dangerousAcceptAnyServerCertificateValidator, - SecurityOptions securityOptions, - string downstreamHttpMethod, - Version downstreamHttpVersion) - { - DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator; - AddHeadersToDownstream = addHeadersToDownstream; - DelegatingHandlers = delegatingHandlers; - Key = key; - UpstreamPathTemplate = upstreamPathTemplate; - UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List(); - DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List(); - DownstreamAddresses = downstreamAddresses ?? new List(); - ServiceName = serviceName; - ServiceNamespace = serviceNamespace; - HttpHandlerOptions = httpHandlerOptions; - UseServiceDiscovery = useServiceDiscovery; - EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting; - QosOptions = qosOptions; - DownstreamScheme = downstreamScheme; - RequestIdKey = requestIdKey; - IsCached = isCached; - CacheOptions = cacheOptions; - LoadBalancerOptions = loadBalancerOptions; - RateLimitOptions = rateLimitOptions; - RouteClaimsRequirement = routeClaimsRequirement; - ClaimsToQueries = claimsToQueries ?? new List(); - ClaimsToHeaders = claimsToHeaders ?? new List(); - ClaimsToClaims = claimsToClaims ?? new List(); - ClaimsToPath = claimsToPath ?? new List(); - IsAuthenticated = isAuthenticated; - IsAuthorised = isAuthorised; - AuthenticationOptions = authenticationOptions; - DownstreamPathTemplate = downstreamPathTemplate; - LoadBalancerKey = loadBalancerKey; - AddHeadersToUpstream = addHeadersToUpstream; - SecurityOptions = securityOptions; - DownstreamHttpMethod = downstreamHttpMethod; - DownstreamHttpVersion = downstreamHttpVersion; - } - - public string Key { get; } - public UpstreamPathTemplate UpstreamPathTemplate { get; } - public List UpstreamHeadersFindAndReplace { get; } - public List DownstreamHeadersFindAndReplace { get; } - public List DownstreamAddresses { get; } - public string ServiceName { get; } - public string ServiceNamespace { get; } - public HttpHandlerOptions HttpHandlerOptions { get; } - public bool UseServiceDiscovery { get; } - public bool EnableEndpointEndpointRateLimiting { get; } - public QoSOptions QosOptions { get; } - public string DownstreamScheme { get; } - public string RequestIdKey { get; } - public bool IsCached { get; } - public CacheOptions CacheOptions { get; } - public LoadBalancerOptions LoadBalancerOptions { get; } - public RateLimitOptions RateLimitOptions { get; } - public Dictionary RouteClaimsRequirement { get; } - public List ClaimsToQueries { get; } - public List ClaimsToHeaders { get; } - public List ClaimsToClaims { get; } - public List ClaimsToPath { get; } - public bool IsAuthenticated { get; } - public bool IsAuthorised { get; } - public AuthenticationOptions AuthenticationOptions { get; } - public DownstreamPathTemplate DownstreamPathTemplate { get; } - public string LoadBalancerKey { get; } - public List DelegatingHandlers { get; } - public List AddHeadersToDownstream { get; } - public List AddHeadersToUpstream { get; } - public bool DangerousAcceptAnyServerCertificateValidator { get; } - public SecurityOptions SecurityOptions { get; } - public string DownstreamHttpMethod { get; } - public Version DownstreamHttpVersion { get; } - } -} +namespace Ocelot.Configuration +{ + using Ocelot.Configuration.Creator; + using System; + using System.Collections.Generic; + using Ocelot.Values; + + public class DownstreamRoute + { + public DownstreamRoute( + string key, + UpstreamPathTemplate upstreamPathTemplate, + List upstreamHeadersFindAndReplace, + List downstreamHeadersFindAndReplace, + List downstreamAddresses, + string serviceName, + string serviceNamespace, + HttpHandlerOptions httpHandlerOptions, + bool useServiceDiscovery, + bool enableEndpointEndpointRateLimiting, + QoSOptions qosOptions, + string downstreamScheme, + string requestIdKey, + bool isCached, + CacheOptions cacheOptions, + LoadBalancerOptions loadBalancerOptions, + RateLimitOptions rateLimitOptions, + Dictionary routeClaimsRequirement, + List claimsToQueries, + List claimsToHeaders, + List claimsToClaims, + List claimsToPath, + bool isAuthenticated, + bool isAuthorised, + AuthenticationOptions authenticationOptions, + DownstreamPathTemplate downstreamPathTemplate, + string loadBalancerKey, + List delegatingHandlers, + List addHeadersToDownstream, + List addHeadersToUpstream, + bool dangerousAcceptAnyServerCertificateValidator, + SecurityOptions securityOptions, + string downstreamHttpMethod, + Version downstreamHttpVersion) + { + DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator; + AddHeadersToDownstream = addHeadersToDownstream; + DelegatingHandlers = delegatingHandlers; + Key = key; + UpstreamPathTemplate = upstreamPathTemplate; + UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List(); + DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List(); + DownstreamAddresses = downstreamAddresses ?? new List(); + ServiceName = serviceName; + ServiceNamespace = serviceNamespace; + HttpHandlerOptions = httpHandlerOptions; + UseServiceDiscovery = useServiceDiscovery; + EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting; + QosOptions = qosOptions; + DownstreamScheme = downstreamScheme; + RequestIdKey = requestIdKey; + IsCached = isCached; + CacheOptions = cacheOptions; + LoadBalancerOptions = loadBalancerOptions; + RateLimitOptions = rateLimitOptions; + RouteClaimsRequirement = routeClaimsRequirement; + ClaimsToQueries = claimsToQueries ?? new List(); + ClaimsToHeaders = claimsToHeaders ?? new List(); + ClaimsToClaims = claimsToClaims ?? new List(); + ClaimsToPath = claimsToPath ?? new List(); + IsAuthenticated = isAuthenticated; + IsAuthorised = isAuthorised; + AuthenticationOptions = authenticationOptions; + DownstreamPathTemplate = downstreamPathTemplate; + LoadBalancerKey = loadBalancerKey; + AddHeadersToUpstream = addHeadersToUpstream; + SecurityOptions = securityOptions; + DownstreamHttpMethod = downstreamHttpMethod; + DownstreamHttpVersion = downstreamHttpVersion; + } + + public string Key { get; } + public UpstreamPathTemplate UpstreamPathTemplate { get; } + public List UpstreamHeadersFindAndReplace { get; } + public List DownstreamHeadersFindAndReplace { get; } + public List DownstreamAddresses { get; } + public string ServiceName { get; } + public string ServiceNamespace { get; } + public HttpHandlerOptions HttpHandlerOptions { get; } + public bool UseServiceDiscovery { get; } + public bool EnableEndpointEndpointRateLimiting { get; } + public QoSOptions QosOptions { get; } + public string DownstreamScheme { get; } + public string RequestIdKey { get; } + public bool IsCached { get; } + public CacheOptions CacheOptions { get; } + public LoadBalancerOptions LoadBalancerOptions { get; } + public RateLimitOptions RateLimitOptions { get; } + public Dictionary RouteClaimsRequirement { get; } + public List ClaimsToQueries { get; } + public List ClaimsToHeaders { get; } + public List ClaimsToClaims { get; } + public List ClaimsToPath { get; } + public bool IsAuthenticated { get; } + public bool IsAuthorised { get; } + public AuthenticationOptions AuthenticationOptions { get; } + public DownstreamPathTemplate DownstreamPathTemplate { get; } + public string LoadBalancerKey { get; } + public List DelegatingHandlers { get; } + public List AddHeadersToDownstream { get; } + public List AddHeadersToUpstream { get; } + public bool DangerousAcceptAnyServerCertificateValidator { get; } + public SecurityOptions SecurityOptions { get; } + public string DownstreamHttpMethod { get; } + public Version DownstreamHttpVersion { get; } + } +} diff --git a/src/Ocelot/Configuration/File/AggregateReRouteConfig.cs b/src/Ocelot/Configuration/File/AggregateRouteConfig.cs similarity index 61% rename from src/Ocelot/Configuration/File/AggregateReRouteConfig.cs rename to src/Ocelot/Configuration/File/AggregateRouteConfig.cs index 6519d6b5a..c63ed6b4a 100644 --- a/src/Ocelot/Configuration/File/AggregateReRouteConfig.cs +++ b/src/Ocelot/Configuration/File/AggregateRouteConfig.cs @@ -1,9 +1,9 @@ -namespace Ocelot.Configuration.File -{ - public class AggregateReRouteConfig - { - public string ReRouteKey { get; set; } - public string Parameter { get; set; } - public string JsonPath { get; set; } - } -} +namespace Ocelot.Configuration.File +{ + public class AggregateRouteConfig + { + public string RouteKey { get; set; } + public string Parameter { get; set; } + public string JsonPath { get; set; } + } +} diff --git a/src/Ocelot/Configuration/File/FileAggregateReRoute.cs b/src/Ocelot/Configuration/File/FileAggregateRoute.cs similarity index 61% rename from src/Ocelot/Configuration/File/FileAggregateReRoute.cs rename to src/Ocelot/Configuration/File/FileAggregateRoute.cs index 2f1b4676e..6701ee33f 100644 --- a/src/Ocelot/Configuration/File/FileAggregateReRoute.cs +++ b/src/Ocelot/Configuration/File/FileAggregateRoute.cs @@ -1,22 +1,22 @@ -using System.Collections.Generic; - -namespace Ocelot.Configuration.File +namespace Ocelot.Configuration.File { - public class FileAggregateReRoute : IReRoute - { - public List ReRouteKeys { get; set; } - public List ReRouteKeysConfig { get; set; } - public string UpstreamPathTemplate { get; set; } - public string UpstreamHost { get; set; } - public bool ReRouteIsCaseSensitive { get; set; } - public string Aggregator { get; set; } - - // Only supports GET..are you crazy!! POST, PUT WOULD BE CRAZY!! :) - public List UpstreamHttpMethod - { - get { return new List { "Get" }; } - } - - public int Priority { get; set; } = 1; - } -} + using System.Collections.Generic; + + public class FileAggregateRoute : IRoute + { + public List RouteKeys { get; set; } + public List RouteKeysConfig { get; set; } + public string UpstreamPathTemplate { get; set; } + public string UpstreamHost { get; set; } + public bool RouteIsCaseSensitive { get; set; } + public string Aggregator { get; set; } + + // Only supports GET..are you crazy!! POST, PUT WOULD BE CRAZY!! :) + public List UpstreamHttpMethod + { + get { return new List { "Get" }; } + } + + public int Priority { get; set; } = 1; + } +} diff --git a/src/Ocelot/Configuration/File/FileConfiguration.cs b/src/Ocelot/Configuration/File/FileConfiguration.cs index ecd41a352..b517f298b 100644 --- a/src/Ocelot/Configuration/File/FileConfiguration.cs +++ b/src/Ocelot/Configuration/File/FileConfiguration.cs @@ -1,23 +1,23 @@ -using System.Collections.Generic; - -namespace Ocelot.Configuration.File -{ - public class FileConfiguration - { - public FileConfiguration() - { - ReRoutes = new List(); - GlobalConfiguration = new FileGlobalConfiguration(); - Aggregates = new List(); - DynamicReRoutes = new List(); - } - - public List ReRoutes { get; set; } - public List DynamicReRoutes { get; set; } - - // Seperate field for aggregates because this let's you re-use ReRoutes in multiple Aggregates - public List Aggregates { get; set; } - - public FileGlobalConfiguration GlobalConfiguration { get; set; } - } -} +using System.Collections.Generic; + +namespace Ocelot.Configuration.File +{ + public class FileConfiguration + { + public FileConfiguration() + { + Routes = new List(); + GlobalConfiguration = new FileGlobalConfiguration(); + Aggregates = new List(); + DynamicRoutes = new List(); + } + + public List Routes { get; set; } + public List DynamicRoutes { get; set; } + + // Seperate field for aggregates because this let's you re-use Routes in multiple Aggregates + public List Aggregates { get; set; } + + public FileGlobalConfiguration GlobalConfiguration { get; set; } + } +} diff --git a/src/Ocelot/Configuration/File/FileDynamicReRoute.cs b/src/Ocelot/Configuration/File/FileDynamicRoute.cs similarity index 83% rename from src/Ocelot/Configuration/File/FileDynamicReRoute.cs rename to src/Ocelot/Configuration/File/FileDynamicRoute.cs index 7657aa89b..fb93c2f91 100644 --- a/src/Ocelot/Configuration/File/FileDynamicReRoute.cs +++ b/src/Ocelot/Configuration/File/FileDynamicRoute.cs @@ -1,9 +1,9 @@ -namespace Ocelot.Configuration.File -{ - public class FileDynamicReRoute - { - public string ServiceName { get; set; } - public FileRateLimitRule RateLimitRule { get; set; } - public string DownstreamHttpVersion { get; set; } - } -} +namespace Ocelot.Configuration.File +{ + public class FileDynamicRoute + { + public string ServiceName { get; set; } + public FileRateLimitRule RateLimitRule { get; set; } + public string DownstreamHttpVersion { get; set; } + } +} diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileRoute.cs similarity index 92% rename from src/Ocelot/Configuration/File/FileReRoute.cs rename to src/Ocelot/Configuration/File/FileRoute.cs index b1a6cd4da..cbaec79ff 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileRoute.cs @@ -1,61 +1,61 @@ -using System.Collections.Generic; - -namespace Ocelot.Configuration.File +namespace Ocelot.Configuration.File { - public class FileReRoute : IReRoute - { - public FileReRoute() - { - UpstreamHttpMethod = new List(); - AddHeadersToRequest = new Dictionary(); - AddClaimsToRequest = new Dictionary(); - RouteClaimsRequirement = new Dictionary(); - AddQueriesToRequest = new Dictionary(); - ChangeDownstreamPathTemplate = new Dictionary(); - DownstreamHeaderTransform = new Dictionary(); - FileCacheOptions = new FileCacheOptions(); - QoSOptions = new FileQoSOptions(); - RateLimitOptions = new FileRateLimitRule(); - AuthenticationOptions = new FileAuthenticationOptions(); - HttpHandlerOptions = new FileHttpHandlerOptions(); - UpstreamHeaderTransform = new Dictionary(); - DownstreamHostAndPorts = new List(); - DelegatingHandlers = new List(); - LoadBalancerOptions = new FileLoadBalancerOptions(); - SecurityOptions = new FileSecurityOptions(); - Priority = 1; - } - - public string DownstreamPathTemplate { get; set; } - public string UpstreamPathTemplate { get; set; } - public List UpstreamHttpMethod { get; set; } - public string DownstreamHttpMethod { get; set; } - public Dictionary AddHeadersToRequest { get; set; } - public Dictionary UpstreamHeaderTransform { get; set; } - public Dictionary DownstreamHeaderTransform { get; set; } - public Dictionary AddClaimsToRequest { get; set; } - public Dictionary RouteClaimsRequirement { get; set; } - public Dictionary AddQueriesToRequest { get; set; } - public Dictionary ChangeDownstreamPathTemplate { get; set; } - public string RequestIdKey { get; set; } - public FileCacheOptions FileCacheOptions { get; set; } - public bool ReRouteIsCaseSensitive { get; set; } - public string ServiceName { get; set; } - public string ServiceNamespace { get; set; } - public string DownstreamScheme { get; set; } - public FileQoSOptions QoSOptions { get; set; } - public FileLoadBalancerOptions LoadBalancerOptions { get; set; } - public FileRateLimitRule RateLimitOptions { get; set; } - public FileAuthenticationOptions AuthenticationOptions { get; set; } - public FileHttpHandlerOptions HttpHandlerOptions { get; set; } - public List DownstreamHostAndPorts { get; set; } - public string UpstreamHost { get; set; } - public string Key { get; set; } - public List DelegatingHandlers { get; set; } - public int Priority { get; set; } - public int Timeout { get; set; } - public bool DangerousAcceptAnyServerCertificateValidator { get; set; } - public FileSecurityOptions SecurityOptions { get; set; } - public string DownstreamHttpVersion { get; set; } - } -} + using System.Collections.Generic; + + public class FileRoute : IRoute + { + public FileRoute() + { + UpstreamHttpMethod = new List(); + AddHeadersToRequest = new Dictionary(); + AddClaimsToRequest = new Dictionary(); + RouteClaimsRequirement = new Dictionary(); + AddQueriesToRequest = new Dictionary(); + ChangeDownstreamPathTemplate = new Dictionary(); + DownstreamHeaderTransform = new Dictionary(); + FileCacheOptions = new FileCacheOptions(); + QoSOptions = new FileQoSOptions(); + RateLimitOptions = new FileRateLimitRule(); + AuthenticationOptions = new FileAuthenticationOptions(); + HttpHandlerOptions = new FileHttpHandlerOptions(); + UpstreamHeaderTransform = new Dictionary(); + DownstreamHostAndPorts = new List(); + DelegatingHandlers = new List(); + LoadBalancerOptions = new FileLoadBalancerOptions(); + SecurityOptions = new FileSecurityOptions(); + Priority = 1; + } + + public string DownstreamPathTemplate { get; set; } + public string UpstreamPathTemplate { get; set; } + public List UpstreamHttpMethod { get; set; } + public string DownstreamHttpMethod { get; set; } + public Dictionary AddHeadersToRequest { get; set; } + public Dictionary UpstreamHeaderTransform { get; set; } + public Dictionary DownstreamHeaderTransform { get; set; } + public Dictionary AddClaimsToRequest { get; set; } + public Dictionary RouteClaimsRequirement { get; set; } + public Dictionary AddQueriesToRequest { get; set; } + public Dictionary ChangeDownstreamPathTemplate { get; set; } + public string RequestIdKey { get; set; } + public FileCacheOptions FileCacheOptions { get; set; } + public bool RouteIsCaseSensitive { get; set; } + public string ServiceName { get; set; } + public string ServiceNamespace { get; set; } + public string DownstreamScheme { get; set; } + public FileQoSOptions QoSOptions { get; set; } + public FileLoadBalancerOptions LoadBalancerOptions { get; set; } + public FileRateLimitRule RateLimitOptions { get; set; } + public FileAuthenticationOptions AuthenticationOptions { get; set; } + public FileHttpHandlerOptions HttpHandlerOptions { get; set; } + public List DownstreamHostAndPorts { get; set; } + public string UpstreamHost { get; set; } + public string Key { get; set; } + public List DelegatingHandlers { get; set; } + public int Priority { get; set; } + public int Timeout { get; set; } + public bool DangerousAcceptAnyServerCertificateValidator { get; set; } + public FileSecurityOptions SecurityOptions { get; set; } + public string DownstreamHttpVersion { get; set; } + } +} diff --git a/src/Ocelot/Configuration/File/IReRoute.cs b/src/Ocelot/Configuration/File/IReRoute.cs index b51914283..9c64ce2c9 100644 --- a/src/Ocelot/Configuration/File/IReRoute.cs +++ b/src/Ocelot/Configuration/File/IReRoute.cs @@ -1,9 +1,9 @@ -namespace Ocelot.Configuration.File -{ - public interface IReRoute - { - string UpstreamPathTemplate { get; set; } - bool ReRouteIsCaseSensitive { get; set; } - int Priority { get; set; } - } -} +namespace Ocelot.Configuration.File +{ + public interface IRoute + { + string UpstreamPathTemplate { get; set; } + bool RouteIsCaseSensitive { get; set; } + int Priority { get; set; } + } +} diff --git a/src/Ocelot/Configuration/IInternalConfiguration.cs b/src/Ocelot/Configuration/IInternalConfiguration.cs index 0925265f5..8be2f1253 100644 --- a/src/Ocelot/Configuration/IInternalConfiguration.cs +++ b/src/Ocelot/Configuration/IInternalConfiguration.cs @@ -1,27 +1,27 @@ -using System.Collections.Generic; - -namespace Ocelot.Configuration -{ - using System; - - public interface IInternalConfiguration - { - List ReRoutes { get; } - - string AdministrationPath { get; } - - ServiceProviderConfiguration ServiceProviderConfiguration { get; } - - string RequestId { get; } - - LoadBalancerOptions LoadBalancerOptions { get; } - - string DownstreamScheme { get; } - - QoSOptions QoSOptions { get; } - - HttpHandlerOptions HttpHandlerOptions { get; } - - Version DownstreamHttpVersion { get; } - } -} +using System.Collections.Generic; + +namespace Ocelot.Configuration +{ + using System; + + public interface IInternalConfiguration + { + List Routes { get; } + + string AdministrationPath { get; } + + ServiceProviderConfiguration ServiceProviderConfiguration { get; } + + string RequestId { get; } + + LoadBalancerOptions LoadBalancerOptions { get; } + + string DownstreamScheme { get; } + + QoSOptions QoSOptions { get; } + + HttpHandlerOptions HttpHandlerOptions { get; } + + Version DownstreamHttpVersion { get; } + } +} diff --git a/src/Ocelot/Configuration/InternalConfiguration.cs b/src/Ocelot/Configuration/InternalConfiguration.cs index 7a398982b..617016230 100644 --- a/src/Ocelot/Configuration/InternalConfiguration.cs +++ b/src/Ocelot/Configuration/InternalConfiguration.cs @@ -1,42 +1,42 @@ -using System.Collections.Generic; - -namespace Ocelot.Configuration -{ - using System; - - public class InternalConfiguration : IInternalConfiguration - { - public InternalConfiguration( - List reRoutes, - string administrationPath, - ServiceProviderConfiguration serviceProviderConfiguration, - string requestId, - LoadBalancerOptions loadBalancerOptions, - string downstreamScheme, - QoSOptions qoSOptions, - HttpHandlerOptions httpHandlerOptions, - Version downstreamHttpVersion) - { - ReRoutes = reRoutes; - AdministrationPath = administrationPath; - ServiceProviderConfiguration = serviceProviderConfiguration; - RequestId = requestId; - LoadBalancerOptions = loadBalancerOptions; - DownstreamScheme = downstreamScheme; - QoSOptions = qoSOptions; - HttpHandlerOptions = httpHandlerOptions; - DownstreamHttpVersion = downstreamHttpVersion; - } - - public List ReRoutes { get; } - public string AdministrationPath { get; } - public ServiceProviderConfiguration ServiceProviderConfiguration { get; } - public string RequestId { get; } - public LoadBalancerOptions LoadBalancerOptions { get; } - public string DownstreamScheme { get; } - public QoSOptions QoSOptions { get; } - public HttpHandlerOptions HttpHandlerOptions { get; } - - public Version DownstreamHttpVersion { get; } - } -} +using System.Collections.Generic; + +namespace Ocelot.Configuration +{ + using System; + + public class InternalConfiguration : IInternalConfiguration + { + public InternalConfiguration( + List routes, + string administrationPath, + ServiceProviderConfiguration serviceProviderConfiguration, + string requestId, + LoadBalancerOptions loadBalancerOptions, + string downstreamScheme, + QoSOptions qoSOptions, + HttpHandlerOptions httpHandlerOptions, + Version downstreamHttpVersion) + { + Routes = routes; + AdministrationPath = administrationPath; + ServiceProviderConfiguration = serviceProviderConfiguration; + RequestId = requestId; + LoadBalancerOptions = loadBalancerOptions; + DownstreamScheme = downstreamScheme; + QoSOptions = qoSOptions; + HttpHandlerOptions = httpHandlerOptions; + DownstreamHttpVersion = downstreamHttpVersion; + } + + public List Routes { get; } + public string AdministrationPath { get; } + public ServiceProviderConfiguration ServiceProviderConfiguration { get; } + public string RequestId { get; } + public LoadBalancerOptions LoadBalancerOptions { get; } + public string DownstreamScheme { get; } + public QoSOptions QoSOptions { get; } + public HttpHandlerOptions HttpHandlerOptions { get; } + + public Version DownstreamHttpVersion { get; } + } +} diff --git a/src/Ocelot/Configuration/ReRoute.cs b/src/Ocelot/Configuration/Route.cs similarity index 65% rename from src/Ocelot/Configuration/ReRoute.cs rename to src/Ocelot/Configuration/Route.cs index d09047bd1..39ebe4872 100644 --- a/src/Ocelot/Configuration/ReRoute.cs +++ b/src/Ocelot/Configuration/Route.cs @@ -5,18 +5,18 @@ using System.Collections.Generic; using System.Net.Http; - public class ReRoute + public class Route { - public ReRoute(List downstreamReRoute, - List downstreamReRouteConfig, + public Route(List downstreamRoute, + List downstreamRouteConfig, List upstreamHttpMethod, UpstreamPathTemplate upstreamTemplatePattern, string upstreamHost, string aggregator) { UpstreamHost = upstreamHost; - DownstreamReRoute = downstreamReRoute; - DownstreamReRouteConfig = downstreamReRouteConfig; + DownstreamRoute = downstreamRoute; + DownstreamRouteConfig = downstreamRouteConfig; UpstreamHttpMethod = upstreamHttpMethod; UpstreamTemplatePattern = upstreamTemplatePattern; Aggregator = aggregator; @@ -25,8 +25,8 @@ public ReRoute(List downstreamReRoute, public UpstreamPathTemplate UpstreamTemplatePattern { get; private set; } public List UpstreamHttpMethod { get; private set; } public string UpstreamHost { get; private set; } - public List DownstreamReRoute { get; private set; } - public List DownstreamReRouteConfig { get; private set; } + public List DownstreamRoute { get; private set; } + public List DownstreamRouteConfig { get; private set; } public string Aggregator { get; private set; } } } diff --git a/src/Ocelot/Configuration/ReRouteOptions.cs b/src/Ocelot/Configuration/RouteOptions.cs similarity index 75% rename from src/Ocelot/Configuration/ReRouteOptions.cs rename to src/Ocelot/Configuration/RouteOptions.cs index e2382d5c6..9b5ba8d94 100644 --- a/src/Ocelot/Configuration/ReRouteOptions.cs +++ b/src/Ocelot/Configuration/RouteOptions.cs @@ -1,20 +1,20 @@ -namespace Ocelot.Configuration -{ - public class ReRouteOptions - { - public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting, bool useServiceDiscovery) - { - IsAuthenticated = isAuthenticated; - IsAuthorised = isAuthorised; - IsCached = isCached; - EnableRateLimiting = isEnableRateLimiting; - UseServiceDiscovery = useServiceDiscovery; - } - - public bool IsAuthenticated { get; private set; } - public bool IsAuthorised { get; private set; } - public bool IsCached { get; private set; } - public bool EnableRateLimiting { get; private set; } - public bool UseServiceDiscovery { get; private set; } - } -} +namespace Ocelot.Configuration +{ + public class RouteOptions + { + public RouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting, bool useServiceDiscovery) + { + IsAuthenticated = isAuthenticated; + IsAuthorised = isAuthorised; + IsCached = isCached; + EnableRateLimiting = isEnableRateLimiting; + UseServiceDiscovery = useServiceDiscovery; + } + + public bool IsAuthenticated { get; private set; } + public bool IsAuthorised { get; private set; } + public bool IsCached { get; private set; } + public bool EnableRateLimiting { get; private set; } + public bool UseServiceDiscovery { get; private set; } + } +} diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs index e40f17efa..37fbf2cf3 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs @@ -1,184 +1,184 @@ -namespace Ocelot.Configuration.Validator -{ - using Errors; - using File; - using FluentValidation; - using Microsoft.Extensions.DependencyInjection; - using Responses; - using ServiceDiscovery; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text.RegularExpressions; - using System.Threading.Tasks; - - public class FileConfigurationFluentValidator : AbstractValidator, IConfigurationValidator - { - private readonly List _serviceDiscoveryFinderDelegates; - - public FileConfigurationFluentValidator(IServiceProvider provider, ReRouteFluentValidator reRouteFluentValidator, FileGlobalConfigurationFluentValidator fileGlobalConfigurationFluentValidator) - { - _serviceDiscoveryFinderDelegates = provider - .GetServices() - .ToList(); - - RuleForEach(configuration => configuration.ReRoutes) - .SetValidator(reRouteFluentValidator); - - RuleFor(configuration => configuration.GlobalConfiguration) - .SetValidator(fileGlobalConfigurationFluentValidator); - - RuleForEach(configuration => configuration.ReRoutes) - .Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes)) - .WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate"); - - RuleForEach(configuration => configuration.ReRoutes) - .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()?"); - - RuleForEach(configuration => configuration.ReRoutes) - .Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates)) - .WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate"); - - RuleForEach(configuration => configuration.Aggregates) - .Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates)) - .WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate"); - - RuleForEach(configuration => configuration.Aggregates) - .Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes)) - .WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property"); - - RuleForEach(configuration => configuration.Aggregates) - .Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes)) - .WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates"); - } - - private bool HaveServiceDiscoveryProviderRegistered(FileReRoute reRoute, FileServiceDiscoveryProvider serviceDiscoveryProvider) - { - if (string.IsNullOrEmpty(reRoute.ServiceName)) - { - return true; - } - - if (serviceDiscoveryProvider?.Type?.ToLower() == "servicefabric") - { - return true; - } - - return _serviceDiscoveryFinderDelegates.Any(); - } - - private bool HaveServiceDiscoveryProviderRegistered(FileServiceDiscoveryProvider serviceDiscoveryProvider) - { - if (serviceDiscoveryProvider == null) - { - return true; - } - - if (serviceDiscoveryProvider?.Type?.ToLower() == "servicefabric") - { - return true; - } - - return string.IsNullOrEmpty(serviceDiscoveryProvider.Type) || _serviceDiscoveryFinderDelegates.Any(); - } - - public async Task> IsValid(FileConfiguration configuration) - { - var validateResult = await ValidateAsync(configuration); - - if (validateResult.IsValid) - { - return new OkResponse(new ConfigurationValidationResult(false)); - } - - var errors = validateResult.Errors.Select(failure => new FileValidationFailedError(failure.ErrorMessage)); - - var result = new ConfigurationValidationResult(true, errors.Cast().ToList()); - - return new OkResponse(result); - } - - private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List reRoutes) - { - var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key)); - - 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) - { - var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key)); - - return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey)); - } - - private static bool IsNotDuplicateIn(FileReRoute reRoute, - List reRoutes) - { - var matchingReRoutes = reRoutes - .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate - && r.UpstreamHost == reRoute.UpstreamHost) - .ToList(); - - if (matchingReRoutes.Count == 1) - { - return true; - } - - var allowAllVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count == 0); - - var duplicateAllowAllVerbs = matchingReRoutes.Count(x => x.UpstreamHttpMethod.Count == 0) > 1; - - var specificVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count != 0); - - var duplicateSpecificVerbs = matchingReRoutes.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any(); - - if (duplicateAllowAllVerbs || duplicateSpecificVerbs || (allowAllVerbs && specificVerbs)) - { - return false; - } - - return true; - } - - private static bool IsNotDuplicateIn(FileReRoute reRoute, - List aggregateReRoutes) - { - var duplicate = aggregateReRoutes - .Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate - && a.UpstreamHost == reRoute.UpstreamHost - && reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get")); - - return !duplicate; - } - - private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute, - List aggregateReRoutes) - { - var matchingReRoutes = aggregateReRoutes - .Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate - && r.UpstreamHost == reRoute.UpstreamHost) - .ToList(); - - return matchingReRoutes.Count <= 1; - } - } -} +namespace Ocelot.Configuration.Validator +{ + using Errors; + using File; + using FluentValidation; + using Microsoft.Extensions.DependencyInjection; + using Responses; + using ServiceDiscovery; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text.RegularExpressions; + using System.Threading.Tasks; + + public class FileConfigurationFluentValidator : AbstractValidator, IConfigurationValidator + { + private readonly List _serviceDiscoveryFinderDelegates; + + public FileConfigurationFluentValidator(IServiceProvider provider, RouteFluentValidator routeFluentValidator, FileGlobalConfigurationFluentValidator fileGlobalConfigurationFluentValidator) + { + _serviceDiscoveryFinderDelegates = provider + .GetServices() + .ToList(); + + RuleForEach(configuration => configuration.Routes) + .SetValidator(routeFluentValidator); + + RuleFor(configuration => configuration.GlobalConfiguration) + .SetValidator(fileGlobalConfigurationFluentValidator); + + RuleForEach(configuration => configuration.Routes) + .Must((config, route) => IsNotDuplicateIn(route, config.Routes)) + .WithMessage((config, route) => $"{nameof(route)} {route.UpstreamPathTemplate} has duplicate"); + + RuleForEach(configuration => configuration.Routes) + .Must((config, route) => HaveServiceDiscoveryProviderRegistered(route, config.GlobalConfiguration.ServiceDiscoveryProvider)) + .WithMessage((config, route) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route 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.Routes) + .Must((config, route) => IsPlaceholderNotDuplicatedIn(route.UpstreamPathTemplate)) + .WithMessage((config, route) => $"{nameof(route)} {route.UpstreamPathTemplate} has duplicated placeholder"); + + RuleFor(configuration => configuration.GlobalConfiguration.ServiceDiscoveryProvider) + .Must(HaveServiceDiscoveryProviderRegistered) + .WithMessage((config, route) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route 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.Routes) + .Must((config, route) => IsNotDuplicateIn(route, config.Aggregates)) + .WithMessage((config, route) => $"{nameof(route)} {route.UpstreamPathTemplate} has duplicate aggregate"); + + RuleForEach(configuration => configuration.Aggregates) + .Must((config, aggregateRoute) => IsNotDuplicateIn(aggregateRoute, config.Aggregates)) + .WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate"); + + RuleForEach(configuration => configuration.Aggregates) + .Must((config, aggregateRoute) => AllRoutesForAggregateExist(aggregateRoute, config.Routes)) + .WithMessage((config, aggregateRoute) => $"Routes for {nameof(aggregateRoute)} {aggregateRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property"); + + RuleForEach(configuration => configuration.Aggregates) + .Must((config, aggregateRoute) => DoesNotContainRoutesWithSpecificRequestIdKeys(aggregateRoute, config.Routes)) + .WithMessage((config, aggregateRoute) => $"{nameof(aggregateRoute)} {aggregateRoute.UpstreamPathTemplate} contains Route with specific RequestIdKey, this is not possible with Aggregates"); + } + + private bool HaveServiceDiscoveryProviderRegistered(FileRoute route, FileServiceDiscoveryProvider serviceDiscoveryProvider) + { + if (string.IsNullOrEmpty(route.ServiceName)) + { + return true; + } + + if (serviceDiscoveryProvider?.Type?.ToLower() == "servicefabric") + { + return true; + } + + return _serviceDiscoveryFinderDelegates.Any(); + } + + private bool HaveServiceDiscoveryProviderRegistered(FileServiceDiscoveryProvider serviceDiscoveryProvider) + { + if (serviceDiscoveryProvider == null) + { + return true; + } + + if (serviceDiscoveryProvider?.Type?.ToLower() == "servicefabric") + { + return true; + } + + return string.IsNullOrEmpty(serviceDiscoveryProvider.Type) || _serviceDiscoveryFinderDelegates.Any(); + } + + public async Task> IsValid(FileConfiguration configuration) + { + var validateResult = await ValidateAsync(configuration); + + if (validateResult.IsValid) + { + return new OkResponse(new ConfigurationValidationResult(false)); + } + + var errors = validateResult.Errors.Select(failure => new FileValidationFailedError(failure.ErrorMessage)); + + var result = new ConfigurationValidationResult(true, errors.Cast().ToList()); + + return new OkResponse(result); + } + + private bool AllRoutesForAggregateExist(FileAggregateRoute fileAggregateRoute, List routes) + { + var routesForAggregate = routes.Where(r => fileAggregateRoute.RouteKeys.Contains(r.Key)); + + return routesForAggregate.Count() == fileAggregateRoute.RouteKeys.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 DoesNotContainRoutesWithSpecificRequestIdKeys(FileAggregateRoute fileAggregateRoute, + List routes) + { + var routesForAggregate = routes.Where(r => fileAggregateRoute.RouteKeys.Contains(r.Key)); + + return routesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey)); + } + + private static bool IsNotDuplicateIn(FileRoute route, + List routes) + { + var matchingRoutes = routes + .Where(r => r.UpstreamPathTemplate == route.UpstreamPathTemplate + && r.UpstreamHost == route.UpstreamHost) + .ToList(); + + if (matchingRoutes.Count == 1) + { + return true; + } + + var allowAllVerbs = matchingRoutes.Any(x => x.UpstreamHttpMethod.Count == 0); + + var duplicateAllowAllVerbs = matchingRoutes.Count(x => x.UpstreamHttpMethod.Count == 0) > 1; + + var specificVerbs = matchingRoutes.Any(x => x.UpstreamHttpMethod.Count != 0); + + var duplicateSpecificVerbs = matchingRoutes.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any(); + + if (duplicateAllowAllVerbs || duplicateSpecificVerbs || (allowAllVerbs && specificVerbs)) + { + return false; + } + + return true; + } + + private static bool IsNotDuplicateIn(FileRoute route, + List aggregateRoutes) + { + var duplicate = aggregateRoutes + .Any(a => a.UpstreamPathTemplate == route.UpstreamPathTemplate + && a.UpstreamHost == route.UpstreamHost + && route.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get")); + + return !duplicate; + } + + private static bool IsNotDuplicateIn(FileAggregateRoute route, + List aggregateRoutes) + { + var matchingRoutes = aggregateRoutes + .Where(r => r.UpstreamPathTemplate == route.UpstreamPathTemplate + && r.UpstreamHost == route.UpstreamHost) + .ToList(); + + return matchingRoutes.Count <= 1; + } + } +} diff --git a/src/Ocelot/Configuration/Validator/FileQoSOptionsFluentValidator.cs b/src/Ocelot/Configuration/Validator/FileQoSOptionsFluentValidator.cs index 4a6c8496f..622c5468e 100644 --- a/src/Ocelot/Configuration/Validator/FileQoSOptionsFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileQoSOptionsFluentValidator.cs @@ -1,30 +1,30 @@ -namespace Ocelot.Configuration.Validator -{ - using File; - using FluentValidation; - using Microsoft.Extensions.DependencyInjection; - using Requester; - using System; - - public class FileQoSOptionsFluentValidator : AbstractValidator - { - private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate; - - public FileQoSOptionsFluentValidator(IServiceProvider provider) - { - _qosDelegatingHandlerDelegate = provider.GetService(); - - When(qosOptions => qosOptions.TimeoutValue > 0 && qosOptions.ExceptionsAllowedBeforeBreaking > 0, () => - { - RuleFor(qosOptions => qosOptions) - .Must(HaveQosHandlerRegistered) - .WithMessage("Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?"); - }); - } - - private bool HaveQosHandlerRegistered(FileQoSOptions arg) - { - return _qosDelegatingHandlerDelegate != null; - } - } -} +namespace Ocelot.Configuration.Validator +{ + using File; + using FluentValidation; + using Microsoft.Extensions.DependencyInjection; + using Requester; + using System; + + public class FileQoSOptionsFluentValidator : AbstractValidator + { + private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate; + + public FileQoSOptionsFluentValidator(IServiceProvider provider) + { + _qosDelegatingHandlerDelegate = provider.GetService(); + + When(qosOptions => qosOptions.TimeoutValue > 0 && qosOptions.ExceptionsAllowedBeforeBreaking > 0, () => + { + RuleFor(qosOptions => qosOptions) + .Must(HaveQosHandlerRegistered) + .WithMessage("Unable to start Ocelot because either a Route or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?"); + }); + } + + private bool HaveQosHandlerRegistered(FileQoSOptions arg) + { + return _qosDelegatingHandlerDelegate != null; + } + } +} diff --git a/src/Ocelot/Configuration/Validator/HostAndPortValidator.cs b/src/Ocelot/Configuration/Validator/HostAndPortValidator.cs index f93a2ccdf..9bf8b6c07 100644 --- a/src/Ocelot/Configuration/Validator/HostAndPortValidator.cs +++ b/src/Ocelot/Configuration/Validator/HostAndPortValidator.cs @@ -1,15 +1,15 @@ -namespace Ocelot.Configuration.Validator -{ - using FluentValidation; - using Ocelot.Configuration.File; - - public class HostAndPortValidator : AbstractValidator - { - public HostAndPortValidator() - { - RuleFor(r => r.Host) - .NotEmpty() - .WithMessage("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!"); - } - } -} +namespace Ocelot.Configuration.Validator +{ + using FluentValidation; + using Ocelot.Configuration.File; + + public class HostAndPortValidator : AbstractValidator + { + public HostAndPortValidator() + { + RuleFor(r => r.Host) + .NotEmpty() + .WithMessage("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using Route.Host or Ocelot cannot find your service!"); + } + } +} diff --git a/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs b/src/Ocelot/Configuration/Validator/RouteFluentValidator.cs similarity index 71% rename from src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs rename to src/Ocelot/Configuration/Validator/RouteFluentValidator.cs index 99a5a422c..edfd5985c 100644 --- a/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/RouteFluentValidator.cs @@ -1,127 +1,127 @@ -namespace Ocelot.Configuration.Validator -{ - using File; - using FluentValidation; - using Microsoft.AspNetCore.Authentication; - using System.Linq; - using System.Text.RegularExpressions; - using System.Threading; - using System.Threading.Tasks; - - public class ReRouteFluentValidator : AbstractValidator - { - private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider; - - public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, HostAndPortValidator hostAndPortValidator, FileQoSOptionsFluentValidator fileQoSOptionsFluentValidator) - { - _authenticationSchemeProvider = authenticationSchemeProvider; - - RuleFor(reRoute => reRoute.QoSOptions) - .SetValidator(fileQoSOptionsFluentValidator); - - RuleFor(reRoute => reRoute.DownstreamPathTemplate) - .NotEmpty() - .WithMessage("{PropertyName} cannot be empty"); - - RuleFor(reRoute => reRoute.UpstreamPathTemplate) - .NotEmpty() - .WithMessage("{PropertyName} cannot be empty"); - - When(reRoute => !string.IsNullOrEmpty(reRoute.DownstreamPathTemplate), () => - { - RuleFor(reRoute => reRoute.DownstreamPathTemplate) - .Must(path => path.StartsWith("/")) - .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash"); - - RuleFor(reRoute => reRoute.DownstreamPathTemplate) - .Must(path => !path.Contains("//")) - .WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."); - - RuleFor(reRoute => reRoute.DownstreamPathTemplate) - .Must(path => !path.Contains("https://") && !path.Contains("http://")) - .WithMessage("{PropertyName} {PropertyValue} contains scheme"); - }); - - When(reRoute => !string.IsNullOrEmpty(reRoute.UpstreamPathTemplate), () => - { - RuleFor(reRoute => reRoute.UpstreamPathTemplate) - .Must(path => !path.Contains("//")) - .WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."); - - RuleFor(reRoute => reRoute.UpstreamPathTemplate) - .Must(path => path.StartsWith("/")) - .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash"); - - RuleFor(reRoute => reRoute.UpstreamPathTemplate) - .Must(path => !path.Contains("https://") && !path.Contains("http://")) - .WithMessage("{PropertyName} {PropertyValue} contains scheme"); - }); - - When(reRoute => reRoute.RateLimitOptions.EnableRateLimiting, () => - { - RuleFor(reRoute => reRoute.RateLimitOptions.Period) - .NotEmpty() - .WithMessage("RateLimitOptions.Period is empty"); - - RuleFor(reRoute => reRoute.RateLimitOptions) - .Must(IsValidPeriod) - .WithMessage("RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period"); - }); - - RuleFor(reRoute => reRoute.AuthenticationOptions) - .MustAsync(IsSupportedAuthenticationProviders) - .WithMessage("{PropertyName} {PropertyValue} is unsupported authentication provider"); - - When(reRoute => string.IsNullOrEmpty(reRoute.ServiceName), () => - { - RuleFor(r => r.DownstreamHostAndPorts).NotEmpty() - .WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!"); - }); - - When(reRoute => string.IsNullOrEmpty(reRoute.ServiceName), () => - { - RuleForEach(reRoute => reRoute.DownstreamHostAndPorts) - .SetValidator(hostAndPortValidator); - }); - - When(reRoute => !string.IsNullOrEmpty(reRoute.DownstreamHttpVersion), () => - { - RuleFor(r => r.DownstreamHttpVersion).Matches("^[0-9]([.,][0-9]{1,1})?$"); - }); - } - - private async Task IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(authenticationOptions.AuthenticationProviderKey)) - { - return true; - } - - var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync(); - - var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList(); - - return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey); - } - - private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions) - { - if (string.IsNullOrEmpty(rateLimitOptions.Period)) - { - return false; - } - - var period = rateLimitOptions.Period; - - var secondsRegEx = new Regex("^[0-9]+s"); - var minutesRegEx = new Regex("^[0-9]+m"); - var hoursRegEx = new Regex("^[0-9]+h"); - var daysRegEx = new Regex("^[0-9]+d"); - - return secondsRegEx.Match(period).Success - || minutesRegEx.Match(period).Success - || hoursRegEx.Match(period).Success - || daysRegEx.Match(period).Success; - } - } -} +namespace Ocelot.Configuration.Validator +{ + using Ocelot.Configuration.File; + using FluentValidation; + using Microsoft.AspNetCore.Authentication; + using System.Linq; + using System.Text.RegularExpressions; + using System.Threading; + using System.Threading.Tasks; + + public class RouteFluentValidator : AbstractValidator + { + private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider; + + public RouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, HostAndPortValidator hostAndPortValidator, FileQoSOptionsFluentValidator fileQoSOptionsFluentValidator) + { + _authenticationSchemeProvider = authenticationSchemeProvider; + + RuleFor(route => route.QoSOptions) + .SetValidator(fileQoSOptionsFluentValidator); + + RuleFor(route => route.DownstreamPathTemplate) + .NotEmpty() + .WithMessage("{PropertyName} cannot be empty"); + + RuleFor(route => route.UpstreamPathTemplate) + .NotEmpty() + .WithMessage("{PropertyName} cannot be empty"); + + When(route => !string.IsNullOrEmpty(route.DownstreamPathTemplate), () => + { + RuleFor(route => route.DownstreamPathTemplate) + .Must(path => path.StartsWith("/")) + .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash"); + + RuleFor(route => route.DownstreamPathTemplate) + .Must(path => !path.Contains("//")) + .WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."); + + RuleFor(route => route.DownstreamPathTemplate) + .Must(path => !path.Contains("https://") && !path.Contains("http://")) + .WithMessage("{PropertyName} {PropertyValue} contains scheme"); + }); + + When(route => !string.IsNullOrEmpty(route.UpstreamPathTemplate), () => + { + RuleFor(route => route.UpstreamPathTemplate) + .Must(path => !path.Contains("//")) + .WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."); + + RuleFor(route => route.UpstreamPathTemplate) + .Must(path => path.StartsWith("/")) + .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash"); + + RuleFor(route => route.UpstreamPathTemplate) + .Must(path => !path.Contains("https://") && !path.Contains("http://")) + .WithMessage("{PropertyName} {PropertyValue} contains scheme"); + }); + + When(route => route.RateLimitOptions.EnableRateLimiting, () => + { + RuleFor(route => route.RateLimitOptions.Period) + .NotEmpty() + .WithMessage("RateLimitOptions.Period is empty"); + + RuleFor(route => route.RateLimitOptions) + .Must(IsValidPeriod) + .WithMessage("RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period"); + }); + + RuleFor(route => route.AuthenticationOptions) + .MustAsync(IsSupportedAuthenticationProviders) + .WithMessage("{PropertyName} {PropertyValue} is unsupported authentication provider"); + + When(route => string.IsNullOrEmpty(route.ServiceName), () => + { + RuleFor(r => r.DownstreamHostAndPorts).NotEmpty() + .WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!"); + }); + + When(route => string.IsNullOrEmpty(route.ServiceName), () => + { + RuleForEach(route => route.DownstreamHostAndPorts) + .SetValidator(hostAndPortValidator); + }); + + When(route => !string.IsNullOrEmpty(route.DownstreamHttpVersion), () => + { + RuleFor(r => r.DownstreamHttpVersion).Matches("^[0-9]([.,][0-9]{1,1})?$"); + }); + } + + private async Task IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken) + { + if (string.IsNullOrEmpty(authenticationOptions.AuthenticationProviderKey)) + { + return true; + } + + var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync(); + + var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList(); + + return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey); + } + + private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions) + { + if (string.IsNullOrEmpty(rateLimitOptions.Period)) + { + return false; + } + + var period = rateLimitOptions.Period; + + var secondsRegEx = new Regex("^[0-9]+s"); + var minutesRegEx = new Regex("^[0-9]+m"); + var hoursRegEx = new Regex("^[0-9]+h"); + var daysRegEx = new Regex("^[0-9]+d"); + + return secondsRegEx.Match(period).Success + || minutesRegEx.Match(period).Success + || hoursRegEx.Match(period).Success + || daysRegEx.Match(period).Success; + } + } +} diff --git a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs index 3d29ef514..107da2c2b 100644 --- a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs +++ b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs @@ -1,85 +1,85 @@ -namespace Ocelot.DependencyInjection -{ - using Configuration.File; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.Configuration.Memory; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Text.RegularExpressions; - - public static class ConfigurationBuilderExtensions - { - [Obsolete("Please set BaseUrl in ocelot.json GlobalConfiguration.BaseUrl")] - public static IConfigurationBuilder AddOcelotBaseUrl(this IConfigurationBuilder builder, string baseUrl) - { - var memorySource = new MemoryConfigurationSource - { - InitialData = new List> - { - new KeyValuePair("BaseUrl", baseUrl) - } - }; - - builder.Add(memorySource); - - return builder; - } - - public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, IWebHostEnvironment env) - { - return builder.AddOcelot(".", env); - } - - public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder, IWebHostEnvironment env) - { - const string primaryConfigFile = "ocelot.json"; - - const string globalConfigFile = "ocelot.global.json"; - - const string subConfigPattern = @"^ocelot\.(.*?)\.json$"; - - string excludeConfigName = env?.EnvironmentName != null ? $"ocelot.{env.EnvironmentName}.json" : string.Empty; - - var reg = new Regex(subConfigPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); - - var files = new DirectoryInfo(folder) - .EnumerateFiles() - .Where(fi => reg.IsMatch(fi.Name) && (fi.Name != excludeConfigName)) - .ToList(); - - var fileConfiguration = new FileConfiguration(); - - foreach (var file in files) - { - if (files.Count > 1 && file.Name.Equals(primaryConfigFile, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var lines = File.ReadAllText(file.FullName); - - var config = JsonConvert.DeserializeObject(lines); - - if (file.Name.Equals(globalConfigFile, StringComparison.OrdinalIgnoreCase)) - { - fileConfiguration.GlobalConfiguration = config.GlobalConfiguration; - } - - fileConfiguration.Aggregates.AddRange(config.Aggregates); - fileConfiguration.ReRoutes.AddRange(config.ReRoutes); - } - - var json = JsonConvert.SerializeObject(fileConfiguration); - - File.WriteAllText(primaryConfigFile, json); - - builder.AddJsonFile(primaryConfigFile, false, false); - - return builder; - } - } -} +namespace Ocelot.DependencyInjection +{ + using Ocelot.Configuration.File; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.Configuration.Memory; + using Newtonsoft.Json; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text.RegularExpressions; + + public static class ConfigurationBuilderExtensions + { + [Obsolete("Please set BaseUrl in ocelot.json GlobalConfiguration.BaseUrl")] + public static IConfigurationBuilder AddOcelotBaseUrl(this IConfigurationBuilder builder, string baseUrl) + { + var memorySource = new MemoryConfigurationSource + { + InitialData = new List> + { + new KeyValuePair("BaseUrl", baseUrl) + } + }; + + builder.Add(memorySource); + + return builder; + } + + public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, IWebHostEnvironment env) + { + return builder.AddOcelot(".", env); + } + + public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder, IWebHostEnvironment env) + { + const string primaryConfigFile = "ocelot.json"; + + const string globalConfigFile = "ocelot.global.json"; + + const string subConfigPattern = @"^ocelot\.(.*?)\.json$"; + + string excludeConfigName = env?.EnvironmentName != null ? $"ocelot.{env.EnvironmentName}.json" : string.Empty; + + var reg = new Regex(subConfigPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); + + var files = new DirectoryInfo(folder) + .EnumerateFiles() + .Where(fi => reg.IsMatch(fi.Name) && (fi.Name != excludeConfigName)) + .ToList(); + + var fileConfiguration = new FileConfiguration(); + + foreach (var file in files) + { + if (files.Count > 1 && file.Name.Equals(primaryConfigFile, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var lines = File.ReadAllText(file.FullName); + + var config = JsonConvert.DeserializeObject(lines); + + if (file.Name.Equals(globalConfigFile, StringComparison.OrdinalIgnoreCase)) + { + fileConfiguration.GlobalConfiguration = config.GlobalConfiguration; + } + + fileConfiguration.Aggregates.AddRange(config.Aggregates); + fileConfiguration.Routes.AddRange(config.Routes); + } + + var json = JsonConvert.SerializeObject(fileConfiguration); + + File.WriteAllText(primaryConfigFile, json); + + builder.AddJsonFile(primaryConfigFile, false, false); + + return builder; + } + } +} diff --git a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs index 17c95f1c7..b9306081b 100644 --- a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs @@ -38,11 +38,11 @@ IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFa where T : ILoadBalancer; IOcelotBuilder AddCustomLoadBalancer( - Func loadBalancerFactoryFunc) + Func loadBalancerFactoryFunc) where T : ILoadBalancer; IOcelotBuilder AddCustomLoadBalancer( - Func loadBalancerFactoryFunc) + Func loadBalancerFactoryFunc) where T : ILoadBalancer; IOcelotBuilder AddConfigPlaceholders(); diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 756868792..f4fbf8447 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -65,13 +65,13 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); - Services.TryAddSingleton(); + Services.TryAddSingleton(); Services.TryAddSingleton(); - Services.TryAddSingleton(); + Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); - Services.TryAddSingleton(); + Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); @@ -80,7 +80,7 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); - Services.TryAddSingleton(); + Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); @@ -175,14 +175,14 @@ public IOcelotBuilder AddTransientDefinedAggregator() public IOcelotBuilder AddCustomLoadBalancer() where T : ILoadBalancer, new() { - AddCustomLoadBalancer((provider, reRoute, serviceDiscoveryProvider) => new T()); + AddCustomLoadBalancer((provider, route, serviceDiscoveryProvider) => new T()); return this; } public IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc) where T : ILoadBalancer { - AddCustomLoadBalancer((provider, reRoute, serviceDiscoveryProvider) => + AddCustomLoadBalancer((provider, route, serviceDiscoveryProvider) => loadBalancerFactoryFunc()); return this; } @@ -190,26 +190,26 @@ public IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc) public IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc) where T : ILoadBalancer { - AddCustomLoadBalancer((provider, reRoute, serviceDiscoveryProvider) => + AddCustomLoadBalancer((provider, route, serviceDiscoveryProvider) => loadBalancerFactoryFunc(provider)); return this; } - public IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc) + public IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc) where T : ILoadBalancer { - AddCustomLoadBalancer((provider, reRoute, serviceDiscoveryProvider) => - loadBalancerFactoryFunc(reRoute, serviceDiscoveryProvider)); + AddCustomLoadBalancer((provider, route, serviceDiscoveryProvider) => + loadBalancerFactoryFunc(route, serviceDiscoveryProvider)); return this; } - public IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc) + public IOcelotBuilder AddCustomLoadBalancer(Func loadBalancerFactoryFunc) where T : ILoadBalancer { Services.AddSingleton(provider => new DelegateInvokingLoadBalancerCreator( - (reRoute, serviceDiscoveryProvider) => - loadBalancerFactoryFunc(provider, reRoute, serviceDiscoveryProvider))); + (route, serviceDiscoveryProvider) => + loadBalancerFactoryFunc(provider, route, serviceDiscoveryProvider))); return this; } diff --git a/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs b/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs index 6e6963194..1b034202b 100644 --- a/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs +++ b/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs @@ -5,9 +5,9 @@ using Ocelot.Logging; using Microsoft.AspNetCore.Http; using Ocelot.Middleware; - using Ocelot.PathManipulation; - using Ocelot.DownstreamRouteFinder.Middleware; - + using Ocelot.PathManipulation; + using Ocelot.DownstreamRouteFinder.Middleware; + public class ClaimsToDownstreamPathMiddleware : OcelotMiddleware { private readonly RequestDelegate _next; @@ -24,16 +24,16 @@ public ClaimsToDownstreamPathMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - if (downstreamReRoute.ClaimsToPath.Any()) + if (downstreamRoute.ClaimsToPath.Any()) { - Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path"); - + Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path"); + var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues(); - - var response = _changeDownstreamPathTemplate.ChangeDownstreamPath(downstreamReRoute.ClaimsToPath, httpContext.User.Claims, - downstreamReRoute.DownstreamPathTemplate, templatePlaceholderNameAndValues); + + var response = _changeDownstreamPathTemplate.ChangeDownstreamPath(downstreamRoute.ClaimsToPath, httpContext.User.Claims, + downstreamRoute.DownstreamPathTemplate, templatePlaceholderNameAndValues); if (response.IsError) { diff --git a/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs b/src/Ocelot/DownstreamRouteFinder/DownstreamRouteHolder.cs similarity index 60% rename from src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs rename to src/Ocelot/DownstreamRouteFinder/DownstreamRouteHolder.cs index 2e2133362..d833d8b93 100644 --- a/src/Ocelot/DownstreamRouteFinder/DownstreamRoute.cs +++ b/src/Ocelot/DownstreamRouteFinder/DownstreamRouteHolder.cs @@ -4,19 +4,19 @@ using Ocelot.DownstreamRouteFinder.UrlMatcher; using System.Collections.Generic; - public class DownstreamRoute + public class DownstreamRouteHolder { - public DownstreamRoute() + public DownstreamRouteHolder() { } - public DownstreamRoute(List templatePlaceholderNameAndValues, ReRoute reRoute) + public DownstreamRouteHolder(List templatePlaceholderNameAndValues, Route route) { TemplatePlaceholderNameAndValues = templatePlaceholderNameAndValues; - ReRoute = reRoute; + Route = route; } public List TemplatePlaceholderNameAndValues { get; private set; } - public ReRoute ReRoute { get; private set; } + public Route Route { get; private set; } } } diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs index b2b1db950..ede41601c 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteCreator.cs @@ -1,140 +1,139 @@ -namespace Ocelot.DownstreamRouteFinder.Finder -{ - using System; - using Configuration; - using Configuration.Builder; - using Configuration.Creator; - using LoadBalancer.LoadBalancers; - using Responses; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using UrlMatcher; - - public class DownstreamRouteCreator : IDownstreamRouteProvider - { - private readonly IQoSOptionsCreator _qoSOptionsCreator; - private readonly ConcurrentDictionary> _cache; - - public DownstreamRouteCreator(IQoSOptionsCreator qoSOptionsCreator) - { - _qoSOptionsCreator = qoSOptionsCreator; - _cache = new ConcurrentDictionary>(); - } - - public Response Get(string upstreamUrlPath, string upstreamQueryString, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost) - { - var serviceName = GetServiceName(upstreamUrlPath); - - var downstreamPath = GetDownstreamPath(upstreamUrlPath); - - if (HasQueryString(downstreamPath)) - { - downstreamPath = RemoveQueryString(downstreamPath); - } - - var downstreamPathForKeys = $"/{serviceName}{downstreamPath}"; - - var loadBalancerKey = CreateLoadBalancerKey(downstreamPathForKeys, upstreamHttpMethod, configuration.LoadBalancerOptions); - - if (_cache.TryGetValue(loadBalancerKey, out var downstreamRoute)) - { - return downstreamRoute; - } - - var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new List { upstreamHttpMethod }); - - var upstreamPathTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue(upstreamUrlPath).Build(); - - var downstreamReRouteBuilder = new DownstreamReRouteBuilder() - .WithServiceName(serviceName) - .WithLoadBalancerKey(loadBalancerKey) - .WithDownstreamPathTemplate(downstreamPath) - .WithUseServiceDiscovery(true) - .WithHttpHandlerOptions(configuration.HttpHandlerOptions) - .WithQosOptions(qosOptions) - .WithDownstreamScheme(configuration.DownstreamScheme) - .WithLoadBalancerOptions(configuration.LoadBalancerOptions) - .WithDownstreamHttpVersion(configuration.DownstreamHttpVersion) - .WithUpstreamPathTemplate(upstreamPathTemplate); - - var rateLimitOptions = configuration.ReRoutes != null - ? configuration.ReRoutes - .SelectMany(x => x.DownstreamReRoute) - .FirstOrDefault(x => x.ServiceName == serviceName) - : null; - - if (rateLimitOptions != null) - { - downstreamReRouteBuilder - .WithRateLimitOptions(rateLimitOptions.RateLimitOptions) - .WithEnableRateLimiting(true); - } - - var downstreamReRoute = downstreamReRouteBuilder.Build(); - - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamHttpMethod(new List() { upstreamHttpMethod }) - .WithUpstreamPathTemplate(upstreamPathTemplate) - .Build(); - - downstreamRoute = new OkResponse(new DownstreamRoute(new List(), reRoute)); - - _cache.AddOrUpdate(loadBalancerKey, downstreamRoute, (x, y) => downstreamRoute); - - return downstreamRoute; - } - - private static string RemoveQueryString(string downstreamPath) - { - return downstreamPath - .Substring(0, downstreamPath.IndexOf('?')); - } - - private static bool HasQueryString(string downstreamPath) - { - return downstreamPath.Contains("?"); - } - - private static string GetDownstreamPath(string upstreamUrlPath) - { - if (upstreamUrlPath.IndexOf('/', 1) == -1) - { - return "/"; - } - - return upstreamUrlPath - .Substring(upstreamUrlPath.IndexOf('/', 1)); - } - - private static string GetServiceName(string upstreamUrlPath) - { - if (upstreamUrlPath.IndexOf('/', 1) == -1) - { - return upstreamUrlPath - .Substring(1); - } - - return upstreamUrlPath - .Substring(1, upstreamUrlPath.IndexOf('/', 1)) - .TrimEnd('/'); - } - - private string CreateLoadBalancerKey(string downstreamTemplatePath, string httpMethod, LoadBalancerOptions loadBalancerOptions) - { - if (!string.IsNullOrEmpty(loadBalancerOptions.Type) && !string.IsNullOrEmpty(loadBalancerOptions.Key) && loadBalancerOptions.Type == nameof(CookieStickySessions)) - { - return $"{nameof(CookieStickySessions)}:{loadBalancerOptions.Key}"; - } - - return CreateQoSKey(downstreamTemplatePath, httpMethod); - } - - private string CreateQoSKey(string downstreamTemplatePath, string httpMethod) - { - var loadBalancerKey = $"{downstreamTemplatePath}|{httpMethod}"; - return loadBalancerKey; - } - } -} +namespace Ocelot.DownstreamRouteFinder.Finder +{ + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.LoadBalancer.LoadBalancers; + using Ocelot.Responses; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Linq; + using Ocelot.DownstreamRouteFinder.UrlMatcher; + + public class DownstreamRouteCreator : IDownstreamRouteProvider + { + private readonly IQoSOptionsCreator _qoSOptionsCreator; + private readonly ConcurrentDictionary> _cache; + + public DownstreamRouteCreator(IQoSOptionsCreator qoSOptionsCreator) + { + _qoSOptionsCreator = qoSOptionsCreator; + _cache = new ConcurrentDictionary>(); + } + + public Response Get(string upstreamUrlPath, string upstreamQueryString, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost) + { + var serviceName = GetServiceName(upstreamUrlPath); + + var downstreamPath = GetDownstreamPath(upstreamUrlPath); + + if (HasQueryString(downstreamPath)) + { + downstreamPath = RemoveQueryString(downstreamPath); + } + + var downstreamPathForKeys = $"/{serviceName}{downstreamPath}"; + + var loadBalancerKey = CreateLoadBalancerKey(downstreamPathForKeys, upstreamHttpMethod, configuration.LoadBalancerOptions); + + if (_cache.TryGetValue(loadBalancerKey, out var downstreamRouteHolder)) + { + return downstreamRouteHolder; + } + + var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new List { upstreamHttpMethod }); + + var upstreamPathTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue(upstreamUrlPath).Build(); + + var downstreamRouteBuilder = new DownstreamRouteBuilder() + .WithServiceName(serviceName) + .WithLoadBalancerKey(loadBalancerKey) + .WithDownstreamPathTemplate(downstreamPath) + .WithUseServiceDiscovery(true) + .WithHttpHandlerOptions(configuration.HttpHandlerOptions) + .WithQosOptions(qosOptions) + .WithDownstreamScheme(configuration.DownstreamScheme) + .WithLoadBalancerOptions(configuration.LoadBalancerOptions) + .WithDownstreamHttpVersion(configuration.DownstreamHttpVersion) + .WithUpstreamPathTemplate(upstreamPathTemplate); + + var rateLimitOptions = configuration.Routes != null + ? configuration.Routes + .SelectMany(x => x.DownstreamRoute) + .FirstOrDefault(x => x.ServiceName == serviceName) + : null; + + if (rateLimitOptions != null) + { + downstreamRouteBuilder + .WithRateLimitOptions(rateLimitOptions.RateLimitOptions) + .WithEnableRateLimiting(true); + } + + var downstreamRoute = downstreamRouteBuilder.Build(); + + var route = new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .WithUpstreamHttpMethod(new List() { upstreamHttpMethod }) + .WithUpstreamPathTemplate(upstreamPathTemplate) + .Build(); + + downstreamRouteHolder = new OkResponse(new DownstreamRouteHolder(new List(), route)); + + _cache.AddOrUpdate(loadBalancerKey, downstreamRouteHolder, (x, y) => downstreamRouteHolder); + + return downstreamRouteHolder; + } + + private static string RemoveQueryString(string downstreamPath) + { + return downstreamPath + .Substring(0, downstreamPath.IndexOf('?')); + } + + private static bool HasQueryString(string downstreamPath) + { + return downstreamPath.Contains("?"); + } + + private static string GetDownstreamPath(string upstreamUrlPath) + { + if (upstreamUrlPath.IndexOf('/', 1) == -1) + { + return "/"; + } + + return upstreamUrlPath + .Substring(upstreamUrlPath.IndexOf('/', 1)); + } + + private static string GetServiceName(string upstreamUrlPath) + { + if (upstreamUrlPath.IndexOf('/', 1) == -1) + { + return upstreamUrlPath + .Substring(1); + } + + return upstreamUrlPath + .Substring(1, upstreamUrlPath.IndexOf('/', 1)) + .TrimEnd('/'); + } + + private string CreateLoadBalancerKey(string downstreamTemplatePath, string httpMethod, LoadBalancerOptions loadBalancerOptions) + { + if (!string.IsNullOrEmpty(loadBalancerOptions.Type) && !string.IsNullOrEmpty(loadBalancerOptions.Key) && loadBalancerOptions.Type == nameof(CookieStickySessions)) + { + return $"{nameof(CookieStickySessions)}:{loadBalancerOptions.Key}"; + } + + return CreateQoSKey(downstreamTemplatePath, httpMethod); + } + + private string CreateQoSKey(string downstreamTemplatePath, string httpMethod) + { + var loadBalancerKey = $"{downstreamTemplatePath}|{httpMethod}"; + return loadBalancerKey; + } + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs index 115fbd424..150a2ca59 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteFinder.cs @@ -1,62 +1,62 @@ -using Ocelot.Configuration; -using Ocelot.DownstreamRouteFinder.UrlMatcher; -using Ocelot.Responses; -using System.Collections.Generic; -using System.Linq; - -namespace Ocelot.DownstreamRouteFinder.Finder -{ - public class DownstreamRouteFinder : IDownstreamRouteProvider - { - private readonly IUrlPathToUrlTemplateMatcher _urlMatcher; - private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder; - - public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder) - { - _urlMatcher = urlMatcher; - _placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder; - } - - public Response Get(string upstreamUrlPath, string upstreamQueryString, string httpMethod, IInternalConfiguration configuration, string upstreamHost) - { - var downstreamRoutes = new List(); - - var applicableReRoutes = configuration.ReRoutes - .Where(r => RouteIsApplicableToThisRequest(r, httpMethod, upstreamHost)) - .OrderByDescending(x => x.UpstreamTemplatePattern.Priority); - - foreach (var reRoute in applicableReRoutes) - { - var urlMatch = _urlMatcher.Match(upstreamUrlPath, upstreamQueryString, reRoute.UpstreamTemplatePattern); - - if (urlMatch.Data.Match) - { - downstreamRoutes.Add(GetPlaceholderNamesAndValues(upstreamUrlPath, upstreamQueryString, reRoute)); - } - } - - if (downstreamRoutes.Any()) - { - var notNullOption = downstreamRoutes.FirstOrDefault(x => !string.IsNullOrEmpty(x.ReRoute.UpstreamHost)); - var nullOption = downstreamRoutes.FirstOrDefault(x => string.IsNullOrEmpty(x.ReRoute.UpstreamHost)); - - return notNullOption != null ? new OkResponse(notNullOption) : new OkResponse(nullOption); - } - - return new ErrorResponse(new UnableToFindDownstreamRouteError(upstreamUrlPath, httpMethod)); - } - - private bool RouteIsApplicableToThisRequest(ReRoute reRoute, string httpMethod, string upstreamHost) - { - return (reRoute.UpstreamHttpMethod.Count == 0 || reRoute.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower())) && - (string.IsNullOrEmpty(reRoute.UpstreamHost) || reRoute.UpstreamHost == upstreamHost); - } - - private DownstreamRoute GetPlaceholderNamesAndValues(string path, string query, ReRoute reRoute) - { - var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, query, reRoute.UpstreamTemplatePattern.OriginalValue); - - return new DownstreamRoute(templatePlaceholderNameAndValues.Data, reRoute); - } - } -} +using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Responses; +using System.Collections.Generic; +using System.Linq; + +namespace Ocelot.DownstreamRouteFinder.Finder +{ + public class DownstreamRouteFinder : IDownstreamRouteProvider + { + private readonly IUrlPathToUrlTemplateMatcher _urlMatcher; + private readonly IPlaceholderNameAndValueFinder _placeholderNameAndValueFinder; + + public DownstreamRouteFinder(IUrlPathToUrlTemplateMatcher urlMatcher, IPlaceholderNameAndValueFinder urlPathPlaceholderNameAndValueFinder) + { + _urlMatcher = urlMatcher; + _placeholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder; + } + + public Response Get(string upstreamUrlPath, string upstreamQueryString, string httpMethod, IInternalConfiguration configuration, string upstreamHost) + { + var downstreamRoutes = new List(); + + var applicableRoutes = configuration.Routes + .Where(r => RouteIsApplicableToThisRequest(r, httpMethod, upstreamHost)) + .OrderByDescending(x => x.UpstreamTemplatePattern.Priority); + + foreach (var route in applicableRoutes) + { + var urlMatch = _urlMatcher.Match(upstreamUrlPath, upstreamQueryString, route.UpstreamTemplatePattern); + + if (urlMatch.Data.Match) + { + downstreamRoutes.Add(GetPlaceholderNamesAndValues(upstreamUrlPath, upstreamQueryString, route)); + } + } + + if (downstreamRoutes.Any()) + { + var notNullOption = downstreamRoutes.FirstOrDefault(x => !string.IsNullOrEmpty(x.Route.UpstreamHost)); + var nullOption = downstreamRoutes.FirstOrDefault(x => string.IsNullOrEmpty(x.Route.UpstreamHost)); + + return notNullOption != null ? new OkResponse(notNullOption) : new OkResponse(nullOption); + } + + return new ErrorResponse(new UnableToFindDownstreamRouteError(upstreamUrlPath, httpMethod)); + } + + private bool RouteIsApplicableToThisRequest(Route route, string httpMethod, string upstreamHost) + { + return (route.UpstreamHttpMethod.Count == 0 || route.UpstreamHttpMethod.Select(x => x.Method.ToLower()).Contains(httpMethod.ToLower())) && + (string.IsNullOrEmpty(route.UpstreamHost) || route.UpstreamHost == upstreamHost); + } + + private DownstreamRouteHolder GetPlaceholderNamesAndValues(string path, string query, Route route) + { + var templatePlaceholderNameAndValues = _placeholderNameAndValueFinder.Find(path, query, route.UpstreamTemplatePattern.OriginalValue); + + return new DownstreamRouteHolder(templatePlaceholderNameAndValues.Data, route); + } + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs index 32f689367..6733d4c5d 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/DownstreamRouteProviderFactory.cs @@ -1,44 +1,44 @@ -namespace Ocelot.DownstreamRouteFinder.Finder -{ - using Configuration; - using Microsoft.Extensions.DependencyInjection; - using Ocelot.Logging; - using System; - using System.Collections.Generic; - using System.Linq; - - public class DownstreamRouteProviderFactory : IDownstreamRouteProviderFactory - { - private readonly Dictionary _providers; - private readonly IOcelotLogger _logger; - - public DownstreamRouteProviderFactory(IServiceProvider provider, IOcelotLoggerFactory factory) - { - _logger = factory.CreateLogger(); - _providers = provider.GetServices().ToDictionary(x => x.GetType().Name); - } - - public IDownstreamRouteProvider Get(IInternalConfiguration config) - { - //todo - this is a bit hacky we are saying there are no reRoutes or there are reRoutes but none of them have - //an upstream path template which means they are dyanmic and service discovery is on... - if ((!config.ReRoutes.Any() || config.ReRoutes.All(x => string.IsNullOrEmpty(x.UpstreamTemplatePattern?.OriginalValue))) && IsServiceDiscovery(config.ServiceProviderConfiguration)) - { - _logger.LogInformation($"Selected {nameof(DownstreamRouteCreator)} as DownstreamRouteProvider for this request"); - return _providers[nameof(DownstreamRouteCreator)]; - } - - return _providers[nameof(DownstreamRouteFinder)]; - } - - private bool IsServiceDiscovery(ServiceProviderConfiguration config) - { - if (!string.IsNullOrEmpty(config?.Host) && config?.Port > 0 && !string.IsNullOrEmpty(config.Type)) - { - return true; - } - - return false; - } - } -} +namespace Ocelot.DownstreamRouteFinder.Finder +{ + using Configuration; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Logging; + using System; + using System.Collections.Generic; + using System.Linq; + + public class DownstreamRouteProviderFactory : IDownstreamRouteProviderFactory + { + private readonly Dictionary _providers; + private readonly IOcelotLogger _logger; + + public DownstreamRouteProviderFactory(IServiceProvider provider, IOcelotLoggerFactory factory) + { + _logger = factory.CreateLogger(); + _providers = provider.GetServices().ToDictionary(x => x.GetType().Name); + } + + public IDownstreamRouteProvider Get(IInternalConfiguration config) + { + //todo - this is a bit hacky we are saying there are no routes or there are routes but none of them have + //an upstream path template which means they are dyanmic and service discovery is on... + if ((!config.Routes.Any() || config.Routes.All(x => string.IsNullOrEmpty(x.UpstreamTemplatePattern?.OriginalValue))) && IsServiceDiscovery(config.ServiceProviderConfiguration)) + { + _logger.LogInformation($"Selected {nameof(DownstreamRouteCreator)} as DownstreamRouteProvider for this request"); + return _providers[nameof(DownstreamRouteCreator)]; + } + + return _providers[nameof(DownstreamRouteFinder)]; + } + + private bool IsServiceDiscovery(ServiceProviderConfiguration config) + { + if (!string.IsNullOrEmpty(config?.Host) && config?.Port > 0 && !string.IsNullOrEmpty(config.Type)) + { + return true; + } + + return false; + } + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProvider.cs b/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProvider.cs index b2809ac9a..ed2a657ef 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProvider.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/IDownstreamRouteProvider.cs @@ -1,10 +1,10 @@ -using Ocelot.Configuration; -using Ocelot.Responses; - -namespace Ocelot.DownstreamRouteFinder.Finder -{ - public interface IDownstreamRouteProvider - { - Response Get(string upstreamUrlPath, string upstreamQueryString, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost); - } -} +using Ocelot.Configuration; +using Ocelot.Responses; + +namespace Ocelot.DownstreamRouteFinder.Finder +{ + public interface IDownstreamRouteProvider + { + Response Get(string upstreamUrlPath, string upstreamQueryString, string upstreamHttpMethod, IInternalConfiguration configuration, string upstreamHost); + } +} diff --git a/src/Ocelot/DownstreamRouteFinder/Finder/UnableToFindDownstreamRouteError.cs b/src/Ocelot/DownstreamRouteFinder/Finder/UnableToFindDownstreamRouteError.cs index ad1b9c84a..b0396f208 100644 --- a/src/Ocelot/DownstreamRouteFinder/Finder/UnableToFindDownstreamRouteError.cs +++ b/src/Ocelot/DownstreamRouteFinder/Finder/UnableToFindDownstreamRouteError.cs @@ -5,7 +5,7 @@ namespace Ocelot.DownstreamRouteFinder.Finder public class UnableToFindDownstreamRouteError : Error { public UnableToFindDownstreamRouteError(string path, string httpVerb) - : base($"Failed to match ReRoute configuration for upstream path: {path}, verb: {httpVerb}.", OcelotErrorCode.UnableToFindDownstreamRouteError, 404) + : base($"Failed to match Route configuration for upstream path: {path}, verb: {httpVerb}.", OcelotErrorCode.UnableToFindDownstreamRouteError, 404) { } } diff --git a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs index 995ca5350..078622e4f 100644 --- a/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs +++ b/src/Ocelot/DownstreamRouteFinder/Middleware/DownstreamRouteFinderMiddleware.cs @@ -47,7 +47,7 @@ public async Task Invoke(HttpContext httpContext) return; } - var downstreamPathTemplates = string.Join(", ", response.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value)); + var downstreamPathTemplates = string.Join(", ", response.Data.Route.DownstreamRoute.Select(r => r.DownstreamPathTemplate.Value)); Logger.LogDebug($"downstream templates are {downstreamPathTemplates}"); // why set both of these on HttpContext diff --git a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs index 992a498f5..6014a21a3 100644 --- a/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs +++ b/src/Ocelot/DownstreamUrlCreator/Middleware/DownstreamUrlCreatorMiddleware.cs @@ -12,9 +12,9 @@ namespace Ocelot.DownstreamUrlCreator.Middleware using Ocelot.Responses; using Ocelot.Values; using System; - using System.Threading.Tasks; - using Ocelot.DownstreamRouteFinder.Middleware; - + using System.Threading.Tasks; + using Ocelot.DownstreamRouteFinder.Middleware; + public class DownstreamUrlCreatorMiddleware : OcelotMiddleware { private readonly RequestDelegate _next; @@ -32,15 +32,15 @@ IDownstreamPathPlaceholderReplacer replacer public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); - + var downstreamRoute = httpContext.Items.DownstreamRoute(); + var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues(); var response = _replacer - .Replace(downstreamReRoute.DownstreamPathTemplate.Value, templatePlaceholderNameAndValues); - - var downstreamRequest = httpContext.Items.DownstreamRequest(); - + .Replace(downstreamRoute.DownstreamPathTemplate.Value, templatePlaceholderNameAndValues); + + var downstreamRequest = httpContext.Items.DownstreamRequest(); + if (response.IsError) { Logger.LogDebug("IDownstreamPathPlaceholderReplacer returned an error, setting pipeline error"); @@ -49,19 +49,19 @@ public async Task Invoke(HttpContext httpContext) return; } - if (!string.IsNullOrEmpty(downstreamReRoute.DownstreamScheme)) - { - //todo make sure this works, hopefully there is a test ;E - httpContext.Items.DownstreamRequest().Scheme = downstreamReRoute.DownstreamScheme; - } - - var internalConfiguration = httpContext.Items.IInternalConfiguration(); - - if (ServiceFabricRequest(internalConfiguration, downstreamReRoute)) - { - var pathAndQuery = CreateServiceFabricUri(downstreamRequest, downstreamReRoute, templatePlaceholderNameAndValues, response); - - //todo check this works again hope there is a test.. + if (!string.IsNullOrEmpty(downstreamRoute.DownstreamScheme)) + { + //todo make sure this works, hopefully there is a test ;E + httpContext.Items.DownstreamRequest().Scheme = downstreamRoute.DownstreamScheme; + } + + var internalConfiguration = httpContext.Items.IInternalConfiguration(); + + if (ServiceFabricRequest(internalConfiguration, downstreamRoute)) + { + var pathAndQuery = CreateServiceFabricUri(downstreamRequest, downstreamRoute, templatePlaceholderNameAndValues, response); + + //todo check this works again hope there is a test.. downstreamRequest.AbsolutePath = pathAndQuery.path; downstreamRequest.Query = pathAndQuery.query; } @@ -70,7 +70,7 @@ public async Task Invoke(HttpContext httpContext) var dsPath = response.Data; if (ContainsQueryString(dsPath)) - { + { downstreamRequest.AbsolutePath = GetPath(dsPath); if (string.IsNullOrEmpty(downstreamRequest.Query)) @@ -83,7 +83,7 @@ public async Task Invoke(HttpContext httpContext) } } else - { + { RemoveQueryStringParametersThatHaveBeenUsedInTemplate(downstreamRequest, templatePlaceholderNameAndValues); downstreamRequest.AbsolutePath = dsPath.Value; @@ -133,17 +133,17 @@ private bool ContainsQueryString(DownstreamPath dsPath) return dsPath.Value.Contains("?"); } - private (string path, string query) CreateServiceFabricUri(DownstreamRequest downstreamRequest, DownstreamReRoute downstreamReRoute, List templatePlaceholderNameAndValues, Response dsPath) + private (string path, string query) CreateServiceFabricUri(DownstreamRequest downstreamRequest, DownstreamRoute downstreamRoute, List templatePlaceholderNameAndValues, Response dsPath) { var query = downstreamRequest.Query; - var serviceName = _replacer.Replace(downstreamReRoute.ServiceName, templatePlaceholderNameAndValues); + var serviceName = _replacer.Replace(downstreamRoute.ServiceName, templatePlaceholderNameAndValues); var pathTemplate = $"/{serviceName.Data.Value}{dsPath.Data.Value}"; return (pathTemplate, query); } - private static bool ServiceFabricRequest(IInternalConfiguration config, DownstreamReRoute downstreamReRoute) + private static bool ServiceFabricRequest(IInternalConfiguration config, DownstreamRoute downstreamRoute) { - return config.ServiceProviderConfiguration.Type?.ToLower() == "servicefabric" && downstreamReRoute.UseServiceDiscovery; + return config.ServiceProviderConfiguration.Type?.ToLower() == "servicefabric" && downstreamRoute.UseServiceDiscovery; } } } diff --git a/src/Ocelot/Headers/Middleware/ClaimsToHeadersMiddleware.cs b/src/Ocelot/Headers/Middleware/ClaimsToHeadersMiddleware.cs index 5d2bed0a5..62242ccc4 100644 --- a/src/Ocelot/Headers/Middleware/ClaimsToHeadersMiddleware.cs +++ b/src/Ocelot/Headers/Middleware/ClaimsToHeadersMiddleware.cs @@ -1,7 +1,7 @@ namespace Ocelot.Headers.Middleware { - using Microsoft.AspNetCore.Http; - using Ocelot.DownstreamRouteFinder.Middleware; + using Microsoft.AspNetCore.Http; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Logging; using Ocelot.Middleware; using System.Linq; @@ -23,15 +23,15 @@ public ClaimsToHeadersMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - if (downstreamReRoute.ClaimsToHeaders.Any()) + if (downstreamRoute.ClaimsToHeaders.Any()) { - Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers"); - - var downstreamRequest = httpContext.Items.DownstreamRequest(); + Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers"); - var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(downstreamReRoute.ClaimsToHeaders, httpContext.User.Claims, downstreamRequest); + var downstreamRequest = httpContext.Items.DownstreamRequest(); + + var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(downstreamRoute.ClaimsToHeaders, httpContext.User.Claims, downstreamRequest); if (response.IsError) { diff --git a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs index 3affff06d..5f8d98bb3 100644 --- a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs +++ b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs @@ -32,14 +32,14 @@ IAddHeadersToRequest addHeadersToRequest public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - var preFAndRs = downstreamReRoute.UpstreamHeadersFindAndReplace; + var preFAndRs = downstreamRoute.UpstreamHeadersFindAndReplace; //todo - this should be on httprequestmessage not httpcontext? _preReplacer.Replace(httpContext, preFAndRs); - _addHeadersToRequest.SetHeadersOnDownstreamRequest(downstreamReRoute.AddHeadersToUpstream, httpContext); + _addHeadersToRequest.SetHeadersOnDownstreamRequest(downstreamRoute.AddHeadersToUpstream, httpContext); await _next.Invoke(httpContext); @@ -50,13 +50,13 @@ public async Task Invoke(HttpContext httpContext) return; } - var postFAndRs = downstreamReRoute.DownstreamHeadersFindAndReplace; + var postFAndRs = downstreamRoute.DownstreamHeadersFindAndReplace; _postReplacer.Replace(httpContext, postFAndRs); var downstreamResponse = httpContext.Items.DownstreamResponse(); - _addHeadersToResponse.Add(downstreamReRoute.AddHeadersToDownstream, downstreamResponse); + _addHeadersToResponse.Add(downstreamRoute.AddHeadersToDownstream, downstreamResponse); } } } diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/CookieStickySessionsCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/CookieStickySessionsCreator.cs index 2f17882ec..32697f09b 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/CookieStickySessionsCreator.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/CookieStickySessionsCreator.cs @@ -1,21 +1,21 @@ -namespace Ocelot.LoadBalancer.LoadBalancers -{ - using System.Threading.Tasks; - using Ocelot.Configuration; - using Ocelot.Infrastructure; - using Ocelot.ServiceDiscovery.Providers; - using Ocelot.Responses; - - public class CookieStickySessionsCreator : ILoadBalancerCreator - { - public Response Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) - { - var loadBalancer = new RoundRobin(async () => await serviceProvider.Get()); - var bus = new InMemoryBus(); - return new OkResponse(new CookieStickySessions(loadBalancer, reRoute.LoadBalancerOptions.Key, - reRoute.LoadBalancerOptions.ExpiryInMs, bus)); - } - - public string Type => nameof(CookieStickySessions); - } -} +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using System.Threading.Tasks; + using Ocelot.Configuration; + using Ocelot.Infrastructure; + using Ocelot.ServiceDiscovery.Providers; + using Ocelot.Responses; + + public class CookieStickySessionsCreator : ILoadBalancerCreator + { + public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) + { + var loadBalancer = new RoundRobin(async () => await serviceProvider.Get()); + var bus = new InMemoryBus(); + return new OkResponse(new CookieStickySessions(loadBalancer, route.LoadBalancerOptions.Key, + route.LoadBalancerOptions.ExpiryInMs, bus)); + } + + public string Type => nameof(CookieStickySessions); + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/DelegateInvokingLoadBalancerCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/DelegateInvokingLoadBalancerCreator.cs index 3a055427e..25f5569dc 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/DelegateInvokingLoadBalancerCreator.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/DelegateInvokingLoadBalancerCreator.cs @@ -1,34 +1,34 @@ -namespace Ocelot.LoadBalancer.LoadBalancers -{ - using System; - using Ocelot.Configuration; - using Ocelot.ServiceDiscovery.Providers; - using Ocelot.Responses; - - public class DelegateInvokingLoadBalancerCreator : ILoadBalancerCreator - where T : ILoadBalancer - { - private readonly Func _creatorFunc; - - public DelegateInvokingLoadBalancerCreator( - Func creatorFunc) - { - _creatorFunc = creatorFunc; - } - - public Response Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) - { - try - { - return new OkResponse(_creatorFunc(reRoute, serviceProvider)); - - } - catch (Exception e) - { - return new ErrorResponse(new ErrorInvokingLoadBalancerCreator(e)); - } - } - - public string Type => typeof(T).Name; - } -} +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using System; + using Ocelot.Configuration; + using Ocelot.ServiceDiscovery.Providers; + using Ocelot.Responses; + + public class DelegateInvokingLoadBalancerCreator : ILoadBalancerCreator + where T : ILoadBalancer + { + private readonly Func _creatorFunc; + + public DelegateInvokingLoadBalancerCreator( + Func creatorFunc) + { + _creatorFunc = creatorFunc; + } + + public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) + { + try + { + return new OkResponse(_creatorFunc(route, serviceProvider)); + + } + catch (Exception e) + { + return new ErrorResponse(new ErrorInvokingLoadBalancerCreator(e)); + } + } + + public string Type => typeof(T).Name; + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerCreator.cs index c2df1daea..47700a27d 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerCreator.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerCreator.cs @@ -1,12 +1,12 @@ -namespace Ocelot.LoadBalancer.LoadBalancers -{ - using Ocelot.Responses; - using Ocelot.Configuration; - using Ocelot.ServiceDiscovery.Providers; - - public interface ILoadBalancerCreator - { - Response Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider); - string Type { get; } - } -} +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using Ocelot.Responses; + using Ocelot.Configuration; + using Ocelot.ServiceDiscovery.Providers; + + public interface ILoadBalancerCreator + { + Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider); + string Type { get; } + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs index 9053b6d46..6c490f37a 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerFactory.cs @@ -1,10 +1,10 @@ -namespace Ocelot.LoadBalancer.LoadBalancers -{ - using Ocelot.Configuration; - using Ocelot.Responses; - - public interface ILoadBalancerFactory - { - Response Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config); - } -} +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using Ocelot.Configuration; + using Ocelot.Responses; + + public interface ILoadBalancerFactory + { + Response Get(DownstreamRoute route, ServiceProviderConfiguration config); + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs index b5cfcfc09..b05a6aa49 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/ILoadBalancerHouse.cs @@ -1,10 +1,10 @@ -using Ocelot.Configuration; -using Ocelot.Responses; - -namespace Ocelot.LoadBalancer.LoadBalancers -{ - public interface ILoadBalancerHouse - { - Response Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config); - } -} +using Ocelot.Configuration; +using Ocelot.Responses; + +namespace Ocelot.LoadBalancer.LoadBalancers +{ + public interface ILoadBalancerHouse + { + Response Get(DownstreamRoute route, ServiceProviderConfiguration config); + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LeastConnectionCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LeastConnectionCreator.cs index 509a04a13..187b198f0 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/LeastConnectionCreator.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LeastConnectionCreator.cs @@ -1,16 +1,16 @@ -namespace Ocelot.LoadBalancer.LoadBalancers -{ - using Ocelot.Configuration; - using Ocelot.ServiceDiscovery.Providers; - using Ocelot.Responses; - - public class LeastConnectionCreator : ILoadBalancerCreator - { - public Response Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) - { - return new OkResponse(new LeastConnection(async () => await serviceProvider.Get(), reRoute.ServiceName)); - } - - public string Type => nameof(LeastConnection); - } -} +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using Ocelot.Configuration; + using Ocelot.ServiceDiscovery.Providers; + using Ocelot.Responses; + + public class LeastConnectionCreator : ILoadBalancerCreator + { + public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) + { + return new OkResponse(new LeastConnection(async () => await serviceProvider.Get(), route.ServiceName)); + } + + public string Type => nameof(LeastConnection); + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs index 31b6f74dd..33eb637a9 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerFactory.cs @@ -1,48 +1,48 @@ -namespace Ocelot.LoadBalancer.LoadBalancers -{ - using System.Collections.Generic; - using System.Linq; - using Ocelot.Configuration; - using Ocelot.Responses; - using Ocelot.ServiceDiscovery; - - public class LoadBalancerFactory : ILoadBalancerFactory - { - private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory; - private readonly IEnumerable _loadBalancerCreators; - - public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory, IEnumerable loadBalancerCreators) - { - _serviceProviderFactory = serviceProviderFactory; - _loadBalancerCreators = loadBalancerCreators; - } - - public Response Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config) - { - var serviceProviderFactoryResponse = _serviceProviderFactory.Get(config, reRoute); - - if (serviceProviderFactoryResponse.IsError) - { - return new ErrorResponse(serviceProviderFactoryResponse.Errors); - } - - var serviceProvider = serviceProviderFactoryResponse.Data; - var requestedType = reRoute.LoadBalancerOptions?.Type ?? nameof(NoLoadBalancer); - var applicableCreator = _loadBalancerCreators.SingleOrDefault(c => c.Type == requestedType); - - if (applicableCreator == null) - { - return new ErrorResponse(new CouldNotFindLoadBalancerCreator($"Could not find load balancer creator for Type: {requestedType}, please check your config specified the correct load balancer and that you have registered a class with the same name.")); - } - - var createdLoadBalancerResponse = applicableCreator.Create(reRoute, serviceProvider); - - if (createdLoadBalancerResponse.IsError) - { - return new ErrorResponse(createdLoadBalancerResponse.Errors); - } - - return new OkResponse(createdLoadBalancerResponse.Data); - } - } -} +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using System.Collections.Generic; + using System.Linq; + using Ocelot.Configuration; + using Ocelot.Responses; + using Ocelot.ServiceDiscovery; + + public class LoadBalancerFactory : ILoadBalancerFactory + { + private readonly IServiceDiscoveryProviderFactory _serviceProviderFactory; + private readonly IEnumerable _loadBalancerCreators; + + public LoadBalancerFactory(IServiceDiscoveryProviderFactory serviceProviderFactory, IEnumerable loadBalancerCreators) + { + _serviceProviderFactory = serviceProviderFactory; + _loadBalancerCreators = loadBalancerCreators; + } + + public Response Get(DownstreamRoute route, ServiceProviderConfiguration config) + { + var serviceProviderFactoryResponse = _serviceProviderFactory.Get(config, route); + + if (serviceProviderFactoryResponse.IsError) + { + return new ErrorResponse(serviceProviderFactoryResponse.Errors); + } + + var serviceProvider = serviceProviderFactoryResponse.Data; + var requestedType = route.LoadBalancerOptions?.Type ?? nameof(NoLoadBalancer); + var applicableCreator = _loadBalancerCreators.SingleOrDefault(c => c.Type == requestedType); + + if (applicableCreator == null) + { + return new ErrorResponse(new CouldNotFindLoadBalancerCreator($"Could not find load balancer creator for Type: {requestedType}, please check your config specified the correct load balancer and that you have registered a class with the same name.")); + } + + var createdLoadBalancerResponse = applicableCreator.Create(route, serviceProvider); + + if (createdLoadBalancerResponse.IsError) + { + return new ErrorResponse(createdLoadBalancerResponse.Errors); + } + + return new OkResponse(createdLoadBalancerResponse.Data); + } + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs index 2113457a0..4a9dd8235 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/LoadBalancerHouse.cs @@ -1,70 +1,70 @@ -using Ocelot.Configuration; -using Ocelot.Responses; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; - -namespace Ocelot.LoadBalancer.LoadBalancers -{ - public class LoadBalancerHouse : ILoadBalancerHouse - { - private readonly ILoadBalancerFactory _factory; - private readonly ConcurrentDictionary _loadBalancers; - - public LoadBalancerHouse(ILoadBalancerFactory factory) - { - _factory = factory; - _loadBalancers = new ConcurrentDictionary(); - } - - public Response Get(DownstreamReRoute reRoute, ServiceProviderConfiguration config) - { - try - { - Response result; - - if (_loadBalancers.TryGetValue(reRoute.LoadBalancerKey, out var loadBalancer)) - { - loadBalancer = _loadBalancers[reRoute.LoadBalancerKey]; - - if (reRoute.LoadBalancerOptions.Type != loadBalancer.GetType().Name) - { - result = _factory.Get(reRoute, config); - if (result.IsError) - { - return new ErrorResponse(result.Errors); - } - - loadBalancer = result.Data; - AddLoadBalancer(reRoute.LoadBalancerKey, loadBalancer); - } - - return new OkResponse(loadBalancer); - } - - result = _factory.Get(reRoute, config); - - if (result.IsError) - { - return new ErrorResponse(result.Errors); - } - - loadBalancer = result.Data; - AddLoadBalancer(reRoute.LoadBalancerKey, loadBalancer); - return new OkResponse(loadBalancer); - } - catch (Exception ex) - { - return new ErrorResponse(new List() - { - new UnableToFindLoadBalancerError($"unabe to find load balancer for {reRoute.LoadBalancerKey} exception is {ex}"), - }); - } - } - - private void AddLoadBalancer(string key, ILoadBalancer loadBalancer) - { - _loadBalancers.AddOrUpdate(key, loadBalancer, (x, y) => loadBalancer); - } - } -} +using Ocelot.Configuration; +using Ocelot.Responses; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Ocelot.LoadBalancer.LoadBalancers +{ + public class LoadBalancerHouse : ILoadBalancerHouse + { + private readonly ILoadBalancerFactory _factory; + private readonly ConcurrentDictionary _loadBalancers; + + public LoadBalancerHouse(ILoadBalancerFactory factory) + { + _factory = factory; + _loadBalancers = new ConcurrentDictionary(); + } + + public Response Get(DownstreamRoute route, ServiceProviderConfiguration config) + { + try + { + Response result; + + if (_loadBalancers.TryGetValue(route.LoadBalancerKey, out var loadBalancer)) + { + loadBalancer = _loadBalancers[route.LoadBalancerKey]; + + if (route.LoadBalancerOptions.Type != loadBalancer.GetType().Name) + { + result = _factory.Get(route, config); + if (result.IsError) + { + return new ErrorResponse(result.Errors); + } + + loadBalancer = result.Data; + AddLoadBalancer(route.LoadBalancerKey, loadBalancer); + } + + return new OkResponse(loadBalancer); + } + + result = _factory.Get(route, config); + + if (result.IsError) + { + return new ErrorResponse(result.Errors); + } + + loadBalancer = result.Data; + AddLoadBalancer(route.LoadBalancerKey, loadBalancer); + return new OkResponse(loadBalancer); + } + catch (Exception ex) + { + return new ErrorResponse(new List() + { + new UnableToFindLoadBalancerError($"unabe to find load balancer for {route.LoadBalancerKey} exception is {ex}"), + }); + } + } + + private void AddLoadBalancer(string key, ILoadBalancer loadBalancer) + { + _loadBalancers.AddOrUpdate(key, loadBalancer, (x, y) => loadBalancer); + } + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancerCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancerCreator.cs index ea2fdd7aa..3bf99737a 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancerCreator.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancerCreator.cs @@ -1,16 +1,16 @@ -namespace Ocelot.LoadBalancer.LoadBalancers -{ - using Ocelot.Configuration; - using Ocelot.ServiceDiscovery.Providers; - using Ocelot.Responses; - - public class NoLoadBalancerCreator : ILoadBalancerCreator - { - public Response Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) - { - return new OkResponse(new NoLoadBalancer(async () => await serviceProvider.Get())); - } - - public string Type => nameof(NoLoadBalancer); - } -} +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using Ocelot.Configuration; + using Ocelot.ServiceDiscovery.Providers; + using Ocelot.Responses; + + public class NoLoadBalancerCreator : ILoadBalancerCreator + { + public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) + { + return new OkResponse(new NoLoadBalancer(async () => await serviceProvider.Get())); + } + + public string Type => nameof(NoLoadBalancer); + } +} diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobinCreator.cs b/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobinCreator.cs index fd698133a..03d2cabbc 100644 --- a/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobinCreator.cs +++ b/src/Ocelot/LoadBalancer/LoadBalancers/RoundRobinCreator.cs @@ -1,16 +1,16 @@ -namespace Ocelot.LoadBalancer.LoadBalancers -{ - using Ocelot.Configuration; - using Ocelot.ServiceDiscovery.Providers; - using Ocelot.Responses; - - public class RoundRobinCreator : ILoadBalancerCreator - { - public Response Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) - { - return new OkResponse(new RoundRobin(async () => await serviceProvider.Get())); - } - - public string Type => nameof(RoundRobin); - } -} +namespace Ocelot.LoadBalancer.LoadBalancers +{ + using Ocelot.Configuration; + using Ocelot.ServiceDiscovery.Providers; + using Ocelot.Responses; + + public class RoundRobinCreator : ILoadBalancerCreator + { + public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) + { + return new OkResponse(new RoundRobin(async () => await serviceProvider.Get())); + } + + public string Type => nameof(RoundRobin); + } +} diff --git a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs index 56ad778e9..454fb7d67 100644 --- a/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs +++ b/src/Ocelot/LoadBalancer/Middleware/LoadBalancingMiddleware.cs @@ -1,7 +1,7 @@ namespace Ocelot.LoadBalancer.Middleware { - using Microsoft.AspNetCore.Http; - using Ocelot.DownstreamRouteFinder.Middleware; + using Microsoft.AspNetCore.Http; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.Logging; using Ocelot.Middleware; @@ -24,11 +24,11 @@ public LoadBalancingMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); - + var downstreamRoute = httpContext.Items.DownstreamRoute(); + var internalConfiguration = httpContext.Items.IInternalConfiguration(); - var loadBalancer = _loadBalancerHouse.Get(downstreamReRoute, internalConfiguration.ServiceProviderConfiguration); + var loadBalancer = _loadBalancerHouse.Get(downstreamRoute, internalConfiguration.ServiceProviderConfiguration); if (loadBalancer.IsError) { @@ -43,9 +43,9 @@ public async Task Invoke(HttpContext httpContext) Logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error"); httpContext.Items.UpsertErrors(hostAndPort.Errors); return; - } - - var downstreamRequest = httpContext.Items.DownstreamRequest(); + } + + var downstreamRequest = httpContext.Items.DownstreamRequest(); //todo check downstreamRequest is ok downstreamRequest.Host = hostAndPort.Data.DownstreamHost; diff --git a/src/Ocelot/Middleware/HttpItemsExtensions.cs b/src/Ocelot/Middleware/HttpItemsExtensions.cs index 290e9383f..46652869e 100644 --- a/src/Ocelot/Middleware/HttpItemsExtensions.cs +++ b/src/Ocelot/Middleware/HttpItemsExtensions.cs @@ -19,9 +19,9 @@ public static void UpsertDownstreamResponse(this IDictionary inp input.Upsert("DownstreamResponse", downstreamResponse); } - public static void UpsertDownstreamReRoute(this IDictionary input, DownstreamReRoute downstreamReRoute) + public static void UpsertDownstreamRoute(this IDictionary input, Configuration.DownstreamRoute downstreamRoute) { - input.Upsert("DownstreamReRoute", downstreamReRoute); + input.Upsert("DownstreamRoute", downstreamRoute); } public static void UpsertTemplatePlaceholderNameAndValues(this IDictionary input, List tPNV) @@ -29,9 +29,9 @@ public static void UpsertTemplatePlaceholderNameAndValues(this IDictionary input, DownstreamRoute downstreamRoute) + public static void UpsertDownstreamRoute(this IDictionary input, DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) { - input.Upsert("DownstreamRoute", downstreamRoute); + input.Upsert("DownstreamRouteHolder", downstreamRoute); } public static void UpsertErrors(this IDictionary input, List errors) @@ -61,9 +61,9 @@ public static List Errors(this IDictionary input) return errors == null ? new List() : errors; } - public static DownstreamRoute DownstreamRoute(this IDictionary input) + public static DownstreamRouteFinder.DownstreamRouteHolder DownstreamRouteHolder(this IDictionary input) { - return input.Get("DownstreamRoute"); + return input.Get("DownstreamRouteHolder"); } public static List TemplatePlaceholderNameAndValues(this IDictionary input) @@ -81,9 +81,9 @@ public static DownstreamResponse DownstreamResponse(this IDictionary("DownstreamResponse"); } - public static DownstreamReRoute DownstreamReRoute(this IDictionary input) + public static Configuration.DownstreamRoute DownstreamRoute(this IDictionary input) { - return input.Get("DownstreamReRoute"); + return input.Get("DownstreamRoute"); } private static T Get(this IDictionary input, string key) diff --git a/src/Ocelot/Multiplexer/IDefinedAggregatorProvider.cs b/src/Ocelot/Multiplexer/IDefinedAggregatorProvider.cs index 7d4d1fddc..2e3941dcb 100644 --- a/src/Ocelot/Multiplexer/IDefinedAggregatorProvider.cs +++ b/src/Ocelot/Multiplexer/IDefinedAggregatorProvider.cs @@ -5,6 +5,6 @@ namespace Ocelot.Multiplexer { public interface IDefinedAggregatorProvider { - Response Get(ReRoute reRoute); + Response Get(Route route); } } diff --git a/src/Ocelot/Multiplexer/IResponseAggregator.cs b/src/Ocelot/Multiplexer/IResponseAggregator.cs index 0ef8615ba..9102b13b7 100644 --- a/src/Ocelot/Multiplexer/IResponseAggregator.cs +++ b/src/Ocelot/Multiplexer/IResponseAggregator.cs @@ -7,6 +7,6 @@ public interface IResponseAggregator { - Task Aggregate(ReRoute reRoute, HttpContext originalContext, List downstreamResponses); + Task Aggregate(Route route, HttpContext originalContext, List downstreamResponses); } } diff --git a/src/Ocelot/Multiplexer/IResponseAggregatorFactory.cs b/src/Ocelot/Multiplexer/IResponseAggregatorFactory.cs index a3a08c2fd..3d829711e 100644 --- a/src/Ocelot/Multiplexer/IResponseAggregatorFactory.cs +++ b/src/Ocelot/Multiplexer/IResponseAggregatorFactory.cs @@ -4,6 +4,6 @@ namespace Ocelot.Multiplexer public interface IResponseAggregatorFactory { - IResponseAggregator Get(ReRoute reRoute); + IResponseAggregator Get(Route route); } } diff --git a/src/Ocelot/Multiplexer/InMemoryResponseAggregatorFactory.cs b/src/Ocelot/Multiplexer/InMemoryResponseAggregatorFactory.cs index 64ec74fd9..8983caea9 100644 --- a/src/Ocelot/Multiplexer/InMemoryResponseAggregatorFactory.cs +++ b/src/Ocelot/Multiplexer/InMemoryResponseAggregatorFactory.cs @@ -13,9 +13,9 @@ public InMemoryResponseAggregatorFactory(IDefinedAggregatorProvider provider, IR _simple = responseAggregator; } - public IResponseAggregator Get(ReRoute reRoute) + public IResponseAggregator Get(Route route) { - if (!string.IsNullOrEmpty(reRoute.Aggregator)) + if (!string.IsNullOrEmpty(route.Aggregator)) { return _userDefined; } diff --git a/src/Ocelot/Multiplexer/MultiplexingMiddleware.cs b/src/Ocelot/Multiplexer/MultiplexingMiddleware.cs index 01d6fbb5c..f5e3ba0ec 100644 --- a/src/Ocelot/Multiplexer/MultiplexingMiddleware.cs +++ b/src/Ocelot/Multiplexer/MultiplexingMiddleware.cs @@ -29,19 +29,19 @@ public async Task Invoke(HttpContext httpContext) if (httpContext.WebSockets.IsWebSocketRequest) { //todo this is obviously stupid - httpContext.Items.UpsertDownstreamReRoute(httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute[0]); + httpContext.Items.UpsertDownstreamRoute(httpContext.Items.DownstreamRouteHolder().Route.DownstreamRoute[0]); await _next.Invoke(httpContext); return; } - var reRouteKeysConfigs = httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRouteConfig; - if (reRouteKeysConfigs == null || !reRouteKeysConfigs.Any()) + var routeKeysConfigs = httpContext.Items.DownstreamRouteHolder().Route.DownstreamRouteConfig; + if (routeKeysConfigs == null || !routeKeysConfigs.Any()) { - var downstreamRoute = httpContext.Items.DownstreamRoute(); + var downstreamRouteHolder = httpContext.Items.DownstreamRouteHolder(); - var tasks = new Task[downstreamRoute.ReRoute.DownstreamReRoute.Count]; + var tasks = new Task[downstreamRouteHolder.Route.DownstreamRoute.Count]; - for (var i = 0; i < downstreamRoute.ReRoute.DownstreamReRoute.Count; i++) + for (var i = 0; i < downstreamRouteHolder.Route.DownstreamRoute.Count; i++) { var newHttpContext = Copy(httpContext); @@ -52,7 +52,7 @@ public async Task Invoke(HttpContext httpContext) newHttpContext.Items .UpsertTemplatePlaceholderNameAndValues(httpContext.Items.TemplatePlaceholderNameAndValues()); newHttpContext.Items - .UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[i]); + .UpsertDownstreamRoute(downstreamRouteHolder.Route.DownstreamRoute[i]); tasks[i] = Fire(newHttpContext, _next); } @@ -67,14 +67,14 @@ public async Task Invoke(HttpContext httpContext) contexts.Add(finished); } - await Map(httpContext, downstreamRoute.ReRoute, contexts); + await Map(httpContext, downstreamRouteHolder.Route, contexts); } else { - httpContext.Items.UpsertDownstreamReRoute(httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute[0]); + httpContext.Items.UpsertDownstreamRoute(httpContext.Items.DownstreamRouteHolder().Route.DownstreamRoute[0]); var mainResponse = await Fire(httpContext, _next); - if (httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute.Count == 1) + if (httpContext.Items.DownstreamRouteHolder().Route.DownstreamRoute.Count == 1) { MapNotAggregate(httpContext, new List() { mainResponse }); return; @@ -91,14 +91,14 @@ public async Task Invoke(HttpContext httpContext) var jObject = Newtonsoft.Json.Linq.JToken.Parse(content); - for (var i = 1; i < httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute.Count; i++) + for (var i = 1; i < httpContext.Items.DownstreamRouteHolder().Route.DownstreamRoute.Count; i++) { var templatePlaceholderNameAndValues = httpContext.Items.TemplatePlaceholderNameAndValues(); - var downstreamReRoute = httpContext.Items.DownstreamRoute().ReRoute.DownstreamReRoute[i]; + var downstreamRoute = httpContext.Items.DownstreamRouteHolder().Route.DownstreamRoute[i]; - var matchAdvancedAgg = reRouteKeysConfigs - .FirstOrDefault(q => q.ReRouteKey == downstreamReRoute.Key); + var matchAdvancedAgg = routeKeysConfigs + .FirstOrDefault(q => q.RouteKey == downstreamRoute.Key); if (matchAdvancedAgg != null) { @@ -121,7 +121,7 @@ public async Task Invoke(HttpContext httpContext) .UpsertTemplatePlaceholderNameAndValues(tPNV); newHttpContext.Items - .UpsertDownstreamReRoute(downstreamReRoute); + .UpsertDownstreamRoute(downstreamRoute); tasks.Add(Fire(newHttpContext, _next)); } @@ -140,7 +140,7 @@ public async Task Invoke(HttpContext httpContext) .UpsertTemplatePlaceholderNameAndValues(templatePlaceholderNameAndValues); newHttpContext.Items - .UpsertDownstreamReRoute(downstreamReRoute); + .UpsertDownstreamRoute(downstreamRoute); tasks.Add(Fire(newHttpContext, _next)); } @@ -156,7 +156,7 @@ public async Task Invoke(HttpContext httpContext) contexts.Add(finished); } - await Map(httpContext, httpContext.Items.DownstreamRoute().ReRoute, contexts); + await Map(httpContext, httpContext.Items.DownstreamRouteHolder().Route, contexts); } } @@ -187,12 +187,12 @@ private HttpContext Copy(HttpContext source) return target; } - private async Task Map(HttpContext httpContext, ReRoute reRoute, List contexts) + private async Task Map(HttpContext httpContext, Route route, List contexts) { - if (reRoute.DownstreamReRoute.Count > 1) + if (route.DownstreamRoute.Count > 1) { - var aggregator = _factory.Get(reRoute); - await aggregator.Aggregate(reRoute, httpContext, contexts); + var aggregator = _factory.Get(route); + await aggregator.Aggregate(route, httpContext, contexts); } else { diff --git a/src/Ocelot/Multiplexer/ServiceLocatorDefinedAggregatorProvider.cs b/src/Ocelot/Multiplexer/ServiceLocatorDefinedAggregatorProvider.cs index 6029e30e6..8848b23f3 100644 --- a/src/Ocelot/Multiplexer/ServiceLocatorDefinedAggregatorProvider.cs +++ b/src/Ocelot/Multiplexer/ServiceLocatorDefinedAggregatorProvider.cs @@ -16,14 +16,14 @@ public ServiceLocatorDefinedAggregatorProvider(IServiceProvider services) _aggregators = services.GetServices().ToDictionary(x => x.GetType().Name); } - public Response Get(ReRoute reRoute) + public Response Get(Route route) { - if (_aggregators.ContainsKey(reRoute.Aggregator)) + if (_aggregators.ContainsKey(route.Aggregator)) { - return new OkResponse(_aggregators[reRoute.Aggregator]); + return new OkResponse(_aggregators[route.Aggregator]); } - return new ErrorResponse(new CouldNotFindAggregatorError(reRoute.Aggregator)); + return new ErrorResponse(new CouldNotFindAggregatorError(route.Aggregator)); } } } diff --git a/src/Ocelot/Multiplexer/SimpleJsonResponseAggregator.cs b/src/Ocelot/Multiplexer/SimpleJsonResponseAggregator.cs index 52d0800b5..2eea6c51a 100644 --- a/src/Ocelot/Multiplexer/SimpleJsonResponseAggregator.cs +++ b/src/Ocelot/Multiplexer/SimpleJsonResponseAggregator.cs @@ -13,7 +13,7 @@ namespace Ocelot.Multiplexer { public class SimpleJsonResponseAggregator : IResponseAggregator { - public async Task Aggregate(ReRoute reRoute, HttpContext originalContext, List downstreamContexts) + public async Task Aggregate(Route route, HttpContext originalContext, List downstreamContexts) { await MapAggregateContent(originalContext, downstreamContexts); } @@ -24,11 +24,11 @@ private static async Task MapAggregateContent(HttpContext originalContext, List< contentBuilder.Append("{"); - var responseKeys = downstreamContexts.Select(s => s.Items.DownstreamReRoute().Key).Distinct().ToList(); + var responseKeys = downstreamContexts.Select(s => s.Items.DownstreamRoute().Key).Distinct().ToList(); for (var k = 0; k < responseKeys.Count; k++) { - var contexts = downstreamContexts.Where(w => w.Items.DownstreamReRoute().Key == responseKeys[k]).ToList(); + var contexts = downstreamContexts.Where(w => w.Items.DownstreamRoute().Key == responseKeys[k]).ToList(); if (contexts.Count == 1) { if (contexts[0].Items.Errors().Count > 0) diff --git a/src/Ocelot/Multiplexer/UserDefinedResponseAggregator.cs b/src/Ocelot/Multiplexer/UserDefinedResponseAggregator.cs index 28f32f18c..5c122be1d 100644 --- a/src/Ocelot/Multiplexer/UserDefinedResponseAggregator.cs +++ b/src/Ocelot/Multiplexer/UserDefinedResponseAggregator.cs @@ -15,9 +15,9 @@ public UserDefinedResponseAggregator(IDefinedAggregatorProvider provider) _provider = provider; } - public async Task Aggregate(ReRoute reRoute, HttpContext originalContext, List downstreamResponses) + public async Task Aggregate(Route route, HttpContext originalContext, List downstreamResponses) { - var aggregator = _provider.Get(reRoute); + var aggregator = _provider.Get(route); if (!aggregator.IsError) { diff --git a/src/Ocelot/QueryStrings/Middleware/ClaimsToQueryStringMiddleware.cs b/src/Ocelot/QueryStrings/Middleware/ClaimsToQueryStringMiddleware.cs index d8426e989..5044c5f0f 100644 --- a/src/Ocelot/QueryStrings/Middleware/ClaimsToQueryStringMiddleware.cs +++ b/src/Ocelot/QueryStrings/Middleware/ClaimsToQueryStringMiddleware.cs @@ -4,9 +4,9 @@ using Ocelot.Middleware; using System.Linq; using System.Threading.Tasks; - using Microsoft.AspNetCore.Http; - using Ocelot.DownstreamRouteFinder.Middleware; - + using Microsoft.AspNetCore.Http; + using Ocelot.DownstreamRouteFinder.Middleware; + public class ClaimsToQueryStringMiddleware : OcelotMiddleware { private readonly RequestDelegate _next; @@ -23,15 +23,15 @@ public ClaimsToQueryStringMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - if (downstreamReRoute.ClaimsToQueries.Any()) + if (downstreamRoute.ClaimsToQueries.Any()) { - Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries"); - + Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries"); + var downstreamRequest = httpContext.Items.DownstreamRequest(); - var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(downstreamReRoute.ClaimsToQueries, httpContext.User.Claims, downstreamRequest); + var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(downstreamRoute.ClaimsToQueries, httpContext.User.Claims, downstreamRequest); if (response.IsError) { diff --git a/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs b/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs index 76d01e94b..fac4823ef 100644 --- a/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs +++ b/src/Ocelot/RateLimit/Middleware/ClientRateLimitMiddleware.cs @@ -26,14 +26,14 @@ public ClientRateLimitMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - var options = downstreamReRoute.RateLimitOptions; + var options = downstreamRoute.RateLimitOptions; // check if rate limiting is enabled - if (!downstreamReRoute.EnableEndpointEndpointRateLimiting) + if (!downstreamRoute.EnableEndpointEndpointRateLimiting) { - Logger.LogInformation($"EndpointRateLimiting is not enabled for {downstreamReRoute.DownstreamPathTemplate.Value}"); + Logger.LogInformation($"EndpointRateLimiting is not enabled for {downstreamRoute.DownstreamPathTemplate.Value}"); await _next.Invoke(httpContext); return; } @@ -44,7 +44,7 @@ public async Task Invoke(HttpContext httpContext) // check white list if (IsWhitelisted(identity, options)) { - Logger.LogInformation($"{downstreamReRoute.DownstreamPathTemplate.Value} is white listed from rate limiting"); + Logger.LogInformation($"{downstreamRoute.DownstreamPathTemplate.Value} is white listed from rate limiting"); await _next.Invoke(httpContext); return; } @@ -62,7 +62,7 @@ public async Task Invoke(HttpContext httpContext) var retryAfter = _processor.RetryAfterFrom(counter.Timestamp, rule); // log blocked request - LogBlockedRequest(httpContext, identity, counter, rule, downstreamReRoute); + LogBlockedRequest(httpContext, identity, counter, rule, downstreamRoute); var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture); @@ -112,10 +112,10 @@ public bool IsWhitelisted(ClientRequestIdentity requestIdentity, RateLimitOption return false; } - public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamReRoute downstreamReRoute) + public virtual void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule, DownstreamRoute downstreamRoute) { Logger.LogInformation( - $"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { downstreamReRoute.UpstreamPathTemplate.OriginalValue }, TraceIdentifier {httpContext.TraceIdentifier}."); + $"Request {identity.HttpVerb}:{identity.Path} from ClientId {identity.ClientId} has been blocked, quota {rule.Limit}/{rule.Period} exceeded by {counter.TotalRequests}. Blocked by rule { downstreamRoute.UpstreamPathTemplate.OriginalValue }, TraceIdentifier {httpContext.TraceIdentifier}."); } public virtual DownstreamResponse ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitOptions option, string retryAfter) diff --git a/src/Ocelot/Request/Mapper/IRequestMapper.cs b/src/Ocelot/Request/Mapper/IRequestMapper.cs index cd075c5df..7667a0b41 100644 --- a/src/Ocelot/Request/Mapper/IRequestMapper.cs +++ b/src/Ocelot/Request/Mapper/IRequestMapper.cs @@ -1,13 +1,13 @@ -namespace Ocelot.Request.Mapper -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration; - using Ocelot.Responses; - using System.Net.Http; - using System.Threading.Tasks; - - public interface IRequestMapper - { - Task> Map(HttpRequest request, DownstreamReRoute downstreamReRoute); - } -} +namespace Ocelot.Request.Mapper +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration; + using Ocelot.Responses; + using System.Net.Http; + using System.Threading.Tasks; + + public interface IRequestMapper + { + Task> Map(HttpRequest request, DownstreamRoute downstreamRoute); + } +} diff --git a/src/Ocelot/Request/Mapper/RequestMapper.cs b/src/Ocelot/Request/Mapper/RequestMapper.cs index 7d383a704..304f42227 100644 --- a/src/Ocelot/Request/Mapper/RequestMapper.cs +++ b/src/Ocelot/Request/Mapper/RequestMapper.cs @@ -1,119 +1,119 @@ -namespace Ocelot.Request.Mapper -{ - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.Http.Extensions; - using Microsoft.Extensions.Primitives; - using Ocelot.Configuration; - using Ocelot.Responses; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net.Http; - using System.Threading.Tasks; - - public class RequestMapper : IRequestMapper - { - private readonly string[] _unsupportedHeaders = { "host" }; - - public async Task> Map(HttpRequest request, DownstreamReRoute downstreamReRoute) - { - try - { - var requestMessage = new HttpRequestMessage() - { - Content = await MapContent(request), - Method = MapMethod(request, downstreamReRoute), - RequestUri = MapUri(request), - Version = downstreamReRoute.DownstreamHttpVersion, - }; - - MapHeaders(request, requestMessage); - - return new OkResponse(requestMessage); - } - catch (Exception ex) - { - return new ErrorResponse(new UnmappableRequestError(ex)); - } - } - - private async Task MapContent(HttpRequest request) - { - if (request.Body == null || (request.Body.CanSeek && request.Body.Length <= 0)) - { - return null; - } - - // Never change this to StreamContent again, I forgot it doesnt work in #464. - var content = new ByteArrayContent(await ToByteArray(request.Body)); - - if (!string.IsNullOrEmpty(request.ContentType)) - { - content.Headers - .TryAddWithoutValidation("Content-Type", new[] { request.ContentType }); - } - - AddHeaderIfExistsOnRequest("Content-Language", content, request); - AddHeaderIfExistsOnRequest("Content-Location", content, request); - AddHeaderIfExistsOnRequest("Content-Range", content, request); - AddHeaderIfExistsOnRequest("Content-MD5", content, request); - AddHeaderIfExistsOnRequest("Content-Disposition", content, request); - AddHeaderIfExistsOnRequest("Content-Encoding", content, request); - - return content; - } - - private void AddHeaderIfExistsOnRequest(string key, HttpContent content, HttpRequest request) - { - if (request.Headers.ContainsKey(key)) - { - content.Headers - .TryAddWithoutValidation(key, request.Headers[key].ToList()); - } - } - - private HttpMethod MapMethod(HttpRequest request, DownstreamReRoute downstreamReRoute) - { - if (!string.IsNullOrEmpty(downstreamReRoute?.DownstreamHttpMethod)) - { - return new HttpMethod(downstreamReRoute.DownstreamHttpMethod); - } - - return new HttpMethod(request.Method); - } - - private Uri MapUri(HttpRequest request) - { - return new Uri(request.GetEncodedUrl()); - } - - private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage) - { - foreach (var header in request.Headers) - { - if (IsSupportedHeader(header)) - { - requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); - } - } - } - - private bool IsSupportedHeader(KeyValuePair header) - { - return !_unsupportedHeaders.Contains(header.Key.ToLower()); - } - - private async Task ToByteArray(Stream stream) - { - using (stream) - { - using (var memStream = new MemoryStream()) - { - await stream.CopyToAsync(memStream); - return memStream.ToArray(); - } - } - } - } -} +namespace Ocelot.Request.Mapper +{ + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Extensions; + using Microsoft.Extensions.Primitives; + using Ocelot.Configuration; + using Ocelot.Responses; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net.Http; + using System.Threading.Tasks; + + public class RequestMapper : IRequestMapper + { + private readonly string[] _unsupportedHeaders = { "host" }; + + public async Task> Map(HttpRequest request, DownstreamRoute downstreamRoute) + { + try + { + var requestMessage = new HttpRequestMessage() + { + Content = await MapContent(request), + Method = MapMethod(request, downstreamRoute), + RequestUri = MapUri(request), + Version = downstreamRoute.DownstreamHttpVersion, + }; + + MapHeaders(request, requestMessage); + + return new OkResponse(requestMessage); + } + catch (Exception ex) + { + return new ErrorResponse(new UnmappableRequestError(ex)); + } + } + + private async Task MapContent(HttpRequest request) + { + if (request.Body == null || (request.Body.CanSeek && request.Body.Length <= 0)) + { + return null; + } + + // Never change this to StreamContent again, I forgot it doesnt work in #464. + var content = new ByteArrayContent(await ToByteArray(request.Body)); + + if (!string.IsNullOrEmpty(request.ContentType)) + { + content.Headers + .TryAddWithoutValidation("Content-Type", new[] { request.ContentType }); + } + + AddHeaderIfExistsOnRequest("Content-Language", content, request); + AddHeaderIfExistsOnRequest("Content-Location", content, request); + AddHeaderIfExistsOnRequest("Content-Range", content, request); + AddHeaderIfExistsOnRequest("Content-MD5", content, request); + AddHeaderIfExistsOnRequest("Content-Disposition", content, request); + AddHeaderIfExistsOnRequest("Content-Encoding", content, request); + + return content; + } + + private void AddHeaderIfExistsOnRequest(string key, HttpContent content, HttpRequest request) + { + if (request.Headers.ContainsKey(key)) + { + content.Headers + .TryAddWithoutValidation(key, request.Headers[key].ToList()); + } + } + + private HttpMethod MapMethod(HttpRequest request, DownstreamRoute downstreamRoute) + { + if (!string.IsNullOrEmpty(downstreamRoute?.DownstreamHttpMethod)) + { + return new HttpMethod(downstreamRoute.DownstreamHttpMethod); + } + + return new HttpMethod(request.Method); + } + + private Uri MapUri(HttpRequest request) + { + return new Uri(request.GetEncodedUrl()); + } + + private void MapHeaders(HttpRequest request, HttpRequestMessage requestMessage) + { + foreach (var header in request.Headers) + { + if (IsSupportedHeader(header)) + { + requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + } + } + } + + private bool IsSupportedHeader(KeyValuePair header) + { + return !_unsupportedHeaders.Contains(header.Key.ToLower()); + } + + private async Task ToByteArray(Stream stream) + { + using (stream) + { + using (var memStream = new MemoryStream()) + { + await stream.CopyToAsync(memStream); + return memStream.ToArray(); + } + } + } + } +} diff --git a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs index 9008c85cb..0b73ec16e 100644 --- a/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs +++ b/src/Ocelot/Request/Middleware/DownstreamRequestInitialiserMiddleware.cs @@ -26,9 +26,9 @@ public DownstreamRequestInitialiserMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - var httpRequestMessage = await _requestMapper.Map(httpContext.Request, downstreamReRoute); + var httpRequestMessage = await _requestMapper.Map(httpContext.Request, downstreamRoute); if (httpRequestMessage.IsError) { diff --git a/src/Ocelot/RequestId/Middleware/ReRouteRequestIdMiddleware.cs b/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs similarity index 85% rename from src/Ocelot/RequestId/Middleware/ReRouteRequestIdMiddleware.cs rename to src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs index fa7e0db0b..8bc7642a5 100644 --- a/src/Ocelot/RequestId/Middleware/ReRouteRequestIdMiddleware.cs +++ b/src/Ocelot/RequestId/Middleware/RequestIdMiddleware.cs @@ -11,14 +11,14 @@ namespace Ocelot.RequestId.Middleware using System.Net.Http.Headers; using System.Threading.Tasks; - public class ReRouteRequestIdMiddleware : OcelotMiddleware + public class RequestIdMiddleware : OcelotMiddleware { private readonly RequestDelegate _next; private readonly IRequestScopedDataRepository _requestScopedDataRepository; - public ReRouteRequestIdMiddleware(RequestDelegate next, + public RequestIdMiddleware(RequestDelegate next, IOcelotLoggerFactory loggerFactory, IRequestScopedDataRepository requestScopedDataRepository) - : base(loggerFactory.CreateLogger()) + : base(loggerFactory.CreateLogger()) { _next = next; _requestScopedDataRepository = requestScopedDataRepository; @@ -32,9 +32,9 @@ public async Task Invoke(HttpContext httpContext) private void SetOcelotRequestId(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); - var key = downstreamReRoute.RequestIdKey ?? DefaultRequestIdKey.Value; + var key = downstreamRoute.RequestIdKey ?? DefaultRequestIdKey.Value; if (httpContext.Request.Headers.TryGetValue(key, out var upstreamRequestIds)) { @@ -52,7 +52,7 @@ private void SetOcelotRequestId(HttpContext httpContext) } } - var requestId = new RequestId(downstreamReRoute.RequestIdKey, httpContext.TraceIdentifier); + var requestId = new RequestId(downstreamRoute.RequestIdKey, httpContext.TraceIdentifier); var downstreamRequest = httpContext.Items.DownstreamRequest(); diff --git a/src/Ocelot/RequestId/Middleware/RequestIdMiddlewareExtensions.cs b/src/Ocelot/RequestId/Middleware/RequestIdMiddlewareExtensions.cs index 8f872da58..11ceeaa62 100644 --- a/src/Ocelot/RequestId/Middleware/RequestIdMiddlewareExtensions.cs +++ b/src/Ocelot/RequestId/Middleware/RequestIdMiddlewareExtensions.cs @@ -6,7 +6,7 @@ public static class RequestIdMiddlewareExtensions { public static IApplicationBuilder UseRequestIdMiddleware(this IApplicationBuilder builder) { - return builder.UseMiddleware(); + return builder.UseMiddleware(); } } } diff --git a/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs b/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs index 3e868b322..e153d0ec2 100644 --- a/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs +++ b/src/Ocelot/Requester/DelegatingHandlerHandlerFactory.cs @@ -1,106 +1,106 @@ -namespace Ocelot.Requester -{ - using Logging; - using Microsoft.Extensions.DependencyInjection; - using Ocelot.Configuration; - using Ocelot.Responses; - using QoS; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net.Http; - - public class DelegatingHandlerHandlerFactory : IDelegatingHandlerHandlerFactory - { - private readonly ITracingHandlerFactory _tracingFactory; - private readonly IQoSFactory _qoSFactory; - private readonly IServiceProvider _serviceProvider; - private readonly IOcelotLogger _logger; - - public DelegatingHandlerHandlerFactory( - ITracingHandlerFactory tracingFactory, - IQoSFactory qoSFactory, - IServiceProvider serviceProvider, - IOcelotLoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger(); - _serviceProvider = serviceProvider; - _tracingFactory = tracingFactory; - _qoSFactory = qoSFactory; - } - - public Response>> Get(DownstreamReRoute downstreamReRoute) - { - var globalDelegatingHandlers = _serviceProvider - .GetServices() - .ToList(); - - var reRouteSpecificHandlers = _serviceProvider - .GetServices() - .ToList(); - - var handlers = new List>(); - - foreach (var handler in globalDelegatingHandlers) - { - if (GlobalIsInHandlersConfig(downstreamReRoute, handler)) - { - reRouteSpecificHandlers.Add(handler.DelegatingHandler); - } - else - { - handlers.Add(() => handler.DelegatingHandler); - } - } - - if (downstreamReRoute.DelegatingHandlers.Any()) - { - var sorted = SortByConfigOrder(downstreamReRoute, reRouteSpecificHandlers); - - foreach (var handler in sorted) - { - handlers.Add(() => handler); - } - } - - if (downstreamReRoute.HttpHandlerOptions.UseTracing) - { - handlers.Add(() => (DelegatingHandler)_tracingFactory.Get()); - } - - if (downstreamReRoute.QosOptions.UseQos) - { - var handler = _qoSFactory.Get(downstreamReRoute); - - if (handler != null && !handler.IsError) - { - handlers.Add(() => handler.Data); - } - else - { - _logger.LogWarning($"ReRoute {downstreamReRoute.UpstreamPathTemplate} specifies use QoS but no QosHandler found in DI container. Will use not use a QosHandler, please check your setup!"); - handlers.Add(() => new NoQosDelegatingHandler()); - } - } - - return new OkResponse>>(handlers); - } - - private List SortByConfigOrder(DownstreamReRoute request, List reRouteSpecificHandlers) - { - return reRouteSpecificHandlers - .Where(x => request.DelegatingHandlers.Contains(x.GetType().Name)) - .OrderBy(d => - { - var type = d.GetType().Name; - var pos = request.DelegatingHandlers.IndexOf(type); - return pos; - }).ToList(); - } - - private bool GlobalIsInHandlersConfig(DownstreamReRoute request, GlobalDelegatingHandler handler) - { - return request.DelegatingHandlers.Contains(handler.DelegatingHandler.GetType().Name); - } - } -} +namespace Ocelot.Requester +{ + using Logging; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Configuration; + using Ocelot.Responses; + using QoS; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + + public class DelegatingHandlerHandlerFactory : IDelegatingHandlerHandlerFactory + { + private readonly ITracingHandlerFactory _tracingFactory; + private readonly IQoSFactory _qoSFactory; + private readonly IServiceProvider _serviceProvider; + private readonly IOcelotLogger _logger; + + public DelegatingHandlerHandlerFactory( + ITracingHandlerFactory tracingFactory, + IQoSFactory qoSFactory, + IServiceProvider serviceProvider, + IOcelotLoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + _serviceProvider = serviceProvider; + _tracingFactory = tracingFactory; + _qoSFactory = qoSFactory; + } + + public Response>> Get(DownstreamRoute downstreamRoute) + { + var globalDelegatingHandlers = _serviceProvider + .GetServices() + .ToList(); + + var routeSpecificHandlers = _serviceProvider + .GetServices() + .ToList(); + + var handlers = new List>(); + + foreach (var handler in globalDelegatingHandlers) + { + if (GlobalIsInHandlersConfig(downstreamRoute, handler)) + { + routeSpecificHandlers.Add(handler.DelegatingHandler); + } + else + { + handlers.Add(() => handler.DelegatingHandler); + } + } + + if (downstreamRoute.DelegatingHandlers.Any()) + { + var sorted = SortByConfigOrder(downstreamRoute, routeSpecificHandlers); + + foreach (var handler in sorted) + { + handlers.Add(() => handler); + } + } + + if (downstreamRoute.HttpHandlerOptions.UseTracing) + { + handlers.Add(() => (DelegatingHandler)_tracingFactory.Get()); + } + + if (downstreamRoute.QosOptions.UseQos) + { + var handler = _qoSFactory.Get(downstreamRoute); + + if (handler != null && !handler.IsError) + { + handlers.Add(() => handler.Data); + } + else + { + _logger.LogWarning($"Route {downstreamRoute.UpstreamPathTemplate} specifies use QoS but no QosHandler found in DI container. Will use not use a QosHandler, please check your setup!"); + handlers.Add(() => new NoQosDelegatingHandler()); + } + } + + return new OkResponse>>(handlers); + } + + private List SortByConfigOrder(DownstreamRoute request, List routeSpecificHandlers) + { + return routeSpecificHandlers + .Where(x => request.DelegatingHandlers.Contains(x.GetType().Name)) + .OrderBy(d => + { + var type = d.GetType().Name; + var pos = request.DelegatingHandlers.IndexOf(type); + return pos; + }).ToList(); + } + + private bool GlobalIsInHandlersConfig(DownstreamRoute request, GlobalDelegatingHandler handler) + { + return request.DelegatingHandlers.Contains(handler.DelegatingHandler.GetType().Name); + } + } +} diff --git a/src/Ocelot/Requester/HttpClientBuilder.cs b/src/Ocelot/Requester/HttpClientBuilder.cs index 0c45f8364..b8499fdd7 100644 --- a/src/Ocelot/Requester/HttpClientBuilder.cs +++ b/src/Ocelot/Requester/HttpClientBuilder.cs @@ -12,7 +12,7 @@ public class HttpClientBuilder : IHttpClientBuilder private readonly IDelegatingHandlerHandlerFactory _factory; private readonly IHttpClientCache _cacheHandlers; private readonly IOcelotLogger _logger; - private DownstreamReRoute _cacheKey; + private DownstreamRoute _cacheKey; private HttpClient _httpClient; private IHttpClient _client; private readonly TimeSpan _defaultTimeout; @@ -31,9 +31,9 @@ public HttpClientBuilder( _defaultTimeout = TimeSpan.FromSeconds(90); } - public IHttpClient Create(DownstreamReRoute downstreamReRoute) + public IHttpClient Create(DownstreamRoute downstreamRoute) { - _cacheKey = downstreamReRoute; + _cacheKey = downstreamRoute; var httpClient = _cacheHandlers.Get(_cacheKey); @@ -43,21 +43,21 @@ public IHttpClient Create(DownstreamReRoute downstreamReRoute) return httpClient; } - var handler = CreateHandler(downstreamReRoute); + var handler = CreateHandler(downstreamRoute); - if (downstreamReRoute.DangerousAcceptAnyServerCertificateValidator) + if (downstreamRoute.DangerousAcceptAnyServerCertificateValidator) { handler.ServerCertificateCustomValidationCallback = (request, certificate, chain, errors) => true; _logger - .LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {downstreamReRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {downstreamReRoute.DownstreamPathTemplate}"); + .LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamRoute, UpstreamPathTemplate: {downstreamRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {downstreamRoute.DownstreamPathTemplate}"); } - var timeout = downstreamReRoute.QosOptions.TimeoutValue == 0 + var timeout = downstreamRoute.QosOptions.TimeoutValue == 0 ? _defaultTimeout - : TimeSpan.FromMilliseconds(downstreamReRoute.QosOptions.TimeoutValue); + : TimeSpan.FromMilliseconds(downstreamRoute.QosOptions.TimeoutValue); - _httpClient = new HttpClient(CreateHttpMessageHandler(handler, downstreamReRoute)) + _httpClient = new HttpClient(CreateHttpMessageHandler(handler, downstreamRoute)) { Timeout = timeout }; @@ -67,34 +67,34 @@ public IHttpClient Create(DownstreamReRoute downstreamReRoute) return _client; } - private HttpClientHandler CreateHandler(DownstreamReRoute downstreamReRoute) + private HttpClientHandler CreateHandler(DownstreamRoute downstreamRoute) { // Dont' create the CookieContainer if UseCookies is not set or the HttpClient will complain // under .Net Full Framework - var useCookies = downstreamReRoute.HttpHandlerOptions.UseCookieContainer; + var useCookies = downstreamRoute.HttpHandlerOptions.UseCookieContainer; - return useCookies ? UseCookiesHandler(downstreamReRoute) : UseNonCookiesHandler(downstreamReRoute); + return useCookies ? UseCookiesHandler(downstreamRoute) : UseNonCookiesHandler(downstreamRoute); } - private HttpClientHandler UseNonCookiesHandler(DownstreamReRoute downstreamReRoute) + private HttpClientHandler UseNonCookiesHandler(DownstreamRoute downstreamRoute) { return new HttpClientHandler { - AllowAutoRedirect = downstreamReRoute.HttpHandlerOptions.AllowAutoRedirect, - UseCookies = downstreamReRoute.HttpHandlerOptions.UseCookieContainer, - UseProxy = downstreamReRoute.HttpHandlerOptions.UseProxy, - MaxConnectionsPerServer = downstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer, + AllowAutoRedirect = downstreamRoute.HttpHandlerOptions.AllowAutoRedirect, + UseCookies = downstreamRoute.HttpHandlerOptions.UseCookieContainer, + UseProxy = downstreamRoute.HttpHandlerOptions.UseProxy, + MaxConnectionsPerServer = downstreamRoute.HttpHandlerOptions.MaxConnectionsPerServer, }; } - private HttpClientHandler UseCookiesHandler(DownstreamReRoute downstreamReRoute) + private HttpClientHandler UseCookiesHandler(DownstreamRoute downstreamRoute) { return new HttpClientHandler { - AllowAutoRedirect = downstreamReRoute.HttpHandlerOptions.AllowAutoRedirect, - UseCookies = downstreamReRoute.HttpHandlerOptions.UseCookieContainer, - UseProxy = downstreamReRoute.HttpHandlerOptions.UseProxy, - MaxConnectionsPerServer = downstreamReRoute.HttpHandlerOptions.MaxConnectionsPerServer, + AllowAutoRedirect = downstreamRoute.HttpHandlerOptions.AllowAutoRedirect, + UseCookies = downstreamRoute.HttpHandlerOptions.UseCookieContainer, + UseProxy = downstreamRoute.HttpHandlerOptions.UseProxy, + MaxConnectionsPerServer = downstreamRoute.HttpHandlerOptions.MaxConnectionsPerServer, CookieContainer = new CookieContainer(), }; } @@ -104,7 +104,7 @@ public void Save() _cacheHandlers.Set(_cacheKey, _client, TimeSpan.FromHours(24)); } - private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamReRoute request) + private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamRoute request) { //todo handle error var handlers = _factory.Get(request).Data; diff --git a/src/Ocelot/Requester/HttpClientHttpRequester.cs b/src/Ocelot/Requester/HttpClientHttpRequester.cs index f3fb87a2b..c0d26e40e 100644 --- a/src/Ocelot/Requester/HttpClientHttpRequester.cs +++ b/src/Ocelot/Requester/HttpClientHttpRequester.cs @@ -32,11 +32,11 @@ public async Task> GetResponse(HttpContext httpCon { var builder = new HttpClientBuilder(_factory, _cacheHandlers, _logger); - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); var downstreamRequest = httpContext.Items.DownstreamRequest(); - var httpClient = builder.Create(downstreamReRoute); + var httpClient = builder.Create(downstreamRoute); try { diff --git a/src/Ocelot/Requester/IDelegatingHandlerHandlerFactory.cs b/src/Ocelot/Requester/IDelegatingHandlerHandlerFactory.cs index cf0b6d07a..1df9f11ba 100644 --- a/src/Ocelot/Requester/IDelegatingHandlerHandlerFactory.cs +++ b/src/Ocelot/Requester/IDelegatingHandlerHandlerFactory.cs @@ -1,13 +1,13 @@ -namespace Ocelot.Requester -{ - using Ocelot.Configuration; - using Ocelot.Responses; - using System; - using System.Collections.Generic; - using System.Net.Http; - - public interface IDelegatingHandlerHandlerFactory - { - Response>> Get(DownstreamReRoute downstreamReRoute); - } -} +namespace Ocelot.Requester +{ + using Ocelot.Configuration; + using Ocelot.Responses; + using System; + using System.Collections.Generic; + using System.Net.Http; + + public interface IDelegatingHandlerHandlerFactory + { + Response>> Get(DownstreamRoute downstreamRoute); + } +} diff --git a/src/Ocelot/Requester/IHttpClientBuilder.cs b/src/Ocelot/Requester/IHttpClientBuilder.cs index c9bc6b983..f554c8bb1 100644 --- a/src/Ocelot/Requester/IHttpClientBuilder.cs +++ b/src/Ocelot/Requester/IHttpClientBuilder.cs @@ -4,7 +4,7 @@ public interface IHttpClientBuilder { - IHttpClient Create(DownstreamReRoute downstreamReRoute); + IHttpClient Create(DownstreamRoute downstreamRoute); void Save(); } diff --git a/src/Ocelot/Requester/IHttpClientCache.cs b/src/Ocelot/Requester/IHttpClientCache.cs index ac4cd2fd6..c1b074976 100644 --- a/src/Ocelot/Requester/IHttpClientCache.cs +++ b/src/Ocelot/Requester/IHttpClientCache.cs @@ -5,8 +5,8 @@ public interface IHttpClientCache { - IHttpClient Get(DownstreamReRoute key); + IHttpClient Get(DownstreamRoute key); - void Set(DownstreamReRoute key, IHttpClient handler, TimeSpan expirationTime); + void Set(DownstreamRoute key, IHttpClient handler, TimeSpan expirationTime); } } diff --git a/src/Ocelot/Requester/MemoryHttpClientCache.cs b/src/Ocelot/Requester/MemoryHttpClientCache.cs index 433d4ac68..e17ba7d5c 100644 --- a/src/Ocelot/Requester/MemoryHttpClientCache.cs +++ b/src/Ocelot/Requester/MemoryHttpClientCache.cs @@ -1,27 +1,27 @@ -namespace Ocelot.Requester -{ - using Configuration; - using System; - using System.Collections.Concurrent; - - public class MemoryHttpClientCache : IHttpClientCache - { - private readonly ConcurrentDictionary _httpClientsCache; - - public MemoryHttpClientCache() - { - _httpClientsCache = new ConcurrentDictionary(); - } - - public void Set(DownstreamReRoute key, IHttpClient client, TimeSpan expirationTime) - { - _httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client); - } - - public IHttpClient Get(DownstreamReRoute key) - { - //todo handle error? - return _httpClientsCache.TryGetValue(key, out var client) ? client : null; - } - } -} +namespace Ocelot.Requester +{ + using Configuration; + using System; + using System.Collections.Concurrent; + + public class MemoryHttpClientCache : IHttpClientCache + { + private readonly ConcurrentDictionary _httpClientsCache; + + public MemoryHttpClientCache() + { + _httpClientsCache = new ConcurrentDictionary(); + } + + public void Set(DownstreamRoute key, IHttpClient client, TimeSpan expirationTime) + { + _httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client); + } + + public IHttpClient Get(DownstreamRoute key) + { + //todo handle error? + return _httpClientsCache.TryGetValue(key, out var client) ? client : null; + } + } +} diff --git a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs index aa49cadb3..531b13dca 100644 --- a/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs +++ b/src/Ocelot/Requester/Middleware/HttpRequesterMiddleware.cs @@ -25,7 +25,7 @@ public HttpRequesterMiddleware(RequestDelegate next, public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); var response = await _requester.GetResponse(httpContext); diff --git a/src/Ocelot/Requester/QoS/IQosFactory.cs b/src/Ocelot/Requester/QoS/IQosFactory.cs index 5a3477cbb..691613bec 100644 --- a/src/Ocelot/Requester/QoS/IQosFactory.cs +++ b/src/Ocelot/Requester/QoS/IQosFactory.cs @@ -1,11 +1,11 @@ -namespace Ocelot.Requester.QoS -{ - using Configuration; - using Responses; - using System.Net.Http; - - public interface IQoSFactory - { - Response Get(DownstreamReRoute request); - } -} +namespace Ocelot.Requester.QoS +{ + using Configuration; + using Responses; + using System.Net.Http; + + public interface IQoSFactory + { + Response Get(DownstreamRoute request); + } +} diff --git a/src/Ocelot/Requester/QoS/QosFactory.cs b/src/Ocelot/Requester/QoS/QosFactory.cs index 711bc2c79..fb9095a01 100644 --- a/src/Ocelot/Requester/QoS/QosFactory.cs +++ b/src/Ocelot/Requester/QoS/QosFactory.cs @@ -1,33 +1,33 @@ -namespace Ocelot.Requester.QoS -{ - using Configuration; - using Logging; - using Microsoft.Extensions.DependencyInjection; - using Responses; - using System; - using System.Net.Http; - - public class QoSFactory : IQoSFactory - { - private readonly IServiceProvider _serviceProvider; - private readonly IOcelotLoggerFactory _ocelotLoggerFactory; - - public QoSFactory(IServiceProvider serviceProvider, IOcelotLoggerFactory ocelotLoggerFactory) - { - _serviceProvider = serviceProvider; - _ocelotLoggerFactory = ocelotLoggerFactory; - } - - public Response Get(DownstreamReRoute request) - { - var handler = _serviceProvider.GetService(); - - if (handler != null) - { - return new OkResponse(handler(request, _ocelotLoggerFactory)); - } - - return new ErrorResponse(new UnableToFindQoSProviderError($"could not find qosProvider for {request.DownstreamScheme}{request.DownstreamAddresses}{request.DownstreamPathTemplate}")); - } - } -} +namespace Ocelot.Requester.QoS +{ + using Configuration; + using Logging; + using Microsoft.Extensions.DependencyInjection; + using Responses; + using System; + using System.Net.Http; + + public class QoSFactory : IQoSFactory + { + private readonly IServiceProvider _serviceProvider; + private readonly IOcelotLoggerFactory _ocelotLoggerFactory; + + public QoSFactory(IServiceProvider serviceProvider, IOcelotLoggerFactory ocelotLoggerFactory) + { + _serviceProvider = serviceProvider; + _ocelotLoggerFactory = ocelotLoggerFactory; + } + + public Response Get(DownstreamRoute request) + { + var handler = _serviceProvider.GetService(); + + if (handler != null) + { + return new OkResponse(handler(request, _ocelotLoggerFactory)); + } + + return new ErrorResponse(new UnableToFindQoSProviderError($"could not find qosProvider for {request.DownstreamScheme}{request.DownstreamAddresses}{request.DownstreamPathTemplate}")); + } + } +} diff --git a/src/Ocelot/Requester/QosDelegatingHandlerDelegate.cs b/src/Ocelot/Requester/QosDelegatingHandlerDelegate.cs index 7b76c4c9a..ee566a6b2 100644 --- a/src/Ocelot/Requester/QosDelegatingHandlerDelegate.cs +++ b/src/Ocelot/Requester/QosDelegatingHandlerDelegate.cs @@ -1,8 +1,8 @@ -namespace Ocelot.Requester -{ - using Configuration; - using Logging; - using System.Net.Http; - - public delegate DelegatingHandler QosDelegatingHandlerDelegate(DownstreamReRoute reRoute, IOcelotLoggerFactory logger); -} +namespace Ocelot.Requester +{ + using Configuration; + using Logging; + using System.Net.Http; + + public delegate DelegatingHandler QosDelegatingHandlerDelegate(DownstreamRoute route, IOcelotLoggerFactory logger); +} diff --git a/src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs b/src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs index 122ecd51d..860bfc644 100644 --- a/src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs +++ b/src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs @@ -8,10 +8,10 @@ public class IPSecurityPolicy : ISecurityPolicy { - public async Task Security(DownstreamReRoute downstreamReRoute, HttpContext httpContext) + public async Task Security(DownstreamRoute downstreamRoute, HttpContext httpContext) { var clientIp = httpContext.Connection.RemoteIpAddress; - var securityOptions = downstreamReRoute.SecurityOptions; + var securityOptions = downstreamRoute.SecurityOptions; if (securityOptions == null) { return new OkResponse(); diff --git a/src/Ocelot/Security/ISecurityPolicy.cs b/src/Ocelot/Security/ISecurityPolicy.cs index 692a9eabd..4a8e87714 100644 --- a/src/Ocelot/Security/ISecurityPolicy.cs +++ b/src/Ocelot/Security/ISecurityPolicy.cs @@ -7,6 +7,6 @@ public interface ISecurityPolicy { - Task Security(DownstreamReRoute downstreamReRoute, HttpContext httpContext); + Task Security(DownstreamRoute downstreamRoute, HttpContext httpContext); } } diff --git a/src/Ocelot/Security/Middleware/SecurityMiddleware.cs b/src/Ocelot/Security/Middleware/SecurityMiddleware.cs index 8c32aa619..94a8ed82f 100644 --- a/src/Ocelot/Security/Middleware/SecurityMiddleware.cs +++ b/src/Ocelot/Security/Middleware/SecurityMiddleware.cs @@ -24,13 +24,13 @@ IEnumerable securityPolicies public async Task Invoke(HttpContext httpContext) { - var downstreamReRoute = httpContext.Items.DownstreamReRoute(); + var downstreamRoute = httpContext.Items.DownstreamRoute(); if (_securityPolicies != null) { foreach (var policy in _securityPolicies) { - var result = await policy.Security(downstreamReRoute, httpContext); + var result = await policy.Security(downstreamRoute, httpContext); if (!result.IsError) { continue; diff --git a/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs index a3c3b18be..a67f43a2b 100644 --- a/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs +++ b/src/Ocelot/ServiceDiscovery/IServiceDiscoveryProviderFactory.cs @@ -1,11 +1,11 @@ -namespace Ocelot.ServiceDiscovery -{ - using Ocelot.Configuration; - using Ocelot.Responses; - using Ocelot.ServiceDiscovery.Providers; - - public interface IServiceDiscoveryProviderFactory - { - Response Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute); - } -} +namespace Ocelot.ServiceDiscovery +{ + using Ocelot.Configuration; + using Ocelot.Responses; + using Ocelot.ServiceDiscovery.Providers; + + public interface IServiceDiscoveryProviderFactory + { + Response Get(ServiceProviderConfiguration serviceConfig, DownstreamRoute route); + } +} diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryFinderDelegate.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryFinderDelegate.cs index a3cf84608..7197d3fc1 100644 --- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryFinderDelegate.cs +++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryFinderDelegate.cs @@ -1,8 +1,8 @@ -namespace Ocelot.ServiceDiscovery -{ - using Ocelot.Configuration; - using Providers; - using System; - - public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, DownstreamReRoute reRoute); -} +namespace Ocelot.ServiceDiscovery +{ + using Ocelot.Configuration; + using Providers; + using System; + + public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, DownstreamRoute route); +} diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs index 7a5808722..762c4f22a 100644 --- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs +++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs @@ -1,66 +1,66 @@ -namespace Ocelot.ServiceDiscovery -{ - using Microsoft.Extensions.DependencyInjection; - using Ocelot.Configuration; - using Ocelot.Logging; - using Ocelot.Responses; - using Ocelot.ServiceDiscovery.Configuration; - using Ocelot.ServiceDiscovery.Providers; - using Ocelot.Values; - using System; - using System.Collections.Generic; - - public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory - { - private readonly IOcelotLoggerFactory _factory; - private readonly ServiceDiscoveryFinderDelegate _delegates; - private readonly IServiceProvider _provider; - - public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory, IServiceProvider provider) - { - _factory = factory; - _provider = provider; - _delegates = provider.GetService(); - } - - public Response Get(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute) - { - if (reRoute.UseServiceDiscovery) - { - return GetServiceDiscoveryProvider(serviceConfig, reRoute); - } - - var services = new List(); - - foreach (var downstreamAddress in reRoute.DownstreamAddresses) - { - var service = new Service(reRoute.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port, reRoute.DownstreamScheme), string.Empty, string.Empty, new string[0]); - - services.Add(service); - } - - return new OkResponse(new ConfigurationServiceProvider(services)); - } - - private Response GetServiceDiscoveryProvider(ServiceProviderConfiguration config, DownstreamReRoute reRoute) - { - if (config.Type?.ToLower() == "servicefabric") - { - var sfConfig = new ServiceFabricConfiguration(config.Host, config.Port, reRoute.ServiceName); - return new OkResponse(new ServiceFabricServiceDiscoveryProvider(sfConfig)); - } - - if (_delegates != null) - { - var provider = _delegates?.Invoke(_provider, config, reRoute); - - if (provider.GetType().Name.ToLower() == config.Type.ToLower()) - { - return new OkResponse(provider); - } - } - - return new ErrorResponse(new UnableToFindServiceDiscoveryProviderError($"Unable to find service discovery provider for type: {config.Type}")); - } - } -} +namespace Ocelot.ServiceDiscovery +{ + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Configuration; + using Ocelot.Logging; + using Ocelot.Responses; + using Ocelot.ServiceDiscovery.Configuration; + using Ocelot.ServiceDiscovery.Providers; + using Ocelot.Values; + using System; + using System.Collections.Generic; + + public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory + { + private readonly IOcelotLoggerFactory _factory; + private readonly ServiceDiscoveryFinderDelegate _delegates; + private readonly IServiceProvider _provider; + + public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory, IServiceProvider provider) + { + _factory = factory; + _provider = provider; + _delegates = provider.GetService(); + } + + public Response Get(ServiceProviderConfiguration serviceConfig, DownstreamRoute route) + { + if (route.UseServiceDiscovery) + { + return GetServiceDiscoveryProvider(serviceConfig, route); + } + + var services = new List(); + + foreach (var downstreamAddress in route.DownstreamAddresses) + { + var service = new Service(route.ServiceName, new ServiceHostAndPort(downstreamAddress.Host, downstreamAddress.Port, route.DownstreamScheme), string.Empty, string.Empty, new string[0]); + + services.Add(service); + } + + return new OkResponse(new ConfigurationServiceProvider(services)); + } + + private Response GetServiceDiscoveryProvider(ServiceProviderConfiguration config, DownstreamRoute route) + { + if (config.Type?.ToLower() == "servicefabric") + { + var sfConfig = new ServiceFabricConfiguration(config.Host, config.Port, route.ServiceName); + return new OkResponse(new ServiceFabricServiceDiscoveryProvider(sfConfig)); + } + + if (_delegates != null) + { + var provider = _delegates?.Invoke(_provider, config, route); + + if (provider.GetType().Name.ToLower() == config.Type.ToLower()) + { + return new OkResponse(provider); + } + } + + return new ErrorResponse(new UnableToFindServiceDiscoveryProviderError($"Unable to find service discovery provider for type: {config.Type}")); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/AggregateTests.cs b/test/Ocelot.AcceptanceTests/AggregateTests.cs index 7a0a29214..9065a3741 100644 --- a/test/Ocelot.AcceptanceTests/AggregateTests.cs +++ b/test/Ocelot.AcceptanceTests/AggregateTests.cs @@ -33,9 +33,9 @@ public void should_fix_issue_597() var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/values?MailId={userid}", UpstreamPathTemplate = "/key1data/{userid}", @@ -51,7 +51,7 @@ public void should_fix_issue_597() }, Key = "key1", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/values?MailId={userid}", UpstreamPathTemplate = "/key2data/{userid}", @@ -67,7 +67,7 @@ public void should_fix_issue_597() }, Key = "key2", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/values?MailId={userid}", UpstreamPathTemplate = "/key3data/{userid}", @@ -83,7 +83,7 @@ public void should_fix_issue_597() }, Key = "key3", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/values?MailId={userid}", UpstreamPathTemplate = "/key4data/{userid}", @@ -100,11 +100,11 @@ public void should_fix_issue_597() Key = "key4", }, }, - Aggregates = new List + Aggregates = new List { - new FileAggregateReRoute + new FileAggregateRoute { - ReRouteKeys = new List{ + RouteKeys = new List{ "key1", "key2", "key3", @@ -112,9 +112,9 @@ public void should_fix_issue_597() }, UpstreamPathTemplate = "/EmpDetail/IN/{userid}", }, - new FileAggregateReRoute + new FileAggregateRoute { - ReRouteKeys = new List{ + RouteKeys = new List{ "key1", "key2", }, @@ -146,9 +146,9 @@ public void should_return_response_200_with_advanced_aggregate_configs() var port3 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -164,7 +164,7 @@ public void should_return_response_200_with_advanced_aggregate_configs() UpstreamHttpMethod = new List { "Get" }, Key = "Comments", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/users/{userId}", DownstreamScheme = "http", @@ -180,7 +180,7 @@ public void should_return_response_200_with_advanced_aggregate_configs() UpstreamHttpMethod = new List { "Get" }, Key = "UserDetails", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/posts/{postId}", DownstreamScheme = "http", @@ -197,22 +197,22 @@ public void should_return_response_200_with_advanced_aggregate_configs() Key = "PostDetails", }, }, - Aggregates = new List + Aggregates = new List { - new FileAggregateReRoute + new FileAggregateRoute { UpstreamPathTemplate = "/", UpstreamHost = "localhost", - ReRouteKeys = new List + RouteKeys = new List { "Comments", "UserDetails", "PostDetails", }, - ReRouteKeysConfig = new List() + RouteKeysConfig = new List() { - new AggregateReRouteConfig(){ReRouteKey = "UserDetails",JsonPath = "$[*].writerId",Parameter = "userId"}, - new AggregateReRouteConfig(){ReRouteKey = "PostDetails",JsonPath = "$[*].postId",Parameter = "postId"}, + new AggregateRouteConfig(){RouteKey = "UserDetails",JsonPath = "$[*].writerId",Parameter = "userId"}, + new AggregateRouteConfig(){RouteKey = "PostDetails",JsonPath = "$[*].postId",Parameter = "postId"}, }, }, }, @@ -242,9 +242,9 @@ public void should_return_response_200_with_simple_url_user_defined_aggregate() var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -260,7 +260,7 @@ public void should_return_response_200_with_simple_url_user_defined_aggregate() UpstreamHttpMethod = new List { "Get" }, Key = "Laura", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -277,13 +277,13 @@ public void should_return_response_200_with_simple_url_user_defined_aggregate() Key = "Tom", }, }, - Aggregates = new List + Aggregates = new List { - new FileAggregateReRoute + new FileAggregateRoute { UpstreamPathTemplate = "/", UpstreamHost = "localhost", - ReRouteKeys = new List + RouteKeys = new List { "Laura", "Tom", @@ -313,9 +313,9 @@ public void should_return_response_200_with_simple_url() var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -331,7 +331,7 @@ public void should_return_response_200_with_simple_url() UpstreamHttpMethod = new List { "Get" }, Key = "Laura", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -348,13 +348,13 @@ public void should_return_response_200_with_simple_url() Key = "Tom", }, }, - Aggregates = new List + Aggregates = new List { - new FileAggregateReRoute + new FileAggregateRoute { UpstreamPathTemplate = "/", UpstreamHost = "localhost", - ReRouteKeys = new List + RouteKeys = new List { "Laura", "Tom", @@ -383,9 +383,9 @@ public void should_return_response_200_with_simple_url_one_service_404() var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -401,7 +401,7 @@ public void should_return_response_200_with_simple_url_one_service_404() UpstreamHttpMethod = new List { "Get" }, Key = "Laura", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -418,13 +418,13 @@ public void should_return_response_200_with_simple_url_one_service_404() Key = "Tom", }, }, - Aggregates = new List + Aggregates = new List { - new FileAggregateReRoute + new FileAggregateRoute { UpstreamPathTemplate = "/", UpstreamHost = "localhost", - ReRouteKeys = new List + RouteKeys = new List { "Laura", "Tom", @@ -453,9 +453,9 @@ public void should_return_response_200_with_simple_url_both_service_404() var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -471,7 +471,7 @@ public void should_return_response_200_with_simple_url_both_service_404() UpstreamHttpMethod = new List { "Get" }, Key = "Laura", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -488,13 +488,13 @@ public void should_return_response_200_with_simple_url_both_service_404() Key = "Tom", }, }, - Aggregates = new List + Aggregates = new List { - new FileAggregateReRoute + new FileAggregateRoute { UpstreamPathTemplate = "/", UpstreamHost = "localhost", - ReRouteKeys = new List + RouteKeys = new List { "Laura", "Tom", @@ -523,9 +523,9 @@ public void should_be_thread_safe() var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -541,7 +541,7 @@ public void should_be_thread_safe() UpstreamHttpMethod = new List { "Get" }, Key = "Laura", }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -558,13 +558,13 @@ public void should_be_thread_safe() Key = "Tom", }, }, - Aggregates = new List + Aggregates = new List { - new FileAggregateReRoute + new FileAggregateRoute { UpstreamPathTemplate = "/", UpstreamHost = "localhost", - ReRouteKeys = new List + RouteKeys = new List { "Laura", "Tom", diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index 5f99c2097..f019c12f2 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -51,9 +51,9 @@ public void should_return_401_using_identity_server_access_token() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = _downstreamServicePath, DownstreamHostAndPorts = new List @@ -92,9 +92,9 @@ public void should_return_response_200_using_identity_server() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = _downstreamServicePath, DownstreamHostAndPorts = new List @@ -135,9 +135,9 @@ public void should_return_response_401_using_identity_server_with_token_requeste var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = _downstreamServicePath, DownstreamHostAndPorts = new List @@ -177,9 +177,9 @@ public void should_return_201_using_identity_server_access_token() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = _downstreamServicePath, DownstreamHostAndPorts = new List @@ -220,9 +220,9 @@ public void should_return_201_using_identity_server_reference_token() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = _downstreamServicePath, DownstreamHostAndPorts = new List diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs index fc9df72be..4f8a28dfc 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs @@ -47,9 +47,9 @@ public void should_return_response_200_authorising_route() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -107,9 +107,9 @@ public void should_return_response_403_authorising_route() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -165,9 +165,9 @@ public void should_return_response_200_using_identity_server_with_allowed_scope( var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -208,9 +208,9 @@ public void should_return_response_403_using_identity_server_with_scope_not_allo var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -251,9 +251,9 @@ public void should_fix_issue_240() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List diff --git a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs index ec102a8dc..08f07f8a3 100644 --- a/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs +++ b/test/Ocelot.AcceptanceTests/ButterflyTracingTests.cs @@ -1,266 +1,266 @@ -namespace Ocelot.AcceptanceTests -{ - using Butterfly.Client.AspNetCore; - using Configuration.File; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Rafty.Infrastructure; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using TestStack.BDDfy; - using Xunit; - using Xunit.Abstractions; - - public class ButterflyTracingTests : IDisposable - { - private IWebHost _serviceOneBuilder; - private IWebHost _serviceTwoBuilder; - private IWebHost _fakeButterfly; - private readonly Steps _steps; - private string _downstreamPathOne; - private string _downstreamPathTwo; - private int _butterflyCalled; - private readonly ITestOutputHelper _output; - - public ButterflyTracingTests(ITestOutputHelper output) - { - _output = output; - _steps = new Steps(); - } - - [Fact] - public void should_forward_tracing_information_from_ocelot_and_downstream_services() - { - int port1 = RandomPortFinder.GetRandomPort(); - int port2 = RandomPortFinder.GetRandomPort(); - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/values", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port1, - } - }, - UpstreamPathTemplate = "/api001/values", - UpstreamHttpMethod = new List { "Get" }, - HttpHandlerOptions = new FileHttpHandlerOptions - { - UseTracing = true - } - }, - new FileReRoute - { - DownstreamPathTemplate = "/api/values", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port2, - } - }, - UpstreamPathTemplate = "/api002/values", - UpstreamHttpMethod = new List { "Get" }, - HttpHandlerOptions = new FileHttpHandlerOptions - { - UseTracing = true - } - } - } - }; - - var butterflyPort = RandomPortFinder.GetRandomPort(); - var butterflyUrl = $"http://localhost:{butterflyPort}"; - - this.Given(x => GivenFakeButterfly(butterflyUrl)) - .And(x => GivenServiceOneIsRunning($"http://localhost:{port1}", "/api/values", 200, "Hello from Laura", butterflyUrl)) - .And(x => GivenServiceTwoIsRunning($"http://localhost:{port2}", "/api/values", 200, "Hello from Tom", butterflyUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl)) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api002/values")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom")) - .BDDfy(); - - var commandOnAllStateMachines = Wait.WaitFor(10000).Until(() => _butterflyCalled >= 4); - - _output.WriteLine($"_butterflyCalled is {_butterflyCalled}"); - - commandOnAllStateMachines.ShouldBeTrue(); - } - - [Fact] - public void should_return_tracing_header() - { - int port = RandomPortFinder.GetRandomPort(); - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/values", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/api001/values", - UpstreamHttpMethod = new List { "Get" }, - HttpHandlerOptions = new FileHttpHandlerOptions - { - UseTracing = true - }, - DownstreamHeaderTransform = new Dictionary() - { - {"Trace-Id", "{TraceId}"}, - {"Tom", "Laura"} - } - } - } - }; - - var butterflyPort = RandomPortFinder.GetRandomPort(); - var butterflyUrl = $"http://localhost:{butterflyPort}"; - - this.Given(x => GivenFakeButterfly(butterflyUrl)) - .And(x => GivenServiceOneIsRunning($"http://localhost:{port}", "/api/values", 200, "Hello from Laura", butterflyUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl)) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => _steps.ThenTheTraceHeaderIsSet("Trace-Id")) - .And(x => _steps.ThenTheResponseHeaderIs("Tom", "Laura")) - .BDDfy(); - } - - private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl) - { - _serviceOneBuilder = new WebHostBuilder() - .UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .ConfigureServices(services => - { - services.AddButterfly(option => - { - option.CollectorUrl = butterflyUrl; - option.Service = "Service One"; - option.IgnoredRoutesRegexPatterns = new string[0]; - }); - }) - .Configure(app => - { - app.UsePathBase(basePath); - app.Run(async context => - { - _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; - - if (_downstreamPathOne != 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(); - - _serviceOneBuilder.Start(); - } - - private void GivenFakeButterfly(string baseUrl) - { - _fakeButterfly = new WebHostBuilder() - .UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.Run(async context => - { - _butterflyCalled++; - await context.Response.WriteAsync("OK..."); - }); - }) - .Build(); - - _fakeButterfly.Start(); - } - - private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl) - { - _serviceTwoBuilder = new WebHostBuilder() - .UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .ConfigureServices(services => - { - services.AddButterfly(option => - { - option.CollectorUrl = butterflyUrl; - option.Service = "Service Two"; - option.IgnoredRoutesRegexPatterns = new string[0]; - }); - }) - .Configure(app => - { - app.UsePathBase(basePath); - app.Run(async context => - { - _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; - - if (_downstreamPathTwo != 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(); - - _serviceTwoBuilder.Start(); - } - - public void Dispose() - { - _serviceOneBuilder?.Dispose(); - _serviceTwoBuilder?.Dispose(); - _fakeButterfly?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Butterfly.Client.AspNetCore; + using Configuration.File; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Rafty.Infrastructure; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using TestStack.BDDfy; + using Xunit; + using Xunit.Abstractions; + + public class ButterflyTracingTests : IDisposable + { + private IWebHost _serviceOneBuilder; + private IWebHost _serviceTwoBuilder; + private IWebHost _fakeButterfly; + private readonly Steps _steps; + private string _downstreamPathOne; + private string _downstreamPathTwo; + private int _butterflyCalled; + private readonly ITestOutputHelper _output; + + public ButterflyTracingTests(ITestOutputHelper output) + { + _output = output; + _steps = new Steps(); + } + + [Fact] + public void should_forward_tracing_information_from_ocelot_and_downstream_services() + { + int port1 = RandomPortFinder.GetRandomPort(); + int port2 = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/values", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port1, + } + }, + UpstreamPathTemplate = "/api001/values", + UpstreamHttpMethod = new List { "Get" }, + HttpHandlerOptions = new FileHttpHandlerOptions + { + UseTracing = true + } + }, + new FileRoute + { + DownstreamPathTemplate = "/api/values", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port2, + } + }, + UpstreamPathTemplate = "/api002/values", + UpstreamHttpMethod = new List { "Get" }, + HttpHandlerOptions = new FileHttpHandlerOptions + { + UseTracing = true + } + } + } + }; + + var butterflyPort = RandomPortFinder.GetRandomPort(); + var butterflyUrl = $"http://localhost:{butterflyPort}"; + + this.Given(x => GivenFakeButterfly(butterflyUrl)) + .And(x => GivenServiceOneIsRunning($"http://localhost:{port1}", "/api/values", 200, "Hello from Laura", butterflyUrl)) + .And(x => GivenServiceTwoIsRunning($"http://localhost:{port2}", "/api/values", 200, "Hello from Tom", butterflyUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api002/values")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Tom")) + .BDDfy(); + + var commandOnAllStateMachines = Wait.WaitFor(10000).Until(() => _butterflyCalled >= 4); + + _output.WriteLine($"_butterflyCalled is {_butterflyCalled}"); + + commandOnAllStateMachines.ShouldBeTrue(); + } + + [Fact] + public void should_return_tracing_header() + { + int port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/values", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/api001/values", + UpstreamHttpMethod = new List { "Get" }, + HttpHandlerOptions = new FileHttpHandlerOptions + { + UseTracing = true + }, + DownstreamHeaderTransform = new Dictionary() + { + {"Trace-Id", "{TraceId}"}, + {"Tom", "Laura"} + } + } + } + }; + + var butterflyPort = RandomPortFinder.GetRandomPort(); + var butterflyUrl = $"http://localhost:{butterflyPort}"; + + this.Given(x => GivenFakeButterfly(butterflyUrl)) + .And(x => GivenServiceOneIsRunning($"http://localhost:{port}", "/api/values", 200, "Hello from Laura", butterflyUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingButterfly(butterflyUrl)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api001/values")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => _steps.ThenTheTraceHeaderIsSet("Trace-Id")) + .And(x => _steps.ThenTheResponseHeaderIs("Tom", "Laura")) + .BDDfy(); + } + + private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl) + { + _serviceOneBuilder = new WebHostBuilder() + .UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .ConfigureServices(services => + { + services.AddButterfly(option => + { + option.CollectorUrl = butterflyUrl; + option.Service = "Service One"; + option.IgnoredRoutesRegexPatterns = new string[0]; + }); + }) + .Configure(app => + { + app.UsePathBase(basePath); + app.Run(async context => + { + _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; + + if (_downstreamPathOne != 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(); + + _serviceOneBuilder.Start(); + } + + private void GivenFakeButterfly(string baseUrl) + { + _fakeButterfly = new WebHostBuilder() + .UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.Run(async context => + { + _butterflyCalled++; + await context.Response.WriteAsync("OK..."); + }); + }) + .Build(); + + _fakeButterfly.Start(); + } + + private void GivenServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody, string butterflyUrl) + { + _serviceTwoBuilder = new WebHostBuilder() + .UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .ConfigureServices(services => + { + services.AddButterfly(option => + { + option.CollectorUrl = butterflyUrl; + option.Service = "Service Two"; + option.IgnoredRoutesRegexPatterns = new string[0]; + }); + }) + .Configure(app => + { + app.UsePathBase(basePath); + app.Run(async context => + { + _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; + + if (_downstreamPathTwo != 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(); + + _serviceTwoBuilder.Start(); + } + + public void Dispose() + { + _serviceOneBuilder?.Dispose(); + _serviceTwoBuilder?.Dispose(); + _fakeButterfly?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/CachingTests.cs b/test/Ocelot.AcceptanceTests/CachingTests.cs index d005d2ae4..2e63d3618 100644 --- a/test/Ocelot.AcceptanceTests/CachingTests.cs +++ b/test/Ocelot.AcceptanceTests/CachingTests.cs @@ -27,9 +27,9 @@ public void should_return_cached_response() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -72,9 +72,9 @@ public void should_return_cached_response_with_expires_header() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -118,9 +118,9 @@ public void should_return_cached_response_when_using_jsonserialized_cache() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -162,9 +162,9 @@ public void should_not_return_cached_response_as_ttl_expires() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List diff --git a/test/Ocelot.AcceptanceTests/CannotStartOcelotTests.cs b/test/Ocelot.AcceptanceTests/CannotStartOcelotTests.cs index 74359480f..d72347720 100644 --- a/test/Ocelot.AcceptanceTests/CannotStartOcelotTests.cs +++ b/test/Ocelot.AcceptanceTests/CannotStartOcelotTests.cs @@ -1,223 +1,223 @@ -namespace Ocelot.AcceptanceTests -{ - using Ocelot.Configuration.File; - using Shouldly; - using System; - using System.Collections.Generic; - using Xunit; - - public class CannotStartOcelotTests : IDisposable - { - private readonly Steps _steps; - - public CannotStartOcelotTests() - { - _steps = new Steps(); - } - - [Fact] - public void should_throw_exception_if_cannot_start_because_service_discovery_provider_specified_in_config_but_no_service_discovery_provider_registered_with_dynamic_re_routes() - { - var invalidConfig = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "localhost", - Type = "consul", - Port = 8500 - } - } - }; - - Exception exception = null; - _steps.GivenThereIsAConfiguration(invalidConfig); - try - { - _steps.GivenOcelotIsRunning(); - } - catch (Exception ex) - { - exception = ex; - } - - exception.ShouldNotBeNull(); - exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: 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()?)"); - } - - [Fact] - public void should_throw_exception_if_cannot_start_because_service_discovery_provider_specified_in_config_but_no_service_discovery_provider_registered() - { - var invalidConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "test" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "localhost", - Type = "consul", - Port = 8500 - } - } - }; - - Exception exception = null; - _steps.GivenThereIsAConfiguration(invalidConfig); - try - { - _steps.GivenOcelotIsRunning(); - } - catch (Exception ex) - { - exception = ex; - } - - exception.ShouldNotBeNull(); - exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: 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()?,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()?)"); - } - - [Fact] - public void should_throw_exception_if_cannot_start_because_no_qos_delegate_registered_globally() - { - var invalidConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura", - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - QoSOptions = new FileQoSOptions - { - TimeoutValue = 1, - ExceptionsAllowedBeforeBreaking = 1 - } - } - }; - - Exception exception = null; - _steps.GivenThereIsAConfiguration(invalidConfig); - try - { - _steps.GivenOcelotIsRunning(); - } - catch (Exception ex) - { - exception = ex; - } - - exception.ShouldNotBeNull(); - exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?)"); - } - - [Fact] - public void should_throw_exception_if_cannot_start_because_no_qos_delegate_registered_for_re_route() - { - var invalidConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura", - QoSOptions = new FileQoSOptions - { - TimeoutValue = 1, - ExceptionsAllowedBeforeBreaking = 1 - } - } - } - }; - - Exception exception = null; - _steps.GivenThereIsAConfiguration(invalidConfig); - try - { - _steps.GivenOcelotIsRunning(); - } - catch (Exception ex) - { - exception = ex; - } - - exception.ShouldNotBeNull(); - exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?)"); - } - - [Fact] - public void should_throw_exception_if_cannot_start() - { - var invalidConfig = new FileConfiguration() - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "api", - DownstreamPathTemplate = "test" - } - } - }; - - Exception exception = null; - _steps.GivenThereIsAConfiguration(invalidConfig); - try - { - _steps.GivenOcelotIsRunning(); - } - catch (Exception ex) - { - exception = ex; - } - - exception.ShouldNotBeNull(); - exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Downstream Path Template test doesnt start with forward slash,Upstream Path Template api doesnt start with forward slash,When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!)"); - } - - public void Dispose() - { - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using Xunit; + + public class CannotStartOcelotTests : IDisposable + { + private readonly Steps _steps; + + public CannotStartOcelotTests() + { + _steps = new Steps(); + } + + [Fact] + public void should_throw_exception_if_cannot_start_because_service_discovery_provider_specified_in_config_but_no_service_discovery_provider_registered_with_dynamic_re_routes() + { + var invalidConfig = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "localhost", + Type = "consul", + Port = 8500 + } + } + }; + + Exception exception = null; + _steps.GivenThereIsAConfiguration(invalidConfig); + try + { + _steps.GivenOcelotIsRunning(); + } + catch (Exception ex) + { + exception = ex; + } + + exception.ShouldNotBeNull(); + exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route 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()?)"); + } + + [Fact] + public void should_throw_exception_if_cannot_start_because_service_discovery_provider_specified_in_config_but_no_service_discovery_provider_registered() + { + var invalidConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "test" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "localhost", + Type = "consul", + Port = 8500 + } + } + }; + + Exception exception = null; + _steps.GivenThereIsAConfiguration(invalidConfig); + try + { + _steps.GivenOcelotIsRunning(); + } + catch (Exception ex) + { + exception = ex; + } + + exception.ShouldNotBeNull(); + exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route 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()?,Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route 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()?)"); + } + + [Fact] + public void should_throw_exception_if_cannot_start_because_no_qos_delegate_registered_globally() + { + var invalidConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura", + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + QoSOptions = new FileQoSOptions + { + TimeoutValue = 1, + ExceptionsAllowedBeforeBreaking = 1 + } + } + }; + + Exception exception = null; + _steps.GivenThereIsAConfiguration(invalidConfig); + try + { + _steps.GivenOcelotIsRunning(); + } + catch (Exception ex) + { + exception = ex; + } + + exception.ShouldNotBeNull(); + exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?)"); + } + + [Fact] + public void should_throw_exception_if_cannot_start_because_no_qos_delegate_registered_for_re_route() + { + var invalidConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura", + QoSOptions = new FileQoSOptions + { + TimeoutValue = 1, + ExceptionsAllowedBeforeBreaking = 1 + } + } + } + }; + + Exception exception = null; + _steps.GivenThereIsAConfiguration(invalidConfig); + try + { + _steps.GivenOcelotIsRunning(); + } + catch (Exception ex) + { + exception = ex; + } + + exception.ShouldNotBeNull(); + exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?)"); + } + + [Fact] + public void should_throw_exception_if_cannot_start() + { + var invalidConfig = new FileConfiguration() + { + Routes = new List + { + new FileRoute + { + UpstreamPathTemplate = "api", + DownstreamPathTemplate = "test" + } + } + }; + + Exception exception = null; + _steps.GivenThereIsAConfiguration(invalidConfig); + try + { + _steps.GivenOcelotIsRunning(); + } + catch (Exception ex) + { + exception = ex; + } + + exception.ShouldNotBeNull(); + exception.Message.ShouldBe("One or more errors occurred. (Unable to start Ocelot, errors are: Downstream Path Template test doesnt start with forward slash,Upstream Path Template api doesnt start with forward slash,When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!)"); + } + + public void Dispose() + { + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs index c56b7d425..5f6e9c993 100644 --- a/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/CaseSensitiveRoutingTests.cs @@ -6,7 +6,7 @@ namespace Ocelot.AcceptanceTests using System.Collections.Generic; using System.Net; using TestStack.BDDfy; - using Xunit; + using Xunit; public class CaseSensitiveRoutingTests : IDisposable { @@ -21,14 +21,14 @@ public CaseSensitiveRoutingTests() [Fact] public void should_return_response_200_when_global_ignore_case_sensitivity_set() - { - int port = RandomPortFinder.GetRandomPort(); + { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/products/{productId}", DownstreamHostAndPorts = new List @@ -55,15 +55,15 @@ public void should_return_response_200_when_global_ignore_case_sensitivity_set() } [Fact] - public void should_return_response_200_when_reroute_ignore_case_sensitivity_set() - { - int port = RandomPortFinder.GetRandomPort(); + public void should_return_response_200_when_route_ignore_case_sensitivity_set() + { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/products/{productId}", DownstreamHostAndPorts = new List @@ -77,7 +77,7 @@ public void should_return_response_200_when_reroute_ignore_case_sensitivity_set( DownstreamScheme = "http", UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = false, + RouteIsCaseSensitive = false, } } }; @@ -91,15 +91,15 @@ public void should_return_response_200_when_reroute_ignore_case_sensitivity_set( } [Fact] - public void should_return_response_404_when_reroute_respect_case_sensitivity_set() - { - int port = RandomPortFinder.GetRandomPort(); + public void should_return_response_404_when_route_respect_case_sensitivity_set() + { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/products/{productId}", DownstreamHostAndPorts = new List @@ -113,7 +113,7 @@ public void should_return_response_404_when_reroute_respect_case_sensitivity_set DownstreamScheme = "http", UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true, + RouteIsCaseSensitive = true, } } }; @@ -127,15 +127,15 @@ public void should_return_response_404_when_reroute_respect_case_sensitivity_set } [Fact] - public void should_return_response_200_when_reroute_respect_case_sensitivity_set() - { - int port = RandomPortFinder.GetRandomPort(); + public void should_return_response_200_when_route_respect_case_sensitivity_set() + { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/products/{productId}", DownstreamHostAndPorts = new List @@ -149,7 +149,7 @@ public void should_return_response_200_when_reroute_respect_case_sensitivity_set DownstreamScheme = "http", UpstreamPathTemplate = "/PRODUCTS/{productId}", UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true, + RouteIsCaseSensitive = true, } } }; @@ -164,14 +164,14 @@ public void should_return_response_200_when_reroute_respect_case_sensitivity_set [Fact] public void should_return_response_404_when_global_respect_case_sensitivity_set() - { - int port = RandomPortFinder.GetRandomPort(); + { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/products/{productId}", DownstreamHostAndPorts = new List @@ -185,7 +185,7 @@ public void should_return_response_404_when_global_respect_case_sensitivity_set( DownstreamScheme = "http", UpstreamPathTemplate = "/products/{productId}", UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true, + RouteIsCaseSensitive = true, } } }; @@ -200,14 +200,14 @@ public void should_return_response_404_when_global_respect_case_sensitivity_set( [Fact] public void should_return_response_200_when_global_respect_case_sensitivity_set() - { - int port = RandomPortFinder.GetRandomPort(); + { + int port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/api/products/{productId}", DownstreamHostAndPorts = new List @@ -221,7 +221,7 @@ public void should_return_response_200_when_global_respect_case_sensitivity_set( DownstreamScheme = "http", UpstreamPathTemplate = "/PRODUCTS/{productId}", UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true, + RouteIsCaseSensitive = true, } } }; @@ -249,4 +249,4 @@ public void Dispose() _steps.Dispose(); } } -} +} diff --git a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs index 6d22205ce..647948ec3 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs @@ -1,205 +1,205 @@ -using Xunit; - -namespace Ocelot.AcceptanceTests -{ - using IdentityServer4.AccessTokenValidation; - using IdentityServer4.Models; - using IdentityServer4.Test; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.DependencyInjection; - using Ocelot.Configuration.File; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using TestStack.BDDfy; - - public class ClaimsToDownstreamPathTests : IDisposable - { - private IWebHost _servicebuilder; - private IWebHost _identityServerBuilder; - private readonly Steps _steps; - private Action _options; - private string _identityServerRootUrl; - private string _downstreamFinalPath; - - public ClaimsToDownstreamPathTests() - { - var identityServerPort = RandomPortFinder.GetRandomPort(); - _identityServerRootUrl = $"http://localhost:{identityServerPort}"; - _steps = new Steps(); - _options = o => - { - o.Authority = _identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - } - - [Fact] - public void should_return_200_and_change_downstream_path() - { - var user = new TestUser() - { - Username = "test", - Password = "test", - SubjectId = "registered|1231231", - }; - - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/users/{userId}", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - }, - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/users", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List - { - "openid", "offline_access", "api", - }, - }, - ChangeDownstreamPathTemplate = - { - {"userId", "Claims[sub] > value[1] > |"}, - }, - }, - }, - }; - - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) - .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) - .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/users")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231")) - .And(x => _downstreamFinalPath.ShouldBe("/users/1231231")) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string url, int statusCode) - { - _servicebuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - _downstreamFinalPath = context.Request.Path.Value; - - string userId = _downstreamFinalPath.Replace("/users/", string.Empty); - - var responseBody = $"UserId: {userId}"; - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - }) - .Build(); - - _servicebuilder.Start(); - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) - { - _identityServerBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = "My API", - Enabled = true, - DisplayName = "test", - Scopes = new List() - { - new Scope("api"), - new Scope("openid"), - new Scope("offline_access") - }, - ApiSecrets = new List() - { - new Secret - { - Value = "secret".Sha256() - } - }, - UserClaims = new List() - { - "CustomerId", "LocationId", "UserType", "UserId" - } - } - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = "client", - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "openid", "offline_access" }, - AccessTokenType = tokenType, - Enabled = true, - RequireClientSecret = false - } - }) - .AddTestUsers(new List - { - user - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - }) - .Build(); - - _identityServerBuilder.Start(); - - _steps.VerifyIdentiryServerStarted(url); - } - - public void Dispose() - { - _servicebuilder?.Dispose(); - _steps.Dispose(); - _identityServerBuilder?.Dispose(); - } - } -} +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + using IdentityServer4.AccessTokenValidation; + using IdentityServer4.Models; + using IdentityServer4.Test; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using TestStack.BDDfy; + + public class ClaimsToDownstreamPathTests : IDisposable + { + private IWebHost _servicebuilder; + private IWebHost _identityServerBuilder; + private readonly Steps _steps; + private Action _options; + private string _identityServerRootUrl; + private string _downstreamFinalPath; + + public ClaimsToDownstreamPathTests() + { + var identityServerPort = RandomPortFinder.GetRandomPort(); + _identityServerRootUrl = $"http://localhost:{identityServerPort}"; + _steps = new Steps(); + _options = o => + { + o.Authority = _identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + } + + [Fact] + public void should_return_200_and_change_downstream_path() + { + var user = new TestUser() + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + }; + + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/users/{userId}", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/users", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List + { + "openid", "offline_access", "api", + }, + }, + ChangeDownstreamPathTemplate = + { + {"userId", "Claims[sub] > value[1] > |"}, + }, + }, + }, + }; + + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt, user)) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200)) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/users")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231")) + .And(x => _downstreamFinalPath.ShouldBe("/users/1231231")) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode) + { + _servicebuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + _downstreamFinalPath = context.Request.Path.Value; + + string userId = _downstreamFinalPath.Replace("/users/", string.Empty); + + var responseBody = $"UserId: {userId}"; + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }) + .Build(); + + _servicebuilder.Start(); + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + Scopes = new List() + { + new Scope("api"), + new Scope("openid"), + new Scope("offline_access") + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256() + } + }, + UserClaims = new List() + { + "CustomerId", "LocationId", "UserType", "UserId" + } + } + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false + } + }) + .AddTestUsers(new List + { + user + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); + } + + public void Dispose() + { + _servicebuilder?.Dispose(); + _steps.Dispose(); + _identityServerBuilder?.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs index 63f79e14f..2712e2b79 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs @@ -63,9 +63,9 @@ public void should_return_response_200_and_foward_claim_as_header() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs index 7173a6f38..cfdbdbb49 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs @@ -62,9 +62,9 @@ public void should_return_response_200_and_foward_claim_as_query_string() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -128,9 +128,9 @@ public void should_return_response_200_and_foward_claim_as_query_string_and_pres var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List diff --git a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs index c9f556e57..a934d7aae 100644 --- a/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs +++ b/test/Ocelot.AcceptanceTests/ClientRateLimitTests.cs @@ -1,222 +1,222 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class ClientRateLimitTests : IDisposable - { - private readonly Steps _steps; - private int _counterOne; - private readonly ServiceHandler _serviceHandler; - - public ClientRateLimitTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_call_withratelimiting() - { - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/ClientRateLimit", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/api/ClientRateLimit", - UpstreamHttpMethod = new List { "Get" }, - RequestIdKey = _steps.RequestIdKey, - RateLimitOptions = new FileRateLimitRule() - { - EnableRateLimiting = true, - ClientWhitelist = new List(), - Limit = 3, - Period = "1s", - PeriodTimespan = 1000 - } - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - RateLimitOptions = new FileRateLimitOptions() - { - ClientIdHeader = "ClientId", - DisableRateLimitHeaders = false, - QuotaExceededMessage = "", - RateLimitCounterPrefix = "", - HttpStatusCode = 428 - }, - RequestIdKey = "oceclientrequest" - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 2)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) - .BDDfy(); - } - - [Fact] - public void should_wait_for_period_timespan_to_elapse_before_making_next_request() - { - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/ClientRateLimit", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/api/ClientRateLimit", - UpstreamHttpMethod = new List { "Get" }, - RequestIdKey = _steps.RequestIdKey, - - RateLimitOptions = new FileRateLimitRule() - { - EnableRateLimiting = true, - ClientWhitelist = new List(), - Limit = 3, - Period = "1s", - PeriodTimespan = 2 - } - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - RateLimitOptions = new FileRateLimitOptions() - { - ClientIdHeader = "ClientId", - DisableRateLimitHeaders = false, - QuotaExceededMessage = "", - RateLimitCounterPrefix = "", - HttpStatusCode = 428 - }, - RequestIdKey = "oceclientrequest" - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 2)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) - .And(x => _steps.GivenIWait(1000)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) - .And(x => _steps.GivenIWait(1000)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .BDDfy(); - } - - [Fact] - public void should_call_middleware_withWhitelistClient() - { - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/ClientRateLimit", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/api/ClientRateLimit", - UpstreamHttpMethod = new List { "Get" }, - RequestIdKey = _steps.RequestIdKey, - - RateLimitOptions = new FileRateLimitRule() - { - EnableRateLimiting = true, - ClientWhitelist = new List() { "ocelotclient1"}, - Limit = 3, - Period = "1s", - PeriodTimespan = 100 - } - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - RateLimitOptions = new FileRateLimitOptions() - { - ClientIdHeader = "ClientId", - DisableRateLimitHeaders = false, - QuotaExceededMessage = "", - RateLimitCounterPrefix = "" - }, - RequestIdKey = "oceclientrequest" - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 4)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context => - { - _counterOne++; - context.Response.StatusCode = 200; - context.Response.WriteAsync(_counterOne.ToString()); - return Task.CompletedTask; - }); - } - - public void Dispose() - { - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class ClientRateLimitTests : IDisposable + { + private readonly Steps _steps; + private int _counterOne; + private readonly ServiceHandler _serviceHandler; + + public ClientRateLimitTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_call_withratelimiting() + { + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/ClientRateLimit", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/api/ClientRateLimit", + UpstreamHttpMethod = new List { "Get" }, + RequestIdKey = _steps.RequestIdKey, + RateLimitOptions = new FileRateLimitRule() + { + EnableRateLimiting = true, + ClientWhitelist = new List(), + Limit = 3, + Period = "1s", + PeriodTimespan = 1000 + } + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + RateLimitOptions = new FileRateLimitOptions() + { + ClientIdHeader = "ClientId", + DisableRateLimitHeaders = false, + QuotaExceededMessage = "", + RateLimitCounterPrefix = "", + HttpStatusCode = 428 + }, + RequestIdKey = "oceclientrequest" + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 2)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) + .BDDfy(); + } + + [Fact] + public void should_wait_for_period_timespan_to_elapse_before_making_next_request() + { + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/ClientRateLimit", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/api/ClientRateLimit", + UpstreamHttpMethod = new List { "Get" }, + RequestIdKey = _steps.RequestIdKey, + + RateLimitOptions = new FileRateLimitRule() + { + EnableRateLimiting = true, + ClientWhitelist = new List(), + Limit = 3, + Period = "1s", + PeriodTimespan = 2 + } + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + RateLimitOptions = new FileRateLimitOptions() + { + ClientIdHeader = "ClientId", + DisableRateLimitHeaders = false, + QuotaExceededMessage = "", + RateLimitCounterPrefix = "", + HttpStatusCode = 428 + }, + RequestIdKey = "oceclientrequest" + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 2)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) + .And(x => _steps.GivenIWait(1000)) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) + .And(x => _steps.GivenIWait(1000)) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 1)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) + .BDDfy(); + } + + [Fact] + public void should_call_middleware_withWhitelistClient() + { + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/ClientRateLimit", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/api/ClientRateLimit", + UpstreamHttpMethod = new List { "Get" }, + RequestIdKey = _steps.RequestIdKey, + + RateLimitOptions = new FileRateLimitRule() + { + EnableRateLimiting = true, + ClientWhitelist = new List() { "ocelotclient1"}, + Limit = 3, + Period = "1s", + PeriodTimespan = 100 + } + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + RateLimitOptions = new FileRateLimitOptions() + { + ClientIdHeader = "ClientId", + DisableRateLimitHeaders = false, + QuotaExceededMessage = "", + RateLimitCounterPrefix = "" + }, + RequestIdKey = "oceclientrequest" + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/api/ClientRateLimit")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/api/ClientRateLimit", 4)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, context => + { + _counterOne++; + context.Response.StatusCode = 200; + context.Response.WriteAsync(_counterOne.ToString()); + return Task.CompletedTask; + }); + } + + public void Dispose() + { + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs index c1d650e5c..1e200f166 100644 --- a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs @@ -1,188 +1,188 @@ -namespace Ocelot.AcceptanceTests -{ - using Configuration.File; - using Consul; - using IdentityServer4.Extensions; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.Hosting; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Text; - using TestStack.BDDfy; - using Xunit; - - public class ConfigurationInConsulTests : IDisposable - { - private IHost _builder; - private readonly Steps _steps; - private IHost _fakeConsulBuilder; - private FileConfiguration _config; - private readonly List _consulServices; - - public ConfigurationInConsulTests() - { - _consulServices = new List(); - _steps = new Steps(); - } - - [Fact] - public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache() - { - int consulPort = RandomPortFinder.GetRandomPort(); - int servicePort = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = servicePort, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) - .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) - { - _fakeConsulBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") - { - var json = JsonConvert.SerializeObject(_config); - - var bytes = Encoding.UTF8.GetBytes(json); - - var base64 = Convert.ToBase64String(bytes); - - var kvp = new FakeConsulGetResponse(base64); - - await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp }); - } - else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") - { - try - { - var reader = new StreamReader(context.Request.Body); - - // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. - // var json = reader.ReadToEnd(); - var json = await reader.ReadToEndAsync(); - - _config = JsonConvert.DeserializeObject(json); - - var response = JsonConvert.SerializeObject(true); - - await context.Response.WriteAsync(response); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - else if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") - { - await context.Response.WriteJsonAsync(_consulServices); - } - }); - }); - }).Build(); - - _fakeConsulBuilder.Start(); - } - - public class FakeConsulGetResponse - { - public FakeConsulGetResponse(string value) - { - Value = value; - } - - public int CreateIndex => 100; - public int ModifyIndex => 200; - public int LockIndex => 200; - public string Key => "InternalConfiguration"; - public int Flags => 0; - public string Value { get; private set; } - public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; - } - - private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) - { - _builder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.UsePathBase(basePath); - app.Run(async context => - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - }); - }) - .Build(); - - _builder.Start(); - } - - public void Dispose() - { - _builder?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Configuration.File; + using Consul; + using IdentityServer4.Extensions; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Hosting; + using Newtonsoft.Json; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Text; + using TestStack.BDDfy; + using Xunit; + + public class ConfigurationInConsulTests : IDisposable + { + private IHost _builder; + private readonly Steps _steps; + private IHost _fakeConsulBuilder; + private FileConfiguration _config; + private readonly List _consulServices; + + public ConfigurationInConsulTests() + { + _consulServices = new List(); + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_with_simple_url_when_using_jsonserialized_cache() + { + int consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = servicePort, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) + { + _fakeConsulBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") + { + var json = JsonConvert.SerializeObject(_config); + + var bytes = Encoding.UTF8.GetBytes(json); + + var base64 = Convert.ToBase64String(bytes); + + var kvp = new FakeConsulGetResponse(base64); + + await context.Response.WriteJsonAsync(new FakeConsulGetResponse[] { kvp }); + } + else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") + { + try + { + var reader = new StreamReader(context.Request.Body); + + // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. + // var json = reader.ReadToEnd(); + var json = await reader.ReadToEndAsync(); + + _config = JsonConvert.DeserializeObject(json); + + var response = JsonConvert.SerializeObject(true); + + await context.Response.WriteAsync(response); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + else if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") + { + await context.Response.WriteJsonAsync(_consulServices); + } + }); + }); + }).Build(); + + _fakeConsulBuilder.Start(); + } + + public class FakeConsulGetResponse + { + public FakeConsulGetResponse(string value) + { + Value = value; + } + + public int CreateIndex => 100; + public int ModifyIndex => 200; + public int LockIndex => 200; + public string Key => "InternalConfiguration"; + public int Flags => 0; + public string Value { get; private set; } + public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; + } + + private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) + { + _builder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.UsePathBase(basePath); + app.Run(async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }); + }) + .Build(); + + _builder.Start(); + } + + public void Dispose() + { + _builder?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs index 6a4a8d2d4..f983ef6da 100644 --- a/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs @@ -1,487 +1,487 @@ -namespace Ocelot.AcceptanceTests -{ - using Cache; - using Configuration.File; - using Consul; - using Infrastructure; - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Http; - using Newtonsoft.Json; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Text; - using TestStack.BDDfy; - using Xunit; - - public class ConsulConfigurationInConsulTests : IDisposable - { - private IWebHost _builder; - private readonly Steps _steps; - private IWebHost _fakeConsulBuilder; - private FileConfiguration _config; - private readonly List _consulServices; - - public ConsulConfigurationInConsulTests() - { - _consulServices = new List(); - _steps = new Steps(); - } - - [Fact] - public void should_return_response_200_with_simple_url() - { - int consulPort = RandomPortFinder.GetRandomPort(); - int servicePort = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = servicePort, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - - this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) - .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_load_configuration_out_of_consul() - { - var consulPort = RandomPortFinder.GetRandomPort(); - int servicePort = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - - var consulConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/status", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = servicePort, - } - }, - UpstreamPathTemplate = "/cs/status", - UpstreamHttpMethod = new List {"Get"} - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) - .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) - .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "/status", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_load_configuration_out_of_consul_if_it_is_changed() - { - var consulPort = RandomPortFinder.GetRandomPort(); - int servicePort = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - - var consulConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/status", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = servicePort, - } - }, - UpstreamPathTemplate = "/cs/status", - UpstreamHttpMethod = new List {"Get"} - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - var secondConsulConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/status", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = servicePort, - } - }, - UpstreamPathTemplate = "/cs/status/awesome", - UpstreamHttpMethod = new List {"Get"} - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) - .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) - .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "/status", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) - .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .When(x => GivenTheConsulConfigurationIs(secondConsulConfig)) - .Then(x => ThenTheConfigIsUpdatedInOcelot()) - .BDDfy(); - } - - [Fact] - public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes_and_rate_limit() - { - int consulPort = RandomPortFinder.GetRandomPort(); - const string serviceName = "web"; - int downstreamServicePort = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = downstreamServicePort, - ID = "web_90_0_2_224_8080", - Tags = new[] { "version-v1" } - }, - }; - - var consulConfig = new FileConfiguration - { - DynamicReRoutes = new List - { - new FileDynamicReRoute - { - ServiceName = serviceName, - RateLimitRule = new FileRateLimitRule() - { - EnableRateLimiting = true, - ClientWhitelist = new List(), - Limit = 3, - Period = "1s", - PeriodTimespan = 1000 - } - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "http", - Host = "localhost", - Port = consulPort - }, - RateLimitOptions = new FileRateLimitOptions() - { - ClientIdHeader = "ClientId", - DisableRateLimitHeaders = false, - QuotaExceededMessage = "", - RateLimitCounterPrefix = "", - HttpStatusCode = 428 - }, - DownstreamScheme = "http", - } - }; - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 200, "Hello from Laura")) - .And(x => GivenTheConsulConfigurationIs(consulConfig)) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 2)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 1)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) - .BDDfy(); - } - - private void ThenTheConfigIsUpdatedInOcelot() - { - var result = Wait.WaitFor(20000).Until(() => - { - try - { - _steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome"); - _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK); - _steps.ThenTheResponseBodyShouldBe("Hello from Laura"); - return true; - } - catch (Exception) - { - return false; - } - }); - result.ShouldBeTrue(); - } - - private void GivenTheConsulConfigurationIs(FileConfiguration config) - { - _config = config; - } - - private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) - { - foreach (var serviceEntry in serviceEntries) - { - _consulServices.Add(serviceEntry); - } - } - - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) - { - _fakeConsulBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.Run(async context => - { - if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") - { - var json = JsonConvert.SerializeObject(_config); - - var bytes = Encoding.UTF8.GetBytes(json); - - var base64 = Convert.ToBase64String(bytes); - - var kvp = new FakeConsulGetResponse(base64); - json = JsonConvert.SerializeObject(new FakeConsulGetResponse[] { kvp }); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") - { - try - { - var reader = new StreamReader(context.Request.Body); - - // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. - // var json = reader.ReadToEnd(); - var json = await reader.ReadToEndAsync(); - - _config = JsonConvert.DeserializeObject(json); - - var response = JsonConvert.SerializeObject(true); - - await context.Response.WriteAsync(response); - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - else if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") - { - var json = JsonConvert.SerializeObject(_consulServices); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - }) - .Build(); - - _fakeConsulBuilder.Start(); - } - - public class FakeConsulGetResponse - { - public FakeConsulGetResponse(string value) - { - Value = value; - } - - public int CreateIndex => 100; - public int ModifyIndex => 200; - public int LockIndex => 200; - public string Key => "InternalConfiguration"; - public int Flags => 0; - public string Value { get; private set; } - public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; - } - - private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) - { - _builder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => - { - app.UsePathBase(basePath); - - app.Run(async context => - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - }) - .Build(); - - _builder.Start(); - } - - public void Dispose() - { - _builder?.Dispose(); - _steps.Dispose(); - } - - private class FakeCache : IOcelotCache - { - public void Add(string key, FileConfiguration value, TimeSpan ttl, string region) - { - throw new NotImplementedException(); - } - - public FileConfiguration Get(string key, string region) - { - throw new NotImplementedException(); - } - - public void ClearRegion(string region) - { - throw new NotImplementedException(); - } - - public void AddAndDelete(string key, FileConfiguration value, TimeSpan ttl, string region) - { - throw new NotImplementedException(); - } - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Cache; + using Configuration.File; + using Consul; + using Infrastructure; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Newtonsoft.Json; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Text; + using TestStack.BDDfy; + using Xunit; + + public class ConsulConfigurationInConsulTests : IDisposable + { + private IWebHost _builder; + private readonly Steps _steps; + private IWebHost _fakeConsulBuilder; + private FileConfiguration _config; + private readonly List _consulServices; + + public ConsulConfigurationInConsulTests() + { + _consulServices = new List(); + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_with_simple_url() + { + int consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = servicePort, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + + this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_load_configuration_out_of_consul() + { + var consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + + var consulConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/status", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = servicePort, + } + }, + UpstreamPathTemplate = "/cs/status", + UpstreamHttpMethod = new List {"Get"} + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) + .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "/status", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_load_configuration_out_of_consul_if_it_is_changed() + { + var consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + + var consulConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/status", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = servicePort, + } + }, + UpstreamPathTemplate = "/cs/status", + UpstreamHttpMethod = new List {"Get"} + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + var secondConsulConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/status", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = servicePort, + } + }, + UpstreamPathTemplate = "/cs/status/awesome", + UpstreamHttpMethod = new List {"Get"} + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => GivenTheConsulConfigurationIs(consulConfig)) + .And(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, "")) + .And(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{servicePort}", "/status", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .And(x => _steps.WhenIGetUrlOnTheApiGateway("/cs/status")) + .And(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .When(x => GivenTheConsulConfigurationIs(secondConsulConfig)) + .Then(x => ThenTheConfigIsUpdatedInOcelot()) + .BDDfy(); + } + + [Fact] + public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes_and_rate_limit() + { + int consulPort = RandomPortFinder.GetRandomPort(); + const string serviceName = "web"; + int downstreamServicePort = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = downstreamServicePort, + ID = "web_90_0_2_224_8080", + Tags = new[] { "version-v1" } + }, + }; + + var consulConfig = new FileConfiguration + { + DynamicRoutes = new List + { + new FileDynamicRoute + { + ServiceName = serviceName, + RateLimitRule = new FileRateLimitRule() + { + EnableRateLimiting = true, + ClientWhitelist = new List(), + Limit = 3, + Period = "1s", + PeriodTimespan = 1000 + } + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "http", + Host = "localhost", + Port = consulPort + }, + RateLimitOptions = new FileRateLimitOptions() + { + ClientIdHeader = "ClientId", + DisableRateLimitHeaders = false, + QuotaExceededMessage = "", + RateLimitCounterPrefix = "", + HttpStatusCode = 428 + }, + DownstreamScheme = "http", + } + }; + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 200, "Hello from Laura")) + .And(x => GivenTheConsulConfigurationIs(consulConfig)) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningUsingConsulToStoreConfig()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 1)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 2)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(200)) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit("/web/something", 1)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(428)) + .BDDfy(); + } + + private void ThenTheConfigIsUpdatedInOcelot() + { + var result = Wait.WaitFor(20000).Until(() => + { + try + { + _steps.WhenIGetUrlOnTheApiGateway("/cs/status/awesome"); + _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK); + _steps.ThenTheResponseBodyShouldBe("Hello from Laura"); + return true; + } + catch (Exception) + { + return false; + } + }); + result.ShouldBeTrue(); + } + + private void GivenTheConsulConfigurationIs(FileConfiguration config) + { + _config = config; + } + + private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) + { + foreach (var serviceEntry in serviceEntries) + { + _consulServices.Add(serviceEntry); + } + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) + { + _fakeConsulBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + if (context.Request.Method.ToLower() == "get" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") + { + var json = JsonConvert.SerializeObject(_config); + + var bytes = Encoding.UTF8.GetBytes(json); + + var base64 = Convert.ToBase64String(bytes); + + var kvp = new FakeConsulGetResponse(base64); + json = JsonConvert.SerializeObject(new FakeConsulGetResponse[] { kvp }); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + else if (context.Request.Method.ToLower() == "put" && context.Request.Path.Value == "/v1/kv/InternalConfiguration") + { + try + { + var reader = new StreamReader(context.Request.Body); + + // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. + // var json = reader.ReadToEnd(); + var json = await reader.ReadToEndAsync(); + + _config = JsonConvert.DeserializeObject(json); + + var response = JsonConvert.SerializeObject(true); + + await context.Response.WriteAsync(response); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + else if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") + { + var json = JsonConvert.SerializeObject(_consulServices); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + }) + .Build(); + + _fakeConsulBuilder.Start(); + } + + public class FakeConsulGetResponse + { + public FakeConsulGetResponse(string value) + { + Value = value; + } + + public int CreateIndex => 100; + public int ModifyIndex => 200; + public int LockIndex => 200; + public string Key => "InternalConfiguration"; + public int Flags => 0; + public string Value { get; private set; } + public string Session => "adf4238a-882b-9ddc-4a9d-5b6758e4159e"; + } + + private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) + { + _builder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.UsePathBase(basePath); + + app.Run(async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }) + .Build(); + + _builder.Start(); + } + + public void Dispose() + { + _builder?.Dispose(); + _steps.Dispose(); + } + + private class FakeCache : IOcelotCache + { + public void Add(string key, FileConfiguration value, TimeSpan ttl, string region) + { + throw new NotImplementedException(); + } + + public FileConfiguration Get(string key, string region) + { + throw new NotImplementedException(); + } + + public void ClearRegion(string region) + { + throw new NotImplementedException(); + } + + public void AddAndDelete(string key, FileConfiguration value, TimeSpan ttl, string region) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs b/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs index 09213a0a6..1ea93a436 100644 --- a/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs @@ -1,352 +1,352 @@ -namespace Ocelot.AcceptanceTests -{ - using Configuration.File; - using Consul; - using Microsoft.AspNetCore.Http; - using Newtonsoft.Json; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Net.WebSockets; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class ConsulWebSocketTests : IDisposable - { - private readonly List _secondRecieved; - private readonly List _firstRecieved; - private readonly List _serviceEntries; - private readonly Steps _steps; - private readonly ServiceHandler _serviceHandler; - - public ConsulWebSocketTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - _firstRecieved = new List(); - _secondRecieved = new List(); - _serviceEntries = new List(); - } - - [Fact] - public void should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer() - { - var downstreamPort = RandomPortFinder.GetRandomPort(); - var downstreamHost = "localhost"; - - var secondDownstreamPort = RandomPortFinder.GetRandomPort(); - var secondDownstreamHost = "localhost"; - - var serviceName = "websockets"; - var consulPort = RandomPortFinder.GetRandomPort(); - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = downstreamHost, - Port = downstreamPort, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = secondDownstreamHost, - Port = secondDownstreamPort, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var config = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/", - DownstreamPathTemplate = "/ws", - DownstreamScheme = "ws", - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "RoundRobin" }, - ServiceName = serviceName, - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "http", - Host = "localhost", - Port = consulPort, - Type = "consul" - } - } - }; - - this.Given(_ => _steps.GivenThereIsAConfiguration(config)) - .And(_ => _steps.StartFakeOcelotWithWebSocketsWithConsul()) - .And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) - .And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws")) - .When(_ => WhenIStartTheClients()) - .Then(_ => ThenBothDownstreamServicesAreCalled()) - .BDDfy(); - } - - private void ThenBothDownstreamServicesAreCalled() - { - _firstRecieved.Count.ShouldBe(10); - _firstRecieved.ForEach(x => - { - x.ShouldBe("test"); - }); - - _secondRecieved.Count.ShouldBe(10); - _secondRecieved.ForEach(x => - { - x.ShouldBe("chocolate"); - }); - } - - private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) - { - foreach (var serviceEntry in serviceEntries) - { - _serviceEntries.Add(serviceEntry); - } - } - - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") - { - var json = JsonConvert.SerializeObject(_serviceEntries); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - } - - private async Task WhenIStartTheClients() - { - var firstClient = StartClient("ws://localhost:5000/"); - - var secondClient = StartSecondClient("ws://localhost:5000/"); - - await Task.WhenAll(firstClient, secondClient); - } - - private async Task StartClient(string url) - { - var client = new ClientWebSocket(); - - await client.ConnectAsync(new Uri(url), CancellationToken.None); - - var sending = Task.Run(async () => - { - string line = "test"; - for (int i = 0; i < 10; i++) - { - var bytes = Encoding.UTF8.GetBytes(line); - - await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, - CancellationToken.None); - await Task.Delay(10); - } - - await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - }); - - var receiving = Task.Run(async () => - { - var buffer = new byte[1024 * 4]; - - while (true) - { - var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - if (result.MessageType == WebSocketMessageType.Text) - { - _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); - } - else if (result.MessageType == WebSocketMessageType.Close) - { - if (client.State != WebSocketState.Closed) - { - // Last version, the client state is CloseReceived - // Valid states are: Open, CloseReceived, CloseSent - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - } - - break; - } - } - }); - - await Task.WhenAll(sending, receiving); - } - - private async Task StartSecondClient(string url) - { - await Task.Delay(500); - - var client = new ClientWebSocket(); - - await client.ConnectAsync(new Uri(url), CancellationToken.None); - - var sending = Task.Run(async () => - { - string line = "test"; - for (int i = 0; i < 10; i++) - { - var bytes = Encoding.UTF8.GetBytes(line); - - await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, - CancellationToken.None); - await Task.Delay(10); - } - - await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - }); - - var receiving = Task.Run(async () => - { - var buffer = new byte[1024 * 4]; - - while (true) - { - var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - if (result.MessageType == WebSocketMessageType.Text) - { - _secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); - } - else if (result.MessageType == WebSocketMessageType.Close) - { - if (client.State != WebSocketState.Closed) - { - // Last version, the client state is CloseReceived - // Valid states are: Open, CloseReceived, CloseSent - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - } - - break; - } - } - }); - - await Task.WhenAll(sending, receiving); - } - - private async Task StartFakeDownstreamService(string url, string path) - { - await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) => - { - if (context.Request.Path == path) - { - if (context.WebSockets.IsWebSocketRequest) - { - var webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Echo(webSocket); - } - else - { - context.Response.StatusCode = 400; - } - } - else - { - await next(); - } - }); - } - - private async Task StartSecondFakeDownstreamService(string url, string path) - { - await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) => - { - if (context.Request.Path == path) - { - if (context.WebSockets.IsWebSocketRequest) - { - WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Message(webSocket); - } - else - { - context.Response.StatusCode = 400; - } - } - else - { - await next(); - } - }); - } - - private async Task Echo(WebSocket webSocket) - { - try - { - var buffer = new byte[1024 * 4]; - - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - while (!result.CloseStatus.HasValue) - { - await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); - - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } - - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - private async Task Message(WebSocket webSocket) - { - try - { - var buffer = new byte[1024 * 4]; - - var bytes = Encoding.UTF8.GetBytes("chocolate"); - - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - while (!result.CloseStatus.HasValue) - { - await webSocket.SendAsync(new ArraySegment(bytes), result.MessageType, result.EndOfMessage, CancellationToken.None); - - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } - - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Configuration.File; + using Consul; + using Microsoft.AspNetCore.Http; + using Newtonsoft.Json; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Net.WebSockets; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class ConsulWebSocketTests : IDisposable + { + private readonly List _secondRecieved; + private readonly List _firstRecieved; + private readonly List _serviceEntries; + private readonly Steps _steps; + private readonly ServiceHandler _serviceHandler; + + public ConsulWebSocketTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + _firstRecieved = new List(); + _secondRecieved = new List(); + _serviceEntries = new List(); + } + + [Fact] + public void should_proxy_websocket_input_to_downstream_service_and_use_service_discovery_and_load_balancer() + { + var downstreamPort = RandomPortFinder.GetRandomPort(); + var downstreamHost = "localhost"; + + var secondDownstreamPort = RandomPortFinder.GetRandomPort(); + var secondDownstreamHost = "localhost"; + + var serviceName = "websockets"; + var consulPort = RandomPortFinder.GetRandomPort(); + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = downstreamHost, + Port = downstreamPort, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = secondDownstreamHost, + Port = secondDownstreamPort, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var config = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + UpstreamPathTemplate = "/", + DownstreamPathTemplate = "/ws", + DownstreamScheme = "ws", + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "RoundRobin" }, + ServiceName = serviceName, + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "http", + Host = "localhost", + Port = consulPort, + Type = "consul" + } + } + }; + + this.Given(_ => _steps.GivenThereIsAConfiguration(config)) + .And(_ => _steps.StartFakeOcelotWithWebSocketsWithConsul()) + .And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) + .And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws")) + .When(_ => WhenIStartTheClients()) + .Then(_ => ThenBothDownstreamServicesAreCalled()) + .BDDfy(); + } + + private void ThenBothDownstreamServicesAreCalled() + { + _firstRecieved.Count.ShouldBe(10); + _firstRecieved.ForEach(x => + { + x.ShouldBe("test"); + }); + + _secondRecieved.Count.ShouldBe(10); + _secondRecieved.ForEach(x => + { + x.ShouldBe("chocolate"); + }); + } + + private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) + { + foreach (var serviceEntry in serviceEntries) + { + _serviceEntries.Add(serviceEntry); + } + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") + { + var json = JsonConvert.SerializeObject(_serviceEntries); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + } + + private async Task WhenIStartTheClients() + { + var firstClient = StartClient("ws://localhost:5000/"); + + var secondClient = StartSecondClient("ws://localhost:5000/"); + + await Task.WhenAll(firstClient, secondClient); + } + + private async Task StartClient(string url) + { + var client = new ClientWebSocket(); + + await client.ConnectAsync(new Uri(url), CancellationToken.None); + + var sending = Task.Run(async () => + { + string line = "test"; + for (int i = 0; i < 10; i++) + { + var bytes = Encoding.UTF8.GetBytes(line); + + await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, + CancellationToken.None); + await Task.Delay(10); + } + + await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + }); + + var receiving = Task.Run(async () => + { + var buffer = new byte[1024 * 4]; + + while (true) + { + var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + if (result.MessageType == WebSocketMessageType.Text) + { + _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + if (client.State != WebSocketState.Closed) + { + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + } + + break; + } + } + }); + + await Task.WhenAll(sending, receiving); + } + + private async Task StartSecondClient(string url) + { + await Task.Delay(500); + + var client = new ClientWebSocket(); + + await client.ConnectAsync(new Uri(url), CancellationToken.None); + + var sending = Task.Run(async () => + { + string line = "test"; + for (int i = 0; i < 10; i++) + { + var bytes = Encoding.UTF8.GetBytes(line); + + await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, + CancellationToken.None); + await Task.Delay(10); + } + + await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + }); + + var receiving = Task.Run(async () => + { + var buffer = new byte[1024 * 4]; + + while (true) + { + var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + if (result.MessageType == WebSocketMessageType.Text) + { + _secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + if (client.State != WebSocketState.Closed) + { + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + } + + break; + } + } + }); + + await Task.WhenAll(sending, receiving); + } + + private async Task StartFakeDownstreamService(string url, string path) + { + await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) => + { + if (context.Request.Path == path) + { + if (context.WebSockets.IsWebSocketRequest) + { + var webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Echo(webSocket); + } + else + { + context.Response.StatusCode = 400; + } + } + else + { + await next(); + } + }); + } + + private async Task StartSecondFakeDownstreamService(string url, string path) + { + await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) => + { + if (context.Request.Path == path) + { + if (context.WebSockets.IsWebSocketRequest) + { + WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Message(webSocket); + } + else + { + context.Response.StatusCode = 400; + } + } + else + { + await next(); + } + }); + } + + private async Task Echo(WebSocket webSocket) + { + try + { + var buffer = new byte[1024 * 4]; + + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + while (!result.CloseStatus.HasValue) + { + await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + private async Task Message(WebSocket webSocket) + { + try + { + var buffer = new byte[1024 * 4]; + + var bytes = Encoding.UTF8.GetBytes("chocolate"); + + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + while (!result.CloseStatus.HasValue) + { + await webSocket.SendAsync(new ArraySegment(bytes), result.MessageType, result.EndOfMessage, CancellationToken.None); + + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ContentTests.cs b/test/Ocelot.AcceptanceTests/ContentTests.cs index 31a16066f..8137bc8a5 100644 --- a/test/Ocelot.AcceptanceTests/ContentTests.cs +++ b/test/Ocelot.AcceptanceTests/ContentTests.cs @@ -1,182 +1,182 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class ContentTests : IDisposable - { - private readonly Steps _steps; - private string _contentType; - private long? _contentLength; - private bool _contentTypeHeaderExists; - private readonly ServiceHandler _serviceHandler; - - public ContentTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_not_add_content_type_or_content_length_headers() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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")) - .And(x => ThenTheContentTypeShouldBeEmpty()) - .And(x => ThenTheContentLengthShouldBeZero()) - .BDDfy(); - } - - [Fact] - public void should_add_content_type_and_content_length_headers() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - } - } - }; - - var contentType = "application/json"; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 201, string.Empty)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenThePostHasContent("postContent")) - .And(x => _steps.GivenThePostHasContentType(contentType)) - .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) - .And(x => ThenTheContentLengthIs(11)) - .And(x => ThenTheContentTypeIsIs(contentType)) - .BDDfy(); - } - - [Fact] - public void should_add_default_content_type_header() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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)) - .And(x => ThenTheContentLengthIs(11)) - .And(x => ThenTheContentTypeIsIs("text/plain; charset=utf-8")) - .BDDfy(); - } - - private void ThenTheContentTypeIsIs(string expected) - { - _contentType.ShouldBe(expected); - } - - private void ThenTheContentLengthShouldBeZero() - { - _contentLength.ShouldBeEquivalentTo(0L); - } - - private void ThenTheContentLengthIs(int expected) - { - _contentLength.ShouldBe(expected); - } - - private void ThenTheContentTypeShouldBeEmpty() - { - _contentType.ShouldBeNullOrEmpty(); - _contentTypeHeaderExists.ShouldBe(false); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - _contentType = context.Request.ContentType; - _contentLength = context.Request.ContentLength; - _contentTypeHeaderExists = context.Request.Headers.TryGetValue("Content-Type", out var value); - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class ContentTests : IDisposable + { + private readonly Steps _steps; + private string _contentType; + private long? _contentLength; + private bool _contentTypeHeaderExists; + private readonly ServiceHandler _serviceHandler; + + public ContentTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_not_add_content_type_or_content_length_headers() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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")) + .And(x => ThenTheContentTypeShouldBeEmpty()) + .And(x => ThenTheContentLengthShouldBeZero()) + .BDDfy(); + } + + [Fact] + public void should_add_content_type_and_content_length_headers() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + } + } + }; + + var contentType = "application/json"; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 201, string.Empty)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenThePostHasContent("postContent")) + .And(x => _steps.GivenThePostHasContentType(contentType)) + .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) + .And(x => ThenTheContentLengthIs(11)) + .And(x => ThenTheContentTypeIsIs(contentType)) + .BDDfy(); + } + + [Fact] + public void should_add_default_content_type_header() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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)) + .And(x => ThenTheContentLengthIs(11)) + .And(x => ThenTheContentTypeIsIs("text/plain; charset=utf-8")) + .BDDfy(); + } + + private void ThenTheContentTypeIsIs(string expected) + { + _contentType.ShouldBe(expected); + } + + private void ThenTheContentLengthShouldBeZero() + { + _contentLength.ShouldBeEquivalentTo(0L); + } + + private void ThenTheContentLengthIs(int expected) + { + _contentLength.ShouldBe(expected); + } + + private void ThenTheContentTypeShouldBeEmpty() + { + _contentType.ShouldBeNullOrEmpty(); + _contentTypeHeaderExists.ShouldBe(false); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + _contentType = context.Request.ContentType; + _contentLength = context.Request.ContentLength; + _contentTypeHeaderExists = context.Request.Headers.TryGetValue("Content-Type", out var value); + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs index 2f7a27568..f420af6a0 100644 --- a/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs +++ b/test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs @@ -43,9 +43,9 @@ public void should_call_pre_query_string_builder_middleware() var fileConfiguration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -88,9 +88,9 @@ public void should_call_authorisation_middleware() var fileConfiguration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -133,9 +133,9 @@ public void should_call_authentication_middleware() var fileConfiguration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/41879/", DownstreamHostAndPorts = new List @@ -178,9 +178,9 @@ public void should_call_pre_error_middleware() var fileConfiguration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -223,9 +223,9 @@ public void should_call_pre_authorisation_middleware() var fileConfiguration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -268,9 +268,9 @@ public void should_call_pre_http_authentication_middleware() var fileConfiguration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -317,9 +317,9 @@ public void should_fix_issue_237() var fileConfiguration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/west", DownstreamHostAndPorts = new List diff --git a/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs index d1e63c819..dedb32bd3 100644 --- a/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/EurekaServiceDiscoveryTests.cs @@ -1,283 +1,283 @@ -namespace Ocelot.AcceptanceTests -{ - using Configuration.File; - using Microsoft.AspNetCore.Http; - using Newtonsoft.Json; - using Steeltoe.Common.Discovery; - using System; - using System.Collections.Generic; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class EurekaServiceDiscoveryTests : IDisposable - { - private readonly Steps _steps; - private readonly List _eurekaInstances; - private readonly ServiceHandler _serviceHandler; - - public EurekaServiceDiscoveryTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - _eurekaInstances = new List(); - } - - [Fact] - public void should_use_eureka_service_discovery_and_make_request() - { - var eurekaPort = 8761; - var serviceName = "product"; - var downstreamServicePort = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; - var fakeEurekaServiceDiscoveryUrl = $"http://localhost:{eurekaPort}"; - - var instanceOne = new FakeEurekaService(serviceName, "localhost", downstreamServicePort, false, - new Uri($"http://localhost:{downstreamServicePort}"), new Dictionary()); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = serviceName, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Type = "Eureka" - } - } - }; - - this.Given(x => x.GivenEurekaProductServiceOneIsRunning(downstreamServiceOneUrl)) - .And(x => x.GivenThereIsAFakeEurekaServiceDiscoveryProvider(fakeEurekaServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithEureka(instanceOne)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithEureka()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(_ => _steps.ThenTheResponseBodyShouldBe(nameof(EurekaServiceDiscoveryTests))) - .BDDfy(); - } - - private void GivenTheServicesAreRegisteredWithEureka(params IServiceInstance[] serviceInstances) - { - foreach (var instance in serviceInstances) - { - _eurekaInstances.Add(instance); - } - } - - private void GivenThereIsAFakeEurekaServiceDiscoveryProvider(string url, string serviceName) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - if (context.Request.Path.Value == "/eureka/apps/") - { - var apps = new List(); - - foreach (var serviceInstance in _eurekaInstances) - { - var a = new Application - { - name = serviceName, - instance = new List - { - new Instance - { - instanceId = $"{serviceInstance.Host}:{serviceInstance}", - hostName = serviceInstance.Host, - app = serviceName, - ipAddr = "127.0.0.1", - status = "UP", - overriddenstatus = "UNKNOWN", - port = new Port {value = serviceInstance.Port, enabled = "true"}, - securePort = new SecurePort {value = serviceInstance.Port, enabled = "true"}, - countryId = 1, - dataCenterInfo = new DataCenterInfo {value = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", name = "MyOwn"}, - leaseInfo = new LeaseInfo - { - renewalIntervalInSecs = 30, - durationInSecs = 90, - registrationTimestamp = 1457714988223, - lastRenewalTimestamp= 1457716158319, - evictionTimestamp = 0, - serviceUpTimestamp = 1457714988223 - }, - metadata = new Metadata - { - value = "java.util.Collections$EmptyMap" - }, - homePageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", - statusPageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", - healthCheckUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", - vipAddress = serviceName, - isCoordinatingDiscoveryServer = "false", - lastUpdatedTimestamp = "1457714988223", - lastDirtyTimestamp = "1457714988172", - actionType = "ADDED" - } - } - }; - - apps.Add(a); - } - - var applications = new EurekaApplications - { - applications = new Applications - { - application = apps, - apps__hashcode = "UP_1_", - versions__delta = "1" - } - }; - - var json = JsonConvert.SerializeObject(applications); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - } - - private void GivenEurekaProductServiceOneIsRunning(string url) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - try - { - context.Response.StatusCode = 200; - await context.Response.WriteAsync(nameof(EurekaServiceDiscoveryTests)); - } - catch (Exception exception) - { - await context.Response.WriteAsync(exception.StackTrace); - } - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } - - public class FakeEurekaService : IServiceInstance - { - public FakeEurekaService(string serviceId, string host, int port, bool isSecure, Uri uri, IDictionary metadata) - { - ServiceId = serviceId; - Host = host; - Port = port; - IsSecure = isSecure; - Uri = uri; - Metadata = metadata; - } - - public string ServiceId { get; } - public string Host { get; } - public int Port { get; } - public bool IsSecure { get; } - public Uri Uri { get; } - public IDictionary Metadata { get; } - } - - public class Port - { - [JsonProperty("$")] - public int value { get; set; } - - [JsonProperty("@enabled")] - public string enabled { get; set; } - } - - public class SecurePort - { - [JsonProperty("$")] - public int value { get; set; } - - [JsonProperty("@enabled")] - public string enabled { get; set; } - } - - public class DataCenterInfo - { - [JsonProperty("@class")] - public string value { get; set; } - - public string name { get; set; } - } - - public class LeaseInfo - { - public int renewalIntervalInSecs { get; set; } - - public int durationInSecs { get; set; } - - public long registrationTimestamp { get; set; } - - public long lastRenewalTimestamp { get; set; } - - public int evictionTimestamp { get; set; } - - public long serviceUpTimestamp { get; set; } - } - - public class Metadata - { - [JsonProperty("@class")] - public string value { get; set; } - } - - public class Instance - { - public string instanceId { get; set; } - public string hostName { get; set; } - public string app { get; set; } - public string ipAddr { get; set; } - public string status { get; set; } - public string overriddenstatus { get; set; } - public Port port { get; set; } - public SecurePort securePort { get; set; } - public int countryId { get; set; } - public DataCenterInfo dataCenterInfo { get; set; } - public LeaseInfo leaseInfo { get; set; } - public Metadata metadata { get; set; } - public string homePageUrl { get; set; } - public string statusPageUrl { get; set; } - public string healthCheckUrl { get; set; } - public string vipAddress { get; set; } - public string isCoordinatingDiscoveryServer { get; set; } - public string lastUpdatedTimestamp { get; set; } - public string lastDirtyTimestamp { get; set; } - public string actionType { get; set; } - } - - public class Application - { - public string name { get; set; } - public List instance { get; set; } - } - - public class Applications - { - public string versions__delta { get; set; } - public string apps__hashcode { get; set; } - public List application { get; set; } - } - - public class EurekaApplications - { - public Applications applications { get; set; } - } -} +namespace Ocelot.AcceptanceTests +{ + using Configuration.File; + using Microsoft.AspNetCore.Http; + using Newtonsoft.Json; + using Steeltoe.Common.Discovery; + using System; + using System.Collections.Generic; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class EurekaServiceDiscoveryTests : IDisposable + { + private readonly Steps _steps; + private readonly List _eurekaInstances; + private readonly ServiceHandler _serviceHandler; + + public EurekaServiceDiscoveryTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + _eurekaInstances = new List(); + } + + [Fact] + public void should_use_eureka_service_discovery_and_make_request() + { + var eurekaPort = 8761; + var serviceName = "product"; + var downstreamServicePort = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; + var fakeEurekaServiceDiscoveryUrl = $"http://localhost:{eurekaPort}"; + + var instanceOne = new FakeEurekaService(serviceName, "localhost", downstreamServicePort, false, + new Uri($"http://localhost:{downstreamServicePort}"), new Dictionary()); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = serviceName, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Type = "Eureka" + } + } + }; + + this.Given(x => x.GivenEurekaProductServiceOneIsRunning(downstreamServiceOneUrl)) + .And(x => x.GivenThereIsAFakeEurekaServiceDiscoveryProvider(fakeEurekaServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithEureka(instanceOne)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithEureka()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(nameof(EurekaServiceDiscoveryTests))) + .BDDfy(); + } + + private void GivenTheServicesAreRegisteredWithEureka(params IServiceInstance[] serviceInstances) + { + foreach (var instance in serviceInstances) + { + _eurekaInstances.Add(instance); + } + } + + private void GivenThereIsAFakeEurekaServiceDiscoveryProvider(string url, string serviceName) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + if (context.Request.Path.Value == "/eureka/apps/") + { + var apps = new List(); + + foreach (var serviceInstance in _eurekaInstances) + { + var a = new Application + { + name = serviceName, + instance = new List + { + new Instance + { + instanceId = $"{serviceInstance.Host}:{serviceInstance}", + hostName = serviceInstance.Host, + app = serviceName, + ipAddr = "127.0.0.1", + status = "UP", + overriddenstatus = "UNKNOWN", + port = new Port {value = serviceInstance.Port, enabled = "true"}, + securePort = new SecurePort {value = serviceInstance.Port, enabled = "true"}, + countryId = 1, + dataCenterInfo = new DataCenterInfo {value = "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", name = "MyOwn"}, + leaseInfo = new LeaseInfo + { + renewalIntervalInSecs = 30, + durationInSecs = 90, + registrationTimestamp = 1457714988223, + lastRenewalTimestamp= 1457716158319, + evictionTimestamp = 0, + serviceUpTimestamp = 1457714988223 + }, + metadata = new Metadata + { + value = "java.util.Collections$EmptyMap" + }, + homePageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", + statusPageUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", + healthCheckUrl = $"{serviceInstance.Host}:{serviceInstance.Port}", + vipAddress = serviceName, + isCoordinatingDiscoveryServer = "false", + lastUpdatedTimestamp = "1457714988223", + lastDirtyTimestamp = "1457714988172", + actionType = "ADDED" + } + } + }; + + apps.Add(a); + } + + var applications = new EurekaApplications + { + applications = new Applications + { + application = apps, + apps__hashcode = "UP_1_", + versions__delta = "1" + } + }; + + var json = JsonConvert.SerializeObject(applications); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + } + + private void GivenEurekaProductServiceOneIsRunning(string url) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + try + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync(nameof(EurekaServiceDiscoveryTests)); + } + catch (Exception exception) + { + await context.Response.WriteAsync(exception.StackTrace); + } + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } + + public class FakeEurekaService : IServiceInstance + { + public FakeEurekaService(string serviceId, string host, int port, bool isSecure, Uri uri, IDictionary metadata) + { + ServiceId = serviceId; + Host = host; + Port = port; + IsSecure = isSecure; + Uri = uri; + Metadata = metadata; + } + + public string ServiceId { get; } + public string Host { get; } + public int Port { get; } + public bool IsSecure { get; } + public Uri Uri { get; } + public IDictionary Metadata { get; } + } + + public class Port + { + [JsonProperty("$")] + public int value { get; set; } + + [JsonProperty("@enabled")] + public string enabled { get; set; } + } + + public class SecurePort + { + [JsonProperty("$")] + public int value { get; set; } + + [JsonProperty("@enabled")] + public string enabled { get; set; } + } + + public class DataCenterInfo + { + [JsonProperty("@class")] + public string value { get; set; } + + public string name { get; set; } + } + + public class LeaseInfo + { + public int renewalIntervalInSecs { get; set; } + + public int durationInSecs { get; set; } + + public long registrationTimestamp { get; set; } + + public long lastRenewalTimestamp { get; set; } + + public int evictionTimestamp { get; set; } + + public long serviceUpTimestamp { get; set; } + } + + public class Metadata + { + [JsonProperty("@class")] + public string value { get; set; } + } + + public class Instance + { + public string instanceId { get; set; } + public string hostName { get; set; } + public string app { get; set; } + public string ipAddr { get; set; } + public string status { get; set; } + public string overriddenstatus { get; set; } + public Port port { get; set; } + public SecurePort securePort { get; set; } + public int countryId { get; set; } + public DataCenterInfo dataCenterInfo { get; set; } + public LeaseInfo leaseInfo { get; set; } + public Metadata metadata { get; set; } + public string homePageUrl { get; set; } + public string statusPageUrl { get; set; } + public string healthCheckUrl { get; set; } + public string vipAddress { get; set; } + public string isCoordinatingDiscoveryServer { get; set; } + public string lastUpdatedTimestamp { get; set; } + public string lastDirtyTimestamp { get; set; } + public string actionType { get; set; } + } + + public class Application + { + public string name { get; set; } + public List instance { get; set; } + } + + public class Applications + { + public string versions__delta { get; set; } + public string apps__hashcode { get; set; } + public List application { get; set; } + } + + public class EurekaApplications + { + public Applications applications { get; set; } + } +} diff --git a/test/Ocelot.AcceptanceTests/GzipTests.cs b/test/Ocelot.AcceptanceTests/GzipTests.cs index 61237b38f..e3ee38442 100644 --- a/test/Ocelot.AcceptanceTests/GzipTests.cs +++ b/test/Ocelot.AcceptanceTests/GzipTests.cs @@ -30,9 +30,9 @@ public void should_return_response_200_with_simple_url() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs index 82157335c..f5d69d138 100644 --- a/test/Ocelot.AcceptanceTests/HeaderTests.cs +++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs @@ -29,9 +29,9 @@ public void should_transform_upstream_header() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -70,9 +70,9 @@ public void should_transform_downstream_header() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -110,9 +110,9 @@ public void should_fix_issue_190() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -154,9 +154,9 @@ public void should_fix_issue_205() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -198,9 +198,9 @@ public void should_fix_issue_417() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -246,9 +246,9 @@ public void request_should_reuse_cookies_with_cookie_container() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/sso/{everything}", DownstreamScheme = "http", @@ -289,9 +289,9 @@ public void request_should_have_own_cookies_no_cookie_container() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/sso/{everything}", DownstreamScheme = "http", @@ -332,9 +332,9 @@ public void issue_474_should_not_put_spaces_in_header() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -369,9 +369,9 @@ public void issue_474_should_put_spaces_in_header() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", diff --git a/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs b/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs index 58dca0afd..129935d48 100644 --- a/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpClientCachingTests.cs @@ -1,167 +1,167 @@ -namespace Ocelot.AcceptanceTests -{ - using Configuration; - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using Requester; - using Shouldly; - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class HttpClientCachingTests : IDisposable - { - private readonly Steps _steps; - private string _downstreamPath; - private readonly ServiceHandler _serviceHandler; - - public HttpClientCachingTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_cache_one_http_client_same_re_route() +namespace Ocelot.AcceptanceTests +{ + using Configuration; + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using Requester; + using Shouldly; + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class HttpClientCachingTests : IDisposable + { + private readonly Steps _steps; + private string _downstreamPath; + private readonly ServiceHandler _serviceHandler; + + public HttpClientCachingTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_cache_one_http_client_same_re_route() { var port = RandomPortFinder.GetRandomPort(); - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - var cache = new FakeHttpClientCache(); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache)) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => cache.Count.ShouldBe(1)) - .BDDfy(); - } - - [Fact] - public void should_cache_two_http_client_different_re_route() + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + var cache = new FakeHttpClientCache(); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => cache.Count.ShouldBe(1)) + .BDDfy(); + } + + [Fact] + public void should_cache_two_http_client_different_re_route() { var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - }, - new FileReRoute - { - DownstreamPathTemplate = "/two", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/two", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - var cache = new FakeHttpClientCache(); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache)) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => cache.Count.ShouldBe(2)) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context => - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - } - - public void Dispose() - { - _serviceHandler.Dispose(); - _steps.Dispose(); - } - - public class FakeHttpClientCache : IHttpClientCache - { - private readonly ConcurrentDictionary _httpClientsCache; - - public FakeHttpClientCache() - { - _httpClientsCache = new ConcurrentDictionary(); - } - - public void Set(DownstreamReRoute key, IHttpClient client, TimeSpan expirationTime) - { - _httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client); - } - - public IHttpClient Get(DownstreamReRoute key) - { - //todo handle error? - return _httpClientsCache.TryGetValue(key, out var client) ? client : null; - } - - public int Count => _httpClientsCache.Count; - } - } -} + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + }, + new FileRoute + { + DownstreamPathTemplate = "/two", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/two", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + var cache = new FakeHttpClientCache(); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithFakeHttpClientCache(cache)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/two")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => cache.Count.ShouldBe(2)) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + } + + public void Dispose() + { + _serviceHandler.Dispose(); + _steps.Dispose(); + } + + public class FakeHttpClientCache : IHttpClientCache + { + private readonly ConcurrentDictionary _httpClientsCache; + + public FakeHttpClientCache() + { + _httpClientsCache = new ConcurrentDictionary(); + } + + public void Set(DownstreamRoute key, IHttpClient client, TimeSpan expirationTime) + { + _httpClientsCache.AddOrUpdate(key, client, (k, oldValue) => client); + } + + public IHttpClient Get(DownstreamRoute key) + { + //todo handle error? + return _httpClientsCache.TryGetValue(key, out var client) ? client : null; + } + + public int Count => _httpClientsCache.Count; + } + } +} diff --git a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs index 57c2dc275..91e4da657 100644 --- a/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs @@ -1,296 +1,296 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class HttpDelegatingHandlersTests : IDisposable - { - private readonly Steps _steps; - private string _downstreamPath; - private readonly ServiceHandler _serviceHandler; - - public HttpDelegatingHandlersTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_call_re_route_ordered_specific_handlers() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - DelegatingHandlers = new List - { - "FakeHandlerTwo", - "FakeHandler" - } - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithSpecficHandlersRegisteredInDi()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => ThenTheOrderedHandlersAreCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_call_global_di_handlers() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => ThenTheHandlersAreCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_call_global_di_handlers_multiple_times() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlerRegisteredInDi()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_call_global_di_handlers_with_dependency() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - var dependency = new FakeDependency(); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi(dependency)) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(x => ThenTheDependencyIsCalled(dependency)) - .BDDfy(); - } - - private void ThenTheDependencyIsCalled(FakeDependency dependency) - { - dependency.Called.ShouldBeTrue(); - } - - private void ThenTheHandlersAreCalledCorrectly() - { - FakeHandler.TimeCalled.ShouldBeLessThan(FakeHandlerTwo.TimeCalled); - } - - private void ThenTheOrderedHandlersAreCalledCorrectly() - { - FakeHandlerTwo.TimeCalled.ShouldBeLessThan(FakeHandler.TimeCalled); - } - - public class FakeDependency - { - public bool Called; - } - - // ReSharper disable once ClassNeverInstantiated.Local - private class FakeHandlerWithDependency : DelegatingHandler - { - private readonly FakeDependency _dependency; - - public FakeHandlerWithDependency(FakeDependency dependency) - { - _dependency = dependency; - } - - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - _dependency.Called = true; - return base.SendAsync(request, cancellationToken); - } - } - - // ReSharper disable once ClassNeverInstantiated.Local - private class FakeHandler : DelegatingHandler - { - public static DateTime TimeCalled { get; private set; } - - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - TimeCalled = DateTime.Now; - return base.SendAsync(request, cancellationToken); - } - } - - // ReSharper disable once ClassNeverInstantiated.Local - private class FakeHandlerTwo : DelegatingHandler - { - public static DateTime TimeCalled { get; private set; } - - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - TimeCalled = DateTime.Now; - return base.SendAsync(request, cancellationToken); - } - } - - // ReSharper disable once ClassNeverInstantiated.Local - private class FakeHandlerAgain : DelegatingHandler - { - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - Console.WriteLine(request.RequestUri); - - //do stuff and optionally call the base handler.. - return await base.SendAsync(request, cancellationToken); - } - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); - } - }); - } - - public void Dispose() - { - _steps?.Dispose(); - _serviceHandler?.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Net; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class HttpDelegatingHandlersTests : IDisposable + { + private readonly Steps _steps; + private string _downstreamPath; + private readonly ServiceHandler _serviceHandler; + + public HttpDelegatingHandlersTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_call_re_route_ordered_specific_handlers() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + DelegatingHandlers = new List + { + "FakeHandlerTwo", + "FakeHandler" + } + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithSpecficHandlersRegisteredInDi()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => ThenTheOrderedHandlersAreCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_call_global_di_handlers() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => ThenTheHandlersAreCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_call_global_di_handlers_multiple_times() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlerRegisteredInDi()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_call_global_di_handlers_with_dependency() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + var dependency = new FakeDependency(); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi(dependency)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(x => ThenTheDependencyIsCalled(dependency)) + .BDDfy(); + } + + private void ThenTheDependencyIsCalled(FakeDependency dependency) + { + dependency.Called.ShouldBeTrue(); + } + + private void ThenTheHandlersAreCalledCorrectly() + { + FakeHandler.TimeCalled.ShouldBeLessThan(FakeHandlerTwo.TimeCalled); + } + + private void ThenTheOrderedHandlersAreCalledCorrectly() + { + FakeHandlerTwo.TimeCalled.ShouldBeLessThan(FakeHandler.TimeCalled); + } + + public class FakeDependency + { + public bool Called; + } + + // ReSharper disable once ClassNeverInstantiated.Local + private class FakeHandlerWithDependency : DelegatingHandler + { + private readonly FakeDependency _dependency; + + public FakeHandlerWithDependency(FakeDependency dependency) + { + _dependency = dependency; + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + _dependency.Called = true; + return base.SendAsync(request, cancellationToken); + } + } + + // ReSharper disable once ClassNeverInstantiated.Local + private class FakeHandler : DelegatingHandler + { + public static DateTime TimeCalled { get; private set; } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + TimeCalled = DateTime.Now; + return base.SendAsync(request, cancellationToken); + } + } + + // ReSharper disable once ClassNeverInstantiated.Local + private class FakeHandlerTwo : DelegatingHandler + { + public static DateTime TimeCalled { get; private set; } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + TimeCalled = DateTime.Now; + return base.SendAsync(request, cancellationToken); + } + } + + // ReSharper disable once ClassNeverInstantiated.Local + private class FakeHandlerAgain : DelegatingHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + Console.WriteLine(request.RequestUri); + + //do stuff and optionally call the base handler.. + return await base.SendAsync(request, cancellationToken); + } + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); + } + }); + } + + public void Dispose() + { + _steps?.Dispose(); + _serviceHandler?.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/HttpTests.cs b/test/Ocelot.AcceptanceTests/HttpTests.cs index da59d0008..ab7826a7c 100644 --- a/test/Ocelot.AcceptanceTests/HttpTests.cs +++ b/test/Ocelot.AcceptanceTests/HttpTests.cs @@ -1,243 +1,243 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Net.Http; - using Microsoft.AspNetCore.Server.Kestrel.Core; - using TestStack.BDDfy; - using Xunit; - - public class HttpTests : IDisposable - { - private readonly Steps _steps; - private readonly ServiceHandler _serviceHandler; - - public HttpTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_return_response_200_when_using_http_one() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{url}", - DownstreamScheme = "https", - UpstreamPathTemplate = "/{url}", - UpstreamHttpMethod = new List { "Get" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - }, - }, - DownstreamHttpMethod = "POST", - DownstreamHttpVersion = "1.0", - DangerousAcceptAnyServerCertificateValidator = true - }, - }, - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_when_using_http_one_point_one() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{url}", - DownstreamScheme = "https", - UpstreamPathTemplate = "/{url}", - UpstreamHttpMethod = new List { "Get" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - }, - }, - DownstreamHttpMethod = "POST", - DownstreamHttpVersion = "1.1", - DangerousAcceptAnyServerCertificateValidator = true - }, - }, - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_when_using_http_two_point_zero() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{url}", - DownstreamScheme = "https", - UpstreamPathTemplate = "/{url}", - UpstreamHttpMethod = new List { "Get" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - }, - }, - DownstreamHttpMethod = "POST", - DownstreamHttpVersion = "2.0", - DangerousAcceptAnyServerCertificateValidator = true - }, - }, - }; - - const string expected = "here is some content"; - var httpContent = new StringContent(expected); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http2)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) - .BDDfy(); - } - - [Fact] - public void should_return_response_502_when_using_http_one_to_talk_to_server_running_http_two() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{url}", - DownstreamScheme = "https", - UpstreamPathTemplate = "/{url}", - UpstreamHttpMethod = new List { "Get" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - }, - }, - DownstreamHttpMethod = "POST", - DownstreamHttpVersion = "1.1", - DangerousAcceptAnyServerCertificateValidator = true - }, - }, - }; - - const string expected = "here is some content"; - var httpContent = new StringContent(expected); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http2)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.BadGateway)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_when_using_http_two_to_talk_to_server_running_http_one_point_one() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{url}", - DownstreamScheme = "https", - UpstreamPathTemplate = "/{url}", - UpstreamHttpMethod = new List { "Get" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - }, - }, - DownstreamHttpMethod = "POST", - DownstreamHttpVersion = "2.0", - DangerousAcceptAnyServerCertificateValidator = true - }, - }, - }; - - const string expected = "here is some content"; - var httpContent = new StringContent(expected); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int port, HttpProtocols protocols) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - context.Response.StatusCode = 200; - var reader = new StreamReader(context.Request.Body); - var body = await reader.ReadToEndAsync(); - await context.Response.WriteAsync(body); - }, port, protocols); - } - - public void Dispose() - { - _serviceHandler.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Net.Http; + using Microsoft.AspNetCore.Server.Kestrel.Core; + using TestStack.BDDfy; + using Xunit; + + public class HttpTests : IDisposable + { + private readonly Steps _steps; + private readonly ServiceHandler _serviceHandler; + + public HttpTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_when_using_http_one() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "1.0", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_using_http_one_point_one() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "1.1", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_using_http_two_point_zero() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "2.0", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http2)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + [Fact] + public void should_return_response_502_when_using_http_one_to_talk_to_server_running_http_two() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "1.1", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http2)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.BadGateway)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_using_http_two_to_talk_to_server_running_http_one_point_one() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "https", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + DownstreamHttpVersion = "2.0", + DangerousAcceptAnyServerCertificateValidator = true + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", port, HttpProtocols.Http1)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int port, HttpProtocols protocols) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + context.Response.StatusCode = 200; + var reader = new StreamReader(context.Request.Body); + var body = await reader.ReadToEndAsync(); + await context.Response.WriteAsync(body); + }, port, protocols); + } + + public void Dispose() + { + _serviceHandler.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs index 5ebf04ac3..476fe9742 100644 --- a/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs +++ b/test/Ocelot.AcceptanceTests/LoadBalancerTests.cs @@ -40,9 +40,9 @@ public void should_load_balance_request_with_least_connection() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -89,9 +89,9 @@ public void should_load_balance_request_with_round_robin() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -138,9 +138,9 @@ public void should_load_balance_request_with_custom_load_balancer() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -165,7 +165,7 @@ public void should_load_balance_request_with_custom_load_balancer() GlobalConfiguration = new FileGlobalConfiguration(), }; - Func loadBalancerFactoryFunc = (serviceProvider, reRoute, serviceDiscoveryProvider) => new CustomLoadBalancer(serviceDiscoveryProvider.Get); + Func loadBalancerFactoryFunc = (serviceProvider, route, serviceDiscoveryProvider) => new CustomLoadBalancer(serviceDiscoveryProvider.Get); this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) diff --git a/test/Ocelot.AcceptanceTests/MethodTests.cs b/test/Ocelot.AcceptanceTests/MethodTests.cs index e70dc7e1d..e66c7dbb0 100644 --- a/test/Ocelot.AcceptanceTests/MethodTests.cs +++ b/test/Ocelot.AcceptanceTests/MethodTests.cs @@ -1,164 +1,164 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Net.Http; - using TestStack.BDDfy; - using Xunit; - - public class MethodTests : IDisposable - { - private readonly Steps _steps; - private readonly ServiceHandler _serviceHandler; - - public MethodTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_return_response_200_when_get_converted_to_post() - { - var port = RandomPortFinder.GetRandomPort(); - - 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 = port, - }, - }, - DownstreamHttpMethod = "POST", - }, - }, - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "POST")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_when_get_converted_to_post_with_content() - { - var port = RandomPortFinder.GetRandomPort(); - - 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 = port, - }, - }, - DownstreamHttpMethod = "POST", - }, - }, - }; - - const string expected = "here is some content"; - var httpContent = new StringContent(expected); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "POST")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_when_get_converted_to_get_with_content() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{url}", - DownstreamScheme = "http", - UpstreamPathTemplate = "/{url}", - UpstreamHttpMethod = new List { "Post" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - }, - }, - DownstreamHttpMethod = "GET", - }, - }, - }; - - const string expected = "here is some content"; - var httpContent = new StringContent(expected); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "GET")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIPostUrlOnTheApiGateway("/", httpContent)) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string expected) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - if (context.Request.Method == expected) - { - context.Response.StatusCode = 200; - var reader = new StreamReader(context.Request.Body); - var body = await reader.ReadToEndAsync(); - await context.Response.WriteAsync(body); - } - else - { - context.Response.StatusCode = 500; - } - }); - } - - public void Dispose() - { - _serviceHandler.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Net.Http; + using TestStack.BDDfy; + using Xunit; + + public class MethodTests : IDisposable + { + private readonly Steps _steps; + private readonly ServiceHandler _serviceHandler; + + public MethodTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_when_get_converted_to_post() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + }, + }, + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "POST")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_get_converted_to_post_with_content() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "POST", + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "POST")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_when_get_converted_to_get_with_content() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Post" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + }, + }, + DownstreamHttpMethod = "GET", + }, + }, + }; + + const string expected = "here is some content"; + var httpContent = new StringContent(expected); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", "GET")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIPostUrlOnTheApiGateway("/", httpContent)) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe(expected)) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string expected) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + if (context.Request.Method == expected) + { + context.Response.StatusCode = 200; + var reader = new StreamReader(context.Request.Body); + var body = await reader.ReadToEndAsync(); + await context.Response.WriteAsync(body); + } + else + { + context.Response.StatusCode = 500; + } + }); + } + + public void Dispose() + { + _serviceHandler.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/PollyQoSTests.cs b/test/Ocelot.AcceptanceTests/PollyQoSTests.cs index 07cc72998..07320c707 100644 --- a/test/Ocelot.AcceptanceTests/PollyQoSTests.cs +++ b/test/Ocelot.AcceptanceTests/PollyQoSTests.cs @@ -24,14 +24,14 @@ public PollyQoSTests() [Fact] public void should_not_timeout() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -65,14 +65,14 @@ public void should_not_timeout() [Fact] public void should_timeout() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -106,14 +106,14 @@ public void should_timeout() [Fact] public void should_open_circuit_breaker_then_close() - { - var port = RandomPortFinder.GetRandomPort(); + { + var port = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -157,16 +157,16 @@ public void should_open_circuit_breaker_then_close() } [Fact] - public void open_circuit_should_not_effect_different_reRoute() - { - var port1 = RandomPortFinder.GetRandomPort(); - var port2 = RandomPortFinder.GetRandomPort(); + public void open_circuit_should_not_effect_different_route() + { + var port1 = RandomPortFinder.GetRandomPort(); + var port2 = RandomPortFinder.GetRandomPort(); var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", @@ -187,7 +187,7 @@ public void open_circuit_should_not_effect_different_reRoute() DurationOfBreak = 1000 } }, - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", diff --git a/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs b/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs index 0fcddac26..de914a9c6 100644 --- a/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs +++ b/test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs @@ -1,76 +1,76 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.Http.Features; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using TestStack.BDDfy; - using Xunit; - - public class ReasonPhraseTests : IDisposable - { - private readonly Steps _steps; - private string _contentType; - private long? _contentLength; - private bool _contentTypeHeaderExists; - private readonly ServiceHandler _serviceHandler; - - public ReasonPhraseTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_return_reason_phrase() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", "some reason")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .And(_ => _steps.ThenTheReasonPhraseIs("some reason")) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string reasonPhrase) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - context.Response.HttpContext.Features.Get().ReasonPhrase = reasonPhrase; - - await context.Response.WriteAsync("YOYO!"); - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Features; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using TestStack.BDDfy; + using Xunit; + + public class ReasonPhraseTests : IDisposable + { + private readonly Steps _steps; + private string _contentType; + private long? _contentLength; + private bool _contentTypeHeaderExists; + private readonly ServiceHandler _serviceHandler; + + public ReasonPhraseTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_return_reason_phrase() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", "some reason")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .And(_ => _steps.ThenTheReasonPhraseIs("some reason")) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string reasonPhrase) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + context.Response.HttpContext.Features.Get().ReasonPhrase = reasonPhrase; + + await context.Response.WriteAsync("YOYO!"); + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/RequestIdTests.cs b/test/Ocelot.AcceptanceTests/RequestIdTests.cs index 36c89f9da..cb7e9185e 100644 --- a/test/Ocelot.AcceptanceTests/RequestIdTests.cs +++ b/test/Ocelot.AcceptanceTests/RequestIdTests.cs @@ -26,9 +26,9 @@ public void should_use_default_request_id_and_forward() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -62,9 +62,9 @@ public void should_use_request_id_and_forward() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -99,9 +99,9 @@ public void should_use_global_request_id_and_forward() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List @@ -140,9 +140,9 @@ public void should_use_global_request_id_create_and_forward() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamHostAndPorts = new List diff --git a/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs b/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs index 2eb35c629..c7e55b5ee 100644 --- a/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs +++ b/test/Ocelot.AcceptanceTests/ResponseCodeTests.cs @@ -25,9 +25,9 @@ public void should_return_response_304_when_service_returns_304() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/{everything}", DownstreamScheme = "http", diff --git a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs index 323c9e145..3e60bca22 100644 --- a/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs +++ b/test/Ocelot.AcceptanceTests/ReturnsErrorTests.cs @@ -23,9 +23,9 @@ public void should_return_bad_gateway_error_if_downstream_service_doesnt_respond { var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", UpstreamPathTemplate = "/", @@ -57,9 +57,9 @@ public void should_return_internal_server_error_if_downstream_service_returns_in var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", UpstreamPathTemplate = "/", @@ -92,9 +92,9 @@ public void should_log_warning_if_downstream_service_returns_internal_server_err var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", UpstreamPathTemplate = "/", diff --git a/test/Ocelot.AcceptanceTests/RoutingTests.cs b/test/Ocelot.AcceptanceTests/RoutingTests.cs index 1ce538824..4f1816330 100644 --- a/test/Ocelot.AcceptanceTests/RoutingTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingTests.cs @@ -1,1105 +1,1105 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class RoutingTests : IDisposable - { - private readonly Steps _steps; - private string _downstreamPath; - private readonly ServiceHandler _serviceHandler; - - public RoutingTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_not_match_forward_slash_in_pattern_before_next_forward_slash() - { - var port = RandomPortFinder.GetRandomPort(); - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/v{apiVersion}/cards", - DownstreamScheme = "http", - UpstreamPathTemplate = "/api/v{apiVersion}/cards", - UpstreamHttpMethod = new List { "Get" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - Priority = 1 - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/api/v1/aaaaaaaaa/cards", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/v1/aaaaaaaaa/cards")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) - .BDDfy(); - } - - [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 port = RandomPortFinder.GetRandomPort(); - - 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 = port, - } - } - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", 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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{url}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/{url}", - UpstreamHttpMethod = new List { "Get" }, - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 50810, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/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 port = RandomPortFinder.GetRandomPort(); - 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 = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", 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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - 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:{port}/", "/", 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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{url}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/{url}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/v1/vacancy", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/vacancy/", - UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" } - }, - new FileReRoute - { - DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/vacancy/{vacancyId}", - UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" } - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/products", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/products/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/products", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/products", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/products", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" } - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/{productId}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/{variantId}/products/{productId}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/{variantId}/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/{productId}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/newThing", - UpstreamPathTemplate = "/newThing", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/{urlPath}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/myApp1Name/api/{urlPath}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get", "Post" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "", 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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List(), - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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 port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/v1/vacancy", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/vacancy/", - UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" } - }, - new FileReRoute - { - DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/vacancy/{vacancyId}", - UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" } - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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_not_set_trailing_slash_on_url_template() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/{url}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/platform/{url}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); - - 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 = port, - } - }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/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(); - } - - [Fact] - public void should_match_multiple_paths_with_catch_all() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{everything}", - DownstreamScheme = "http", - UpstreamPathTemplate = "/{everything}", - UpstreamHttpMethod = new List { "Get" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/test/toot", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/test/toot")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_fix_issue_271() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/v1/{everything}", - DownstreamScheme = "http", - UpstreamPathTemplate = "/api/v1/{everything}", - UpstreamHttpMethod = new List { "Get", "Put", "Post" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - }, - new FileReRoute - { - DownstreamPathTemplate = "/connect/token", - DownstreamScheme = "http", - UpstreamPathTemplate = "/connect/token", - UpstreamHttpMethod = new List { "Post" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 5001, - } - }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/api/v1/modules/Test", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/v1/modules/Test")) - .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) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); - } - }); - } - - internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath) - { - _downstreamPath.ShouldBe(expectedDownstreamPath); - } - - public void Dispose() - { - _serviceHandler.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class RoutingTests : IDisposable + { + private readonly Steps _steps; + private string _downstreamPath; + private readonly ServiceHandler _serviceHandler; + + public RoutingTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_not_match_forward_slash_in_pattern_before_next_forward_slash() + { + var port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/v{apiVersion}/cards", + DownstreamScheme = "http", + UpstreamPathTemplate = "/api/v{apiVersion}/cards", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + Priority = 1 + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/api/v1/aaaaaaaaa/cards", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/v1/aaaaaaaaa/cards")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) + .BDDfy(); + } + + [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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + } + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", 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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 50810, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/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 port = RandomPortFinder.GetRandomPort(); + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/", 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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + }, + new FileRoute + { + 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:{port}/", "/", 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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{url}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/{url}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/v1/vacancy", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/vacancy/", + UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" } + }, + new FileRoute + { + DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/vacancy/{vacancyId}", + UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" } + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/products", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/products/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/products", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/products", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/products", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" } + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/{variantId}/products/{productId}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/{variantId}/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/{productId}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/newThing", + UpstreamPathTemplate = "/newThing", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/{urlPath}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/myApp1Name/api/{urlPath}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get", "Post" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "", 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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List(), + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/v1/vacancy", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/vacancy/", + UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" } + }, + new FileRoute + { + DownstreamPathTemplate = "/api/v1/vacancy/{vacancyId}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/vacancy/{vacancyId}", + UpstreamHttpMethod = new List { "Options", "Put", "Get", "Post", "Delete" }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" } + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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_not_set_trailing_slash_on_url_template() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/{url}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/platform/{url}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/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 port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/goods/{url}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/goods/{url}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 53879, + } + }, + Priority = 0 + }, + new FileRoute + { + DownstreamPathTemplate = "/goods/delete", + DownstreamScheme = "http", + UpstreamPathTemplate = "/goods/delete", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/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(); + } + + [Fact] + public void should_match_multiple_paths_with_catch_all() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{everything}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{everything}", + UpstreamHttpMethod = new List { "Get" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/test/toot", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/test/toot")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_fix_issue_271() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/v1/{everything}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/api/v1/{everything}", + UpstreamHttpMethod = new List { "Get", "Put", "Post" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + }, + new FileRoute + { + DownstreamPathTemplate = "/connect/token", + DownstreamScheme = "http", + UpstreamPathTemplate = "/connect/token", + UpstreamHttpMethod = new List { "Post" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 5001, + } + }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}/", "/api/v1/modules/Test", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/v1/modules/Test")) + .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) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); + } + }); + } + + internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath) + { + _downstreamPath.ShouldBe(expectedDownstreamPath); + } + + public void Dispose() + { + _serviceHandler.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs b/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs index c8339381a..268e6d202 100644 --- a/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs +++ b/test/Ocelot.AcceptanceTests/RoutingWithQueryStringTests.cs @@ -1,271 +1,271 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class RoutingWithQueryStringTests : IDisposable - { - private readonly Steps _steps; - private readonly ServiceHandler _serviceHandler; - - public RoutingWithQueryStringTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_return_response_200_with_query_string_template() - { - var subscriptionId = Guid.NewGuid().ToString(); - var unitId = Guid.NewGuid().ToString(); - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/units/{subscriptionId}/{unitId}/updates")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_odata_query_string() - { - var subscriptionId = Guid.NewGuid().ToString(); - var unitId = Guid.NewGuid().ToString(); - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{everything}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/{everything}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/odata/customers", "?$filter=Name%20eq%20'Sam'", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/odata/customers?$filter=Name eq 'Sam' ")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_query_string_upstream_template() - { - var subscriptionId = Guid.NewGuid().ToString(); - var unitId = Guid.NewGuid().ToString(); - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_return_response_404_with_query_string_upstream_template_no_query_string() - { - var subscriptionId = Guid.NewGuid().ToString(); - var unitId = Guid.NewGuid().ToString(); - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) - .BDDfy(); - } - - [Fact] - public void should_return_response_404_with_query_string_upstream_template_different_query_string() - { - var subscriptionId = Guid.NewGuid().ToString(); - var unitId = Guid.NewGuid().ToString(); - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?test=1")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_query_string_upstream_template_multiple_params() - { - var subscriptionId = Guid.NewGuid().ToString(); - var unitId = Guid.NewGuid().ToString(); - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "?productId=1", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}&productId=1")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string queryString, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - if ((context.Request.PathBase.Value != basePath) || context.Request.QueryString.Value != queryString) - { - context.Response.StatusCode = 500; - await context.Response.WriteAsync("downstream path didnt match base path"); - } - else - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - } - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class RoutingWithQueryStringTests : IDisposable + { + private readonly Steps _steps; + private readonly ServiceHandler _serviceHandler; + + public RoutingWithQueryStringTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_with_query_string_template() + { + var subscriptionId = Guid.NewGuid().ToString(); + var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/subscriptions/{subscriptionId}/updates", $"?unitId={unitId}", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/units/{subscriptionId}/{unitId}/updates")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_odata_query_string() + { + var subscriptionId = Guid.NewGuid().ToString(); + var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{everything}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/{everything}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/odata/customers", "?$filter=Name%20eq%20'Sam'", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/odata/customers?$filter=Name eq 'Sam' ")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_query_string_upstream_template() + { + var subscriptionId = Guid.NewGuid().ToString(); + var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_return_response_404_with_query_string_upstream_template_no_query_string() + { + var subscriptionId = Guid.NewGuid().ToString(); + var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) + .BDDfy(); + } + + [Fact] + public void should_return_response_404_with_query_string_upstream_template_different_query_string() + { + var subscriptionId = Guid.NewGuid().ToString(); + var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?test=1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_query_string_upstream_template_multiple_params() + { + var subscriptionId = Guid.NewGuid().ToString(); + var unitId = Guid.NewGuid().ToString(); + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/units/{subscriptionId}/{unitId}/updates", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", $"/api/units/{subscriptionId}/{unitId}/updates", "?productId=1", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway($"/api/subscriptions/{subscriptionId}/updates?unitId={unitId}&productId=1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string queryString, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + if ((context.Request.PathBase.Value != basePath) || context.Request.QueryString.Value != queryString) + { + context.Response.StatusCode = 500; + await context.Response.WriteAsync("downstream path didnt match base path"); + } + else + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + } + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs index b2d8d1d34..329c410ed 100644 --- a/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceDiscoveryTests.cs @@ -1,597 +1,597 @@ -namespace Ocelot.AcceptanceTests -{ - using Configuration.File; - using Consul; - using Microsoft.AspNetCore.Http; - using Newtonsoft.Json; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class ServiceDiscoveryTests : IDisposable - { - private readonly Steps _steps; - private readonly List _consulServices; - private int _counterOne; - private int _counterTwo; - private static readonly object SyncLock = new object(); - private string _downstreamPath; - private string _receivedToken; - private readonly ServiceHandler _serviceHandler; - - public ServiceDiscoveryTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - _consulServices = new List(); - } - - [Fact] - public void should_use_consul_service_discovery_and_load_balance_request() - { - var consulPort = RandomPortFinder.GetRandomPort(); - var servicePort1 = RandomPortFinder.GetRandomPort(); - var servicePort2 = RandomPortFinder.GetRandomPort(); - var serviceName = "product"; - var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; - var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = servicePort1, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = servicePort2, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = serviceName, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) - .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithConsul()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 50)) - .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50)) - .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26)) - .BDDfy(); - } - - [Fact] - public void should_handle_request_to_consul_for_downstream_service_and_make_request() - { - int consulPort = RandomPortFinder.GetRandomPort(); - int servicePort = RandomPortFinder.GetRandomPort(); - const string serviceName = "web"; - string downstreamServiceOneUrl = $"http://localhost:{servicePort}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = servicePort, - ID = "web_90_0_2_224_8080", - Tags = new[] { "version-v1" } - }, - }; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/home", - DownstreamScheme = "http", - UpstreamPathTemplate = "/home", - UpstreamHttpMethod = new List { "Get", "Options" }, - ServiceName = serviceName, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithConsul()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/home")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes() - { - int consulPort = RandomPortFinder.GetRandomPort(); - const string serviceName = "web"; - int downstreamServicePort = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = downstreamServicePort, - ID = "web_90_0_2_224_8080", - Tags = new[] { "version-v1" } - }, - }; - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "http", - Host = "localhost", - Port = consulPort - }, - DownstreamScheme = "http", - HttpHandlerOptions = new FileHttpHandlerOptions - { - AllowAutoRedirect = true, - UseCookieContainer = true, - UseTracing = false - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 200, "Hello from Laura")) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithConsul()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/web/something")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_use_consul_service_discovery_and_load_balance_request_no_re_routes() - { - var consulPort = RandomPortFinder.GetRandomPort(); - var serviceName = "product"; - var serviceOnePort = RandomPortFinder.GetRandomPort(); - var serviceTwoPort = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{serviceOnePort}"; - var downstreamServiceTwoUrl = $"http://localhost:{serviceTwoPort}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = serviceOnePort, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = serviceTwoPort, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - }, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, - DownstreamScheme = "http" - } - }; - - this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) - .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithConsul()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes($"/{serviceName}/", 50)) - .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50)) - .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26)) - .BDDfy(); - } - - [Fact] - public void should_use_token_to_make_request_to_consul() - { - var token = "abctoken"; - var consulPort = RandomPortFinder.GetRandomPort(); - var serviceName = "web"; - var servicePort = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{servicePort}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = servicePort, - ID = "web_90_0_2_224_8080", - Tags = new[] { "version-v1" } - }, - }; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/home", - DownstreamScheme = "http", - UpstreamPathTemplate = "/home", - UpstreamHttpMethod = new List { "Get", "Options" }, - ServiceName = serviceName, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort, - Token = token - } - } - }; - - this.Given(_ => GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) - .And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .And(_ => _steps.GivenThereIsAConfiguration(configuration)) - .And(_ => _steps.GivenOcelotIsRunningWithConsul()) - .When(_ => _steps.WhenIGetUrlOnTheApiGateway("/home")) - .Then(_ => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(_ => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .And(_ => _receivedToken.ShouldBe(token)) - .BDDfy(); - } - - [Fact] - public void should_send_request_to_service_after_it_becomes_available_in_consul() - { - var consulPort = RandomPortFinder.GetRandomPort(); - var serviceName = "product"; - var servicePort1 = RandomPortFinder.GetRandomPort(); - var servicePort2 = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; - var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = servicePort1, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - var serviceEntryTwo = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = servicePort2, - ID = Guid.NewGuid().ToString(), - Tags = new string[0] - }, - }; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = serviceName, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) - .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithConsul()) - .And(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10)) - .And(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(10)) - .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(4, 6)) - .And(x => WhenIRemoveAService(serviceEntryTwo)) - .And(x => GivenIResetCounters()) - .And(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10)) - .And(x => ThenOnlyOneServiceHasBeenCalled()) - .And(x => WhenIAddAServiceBackIn(serviceEntryTwo)) - .And(x => GivenIResetCounters()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10)) - .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(10)) - .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(4, 6)) - .BDDfy(); - } - - [Fact] - public void should_handle_request_to_poll_consul_for_downstream_service_and_make_request() - { - int consulPort = RandomPortFinder.GetRandomPort(); - const string serviceName = "web"; - int downstreamServicePort = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - var serviceEntryOne = new ServiceEntry() - { - Service = new AgentService() - { - Service = serviceName, - Address = "localhost", - Port = downstreamServicePort, - ID = $"web_90_0_2_224_{downstreamServicePort}", - Tags = new[] { "version-v1" } - }, - }; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/home", - DownstreamScheme = "http", - UpstreamPathTemplate = "/home", - UpstreamHttpMethod = new List { "Get", "Options" }, - ServiceName = serviceName, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "http", - Host = "localhost", - Port = consulPort, - Type = "PollConsul", - PollingInterval = 0, - Namespace = string.Empty - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) - .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithConsul()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk("/home")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - private void WhenIAddAServiceBackIn(ServiceEntry serviceEntryTwo) - { - _consulServices.Add(serviceEntryTwo); - } - - private void ThenOnlyOneServiceHasBeenCalled() - { - _counterOne.ShouldBe(10); - _counterTwo.ShouldBe(0); - } - - private void WhenIRemoveAService(ServiceEntry serviceEntryTwo) - { - _consulServices.Remove(serviceEntryTwo); - } - - private void GivenIResetCounters() - { - _counterOne = 0; - _counterTwo = 0; - } - - private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top) - { - _counterOne.ShouldBeInRange(bottom, top); - _counterOne.ShouldBeInRange(bottom, top); - } - - private void ThenTheTwoServicesShouldHaveBeenCalledTimes(int expected) - { - var total = _counterOne + _counterTwo; - total.ShouldBe(expected); - } - - private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) - { - foreach (var serviceEntry in serviceEntries) - { - _consulServices.Add(serviceEntry); - } - } - - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") - { - if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values)) - { - _receivedToken = values.First(); - } - var json = JsonConvert.SerializeObject(_consulServices); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - } - - private void GivenProductServiceOneIsRunning(string url, int statusCode) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - try - { - string response; - lock (SyncLock) - { - _counterOne++; - response = _counterOne.ToString(); - } - - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(response); - } - catch (Exception exception) - { - await context.Response.WriteAsync(exception.StackTrace); - } - }); - } - - private void GivenProductServiceTwoIsRunning(string url, int statusCode) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - try - { - string response; - lock (SyncLock) - { - _counterTwo++; - response = _counterTwo.ToString(); - } - - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(response); - } - catch (Exception exception) - { - await context.Response.WriteAsync(exception.StackTrace); - } - }); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); - } - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Configuration.File; + using Consul; + using Microsoft.AspNetCore.Http; + using Newtonsoft.Json; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class ServiceDiscoveryTests : IDisposable + { + private readonly Steps _steps; + private readonly List _consulServices; + private int _counterOne; + private int _counterTwo; + private static readonly object SyncLock = new object(); + private string _downstreamPath; + private string _receivedToken; + private readonly ServiceHandler _serviceHandler; + + public ServiceDiscoveryTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + _consulServices = new List(); + } + + [Fact] + public void should_use_consul_service_discovery_and_load_balance_request() + { + var consulPort = RandomPortFinder.GetRandomPort(); + var servicePort1 = RandomPortFinder.GetRandomPort(); + var servicePort2 = RandomPortFinder.GetRandomPort(); + var serviceName = "product"; + var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; + var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = servicePort1, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = servicePort2, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = serviceName, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithConsul()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 50)) + .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50)) + .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26)) + .BDDfy(); + } + + [Fact] + public void should_handle_request_to_consul_for_downstream_service_and_make_request() + { + int consulPort = RandomPortFinder.GetRandomPort(); + int servicePort = RandomPortFinder.GetRandomPort(); + const string serviceName = "web"; + string downstreamServiceOneUrl = $"http://localhost:{servicePort}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = servicePort, + ID = "web_90_0_2_224_8080", + Tags = new[] { "version-v1" } + }, + }; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/home", + DownstreamScheme = "http", + UpstreamPathTemplate = "/home", + UpstreamHttpMethod = new List { "Get", "Options" }, + ServiceName = serviceName, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithConsul()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/home")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_handle_request_to_consul_for_downstream_service_and_make_request_no_re_routes() + { + int consulPort = RandomPortFinder.GetRandomPort(); + const string serviceName = "web"; + int downstreamServicePort = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = downstreamServicePort, + ID = "web_90_0_2_224_8080", + Tags = new[] { "version-v1" } + }, + }; + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "http", + Host = "localhost", + Port = consulPort + }, + DownstreamScheme = "http", + HttpHandlerOptions = new FileHttpHandlerOptions + { + AllowAutoRedirect = true, + UseCookieContainer = true, + UseTracing = false + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/something", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithConsul()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/web/something")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_use_consul_service_discovery_and_load_balance_request_no_re_routes() + { + var consulPort = RandomPortFinder.GetRandomPort(); + var serviceName = "product"; + var serviceOnePort = RandomPortFinder.GetRandomPort(); + var serviceTwoPort = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{serviceOnePort}"; + var downstreamServiceTwoUrl = $"http://localhost:{serviceTwoPort}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = serviceOnePort, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = serviceTwoPort, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + DownstreamScheme = "http" + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithConsul()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes($"/{serviceName}/", 50)) + .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(50)) + .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(24, 26)) + .BDDfy(); + } + + [Fact] + public void should_use_token_to_make_request_to_consul() + { + var token = "abctoken"; + var consulPort = RandomPortFinder.GetRandomPort(); + var serviceName = "web"; + var servicePort = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{servicePort}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = servicePort, + ID = "web_90_0_2_224_8080", + Tags = new[] { "version-v1" } + }, + }; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/home", + DownstreamScheme = "http", + UpstreamPathTemplate = "/home", + UpstreamHttpMethod = new List { "Get", "Options" }, + ServiceName = serviceName, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort, + Token = token + } + } + }; + + this.Given(_ => GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) + .And(_ => GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(_ => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .And(_ => _steps.GivenThereIsAConfiguration(configuration)) + .And(_ => _steps.GivenOcelotIsRunningWithConsul()) + .When(_ => _steps.WhenIGetUrlOnTheApiGateway("/home")) + .Then(_ => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(_ => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .And(_ => _receivedToken.ShouldBe(token)) + .BDDfy(); + } + + [Fact] + public void should_send_request_to_service_after_it_becomes_available_in_consul() + { + var consulPort = RandomPortFinder.GetRandomPort(); + var serviceName = "product"; + var servicePort1 = RandomPortFinder.GetRandomPort(); + var servicePort2 = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; + var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = servicePort1, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + var serviceEntryTwo = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = servicePort2, + ID = Guid.NewGuid().ToString(), + Tags = new string[0] + }, + }; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = serviceName, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithConsul()) + .And(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10)) + .And(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(10)) + .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(4, 6)) + .And(x => WhenIRemoveAService(serviceEntryTwo)) + .And(x => GivenIResetCounters()) + .And(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10)) + .And(x => ThenOnlyOneServiceHasBeenCalled()) + .And(x => WhenIAddAServiceBackIn(serviceEntryTwo)) + .And(x => GivenIResetCounters()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10)) + .Then(x => x.ThenTheTwoServicesShouldHaveBeenCalledTimes(10)) + .And(x => x.ThenBothServicesCalledRealisticAmountOfTimes(4, 6)) + .BDDfy(); + } + + [Fact] + public void should_handle_request_to_poll_consul_for_downstream_service_and_make_request() + { + int consulPort = RandomPortFinder.GetRandomPort(); + const string serviceName = "web"; + int downstreamServicePort = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{downstreamServicePort}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + var serviceEntryOne = new ServiceEntry() + { + Service = new AgentService() + { + Service = serviceName, + Address = "localhost", + Port = downstreamServicePort, + ID = $"web_90_0_2_224_{downstreamServicePort}", + Tags = new[] { "version-v1" } + }, + }; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/home", + DownstreamScheme = "http", + UpstreamPathTemplate = "/home", + UpstreamHttpMethod = new List { "Get", "Options" }, + ServiceName = serviceName, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "LeastConnection" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "http", + Host = "localhost", + Port = consulPort, + Type = "PollConsul", + PollingInterval = 0, + Namespace = string.Empty + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn(downstreamServiceOneUrl, "/api/home", 200, "Hello from Laura")) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl, serviceName)) + .And(x => x.GivenTheServicesAreRegisteredWithConsul(serviceEntryOne)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithConsul()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayWaitingForTheResponseToBeOk("/home")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + private void WhenIAddAServiceBackIn(ServiceEntry serviceEntryTwo) + { + _consulServices.Add(serviceEntryTwo); + } + + private void ThenOnlyOneServiceHasBeenCalled() + { + _counterOne.ShouldBe(10); + _counterTwo.ShouldBe(0); + } + + private void WhenIRemoveAService(ServiceEntry serviceEntryTwo) + { + _consulServices.Remove(serviceEntryTwo); + } + + private void GivenIResetCounters() + { + _counterOne = 0; + _counterTwo = 0; + } + + private void ThenBothServicesCalledRealisticAmountOfTimes(int bottom, int top) + { + _counterOne.ShouldBeInRange(bottom, top); + _counterOne.ShouldBeInRange(bottom, top); + } + + private void ThenTheTwoServicesShouldHaveBeenCalledTimes(int expected) + { + var total = _counterOne + _counterTwo; + total.ShouldBe(expected); + } + + private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries) + { + foreach (var serviceEntry in serviceEntries) + { + _consulServices.Add(serviceEntry); + } + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + if (context.Request.Path.Value == $"/v1/health/service/{serviceName}") + { + if (context.Request.Headers.TryGetValue("X-Consul-Token", out var values)) + { + _receivedToken = values.First(); + } + var json = JsonConvert.SerializeObject(_consulServices); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + } + + private void GivenProductServiceOneIsRunning(string url, int statusCode) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + try + { + string response; + lock (SyncLock) + { + _counterOne++; + response = _counterOne.ToString(); + } + + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(response); + } + catch (Exception exception) + { + await context.Response.WriteAsync(exception.StackTrace); + } + }); + } + + private void GivenProductServiceTwoIsRunning(string url, int statusCode) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + try + { + string response; + lock (SyncLock) + { + _counterTwo++; + response = _counterTwo.ToString(); + } + + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(response); + } + catch (Exception exception) + { + await context.Response.WriteAsync(exception.StackTrace); + } + }); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); + } + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs b/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs index 03643a7f1..6c132091a 100644 --- a/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs +++ b/test/Ocelot.AcceptanceTests/ServiceFabricTests.cs @@ -1,208 +1,208 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class ServiceFabricTests : IDisposable - { - private readonly Steps _steps; - private string _downstreamPath; - private readonly ServiceHandler _serviceHandler; - - public ServiceFabricTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_fix_issue_555() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/{everything}", - DownstreamScheme = "http", - UpstreamPathTemplate = "/{everything}", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "OcelotServiceApplication/OcelotApplicationService" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = port, - Type = "ServiceFabric" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/a", 200, "Hello from Laura", "b=c")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/a?b=c")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_support_service_fabric_naming_and_dns_service_stateless_and_guest() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/values", - DownstreamScheme = "http", - UpstreamPathTemplate = "/EquipmentInterfaces", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "OcelotServiceApplication/OcelotApplicationService" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = port, - Type = "ServiceFabric" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "test=best")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces?test=best")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_support_service_fabric_naming_and_dns_service_statefull_and_actors() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/values", - DownstreamScheme = "http", - UpstreamPathTemplate = "/EquipmentInterfaces", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "OcelotServiceApplication/OcelotApplicationService" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = port, - Type = "ServiceFabric" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "PartitionKind=test&PartitionKey=1")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces?PartitionKind=test&PartitionKey=1")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); - } - - [Fact] - public void should_support_placeholder_in_service_fabric_service_name() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/values", - DownstreamScheme = "http", - UpstreamPathTemplate = "/api/{version}/values", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "Service_{version}/Api" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Host = "localhost", - Port = port, - Type = "ServiceFabric" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/Service_1.0/Api/values", 200, "Hello from Laura", "test=best")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/1.0/values?test=best")) - .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, string expectedQueryString) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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 - { - if (context.Request.QueryString.Value.Contains(expectedQueryString)) - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - } - else - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync("downstream path didnt match base path"); - } - } - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class ServiceFabricTests : IDisposable + { + private readonly Steps _steps; + private string _downstreamPath; + private readonly ServiceHandler _serviceHandler; + + public ServiceFabricTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_fix_issue_555() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/{everything}", + DownstreamScheme = "http", + UpstreamPathTemplate = "/{everything}", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "OcelotServiceApplication/OcelotApplicationService" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = port, + Type = "ServiceFabric" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/a", 200, "Hello from Laura", "b=c")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/a?b=c")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_support_service_fabric_naming_and_dns_service_stateless_and_guest() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/values", + DownstreamScheme = "http", + UpstreamPathTemplate = "/EquipmentInterfaces", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "OcelotServiceApplication/OcelotApplicationService" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = port, + Type = "ServiceFabric" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "test=best")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces?test=best")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_support_service_fabric_naming_and_dns_service_statefull_and_actors() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/values", + DownstreamScheme = "http", + UpstreamPathTemplate = "/EquipmentInterfaces", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "OcelotServiceApplication/OcelotApplicationService" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = port, + Type = "ServiceFabric" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/OcelotServiceApplication/OcelotApplicationService/api/values", 200, "Hello from Laura", "PartitionKind=test&PartitionKey=1")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/EquipmentInterfaces?PartitionKind=test&PartitionKey=1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + + [Fact] + public void should_support_placeholder_in_service_fabric_service_name() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/values", + DownstreamScheme = "http", + UpstreamPathTemplate = "/api/{version}/values", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "Service_{version}/Api" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Host = "localhost", + Port = port, + Type = "ServiceFabric" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/Service_1.0/Api/values", 200, "Hello from Laura", "test=best")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/1.0/values?test=best")) + .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, string expectedQueryString) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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 + { + if (context.Request.QueryString.Value.Contains(expectedQueryString)) + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + } + else + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync("downstream path didnt match base path"); + } + } + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/SslTests.cs b/test/Ocelot.AcceptanceTests/SslTests.cs index e270a0f22..4301cc611 100644 --- a/test/Ocelot.AcceptanceTests/SslTests.cs +++ b/test/Ocelot.AcceptanceTests/SslTests.cs @@ -27,9 +27,9 @@ public void should_dangerous_accept_any_server_certificate_validator() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "https", @@ -64,9 +64,9 @@ public void should_not_dangerous_accept_any_server_certificate_validator() var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "https", diff --git a/test/Ocelot.AcceptanceTests/StartupTests.cs b/test/Ocelot.AcceptanceTests/StartupTests.cs index 5fc2ea0ee..8934ad6e5 100644 --- a/test/Ocelot.AcceptanceTests/StartupTests.cs +++ b/test/Ocelot.AcceptanceTests/StartupTests.cs @@ -1,102 +1,102 @@ -namespace Ocelot.AcceptanceTests -{ - using Configuration.Repository; - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using Responses; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class StartupTests : IDisposable - { - private readonly Steps _steps; - private readonly ServiceHandler _serviceHandler; - private string _downstreamPath; - - public StartupTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_not_try_and_write_to_disk_on_startup_when_not_using_admin_api() - { - var port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - } - } - }; - - var fakeRepo = new FakeFileConfigurationRepository(); - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunningWithBlowingUpDiskRepo(fakeRepo)) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); - } - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - - private class FakeFileConfigurationRepository : IFileConfigurationRepository - { - public Task> Get() - { - throw new NotImplementedException(); - } - - public Task Set(FileConfiguration fileConfiguration) - { - throw new NotImplementedException(); - } - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Configuration.Repository; + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using Responses; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class StartupTests : IDisposable + { + private readonly Steps _steps; + private readonly ServiceHandler _serviceHandler; + private string _downstreamPath; + + public StartupTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_not_try_and_write_to_disk_on_startup_when_not_using_admin_api() + { + var port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + var fakeRepo = new FakeFileConfigurationRepository(); + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunningWithBlowingUpDiskRepo(fakeRepo)) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); + } + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + + private class FakeFileConfigurationRepository : IFileConfigurationRepository + { + public Task> Get() + { + throw new NotImplementedException(); + } + + public Task Set(FileConfiguration fileConfiguration) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 2d22bc5aa..de2556b28 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -261,7 +261,7 @@ public void GivenOcelotIsRunning() /// /// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. /// - public void GivenOcelotIsRunningWithCustomLoadBalancer(Func loadBalancerFactoryFunc) + public void GivenOcelotIsRunningWithCustomLoadBalancer(Func loadBalancerFactoryFunc) where T : ILoadBalancer { _webHostBuilder = new WebHostBuilder(); diff --git a/test/Ocelot.AcceptanceTests/StickySessionsTests.cs b/test/Ocelot.AcceptanceTests/StickySessionsTests.cs index c4073d87e..92ad8f305 100644 --- a/test/Ocelot.AcceptanceTests/StickySessionsTests.cs +++ b/test/Ocelot.AcceptanceTests/StickySessionsTests.cs @@ -1,293 +1,293 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using Shouldly; - using System; - using System.Collections.Generic; - using TestStack.BDDfy; - using Xunit; - - public class StickySessionsTests : IDisposable - { - private readonly Steps _steps; - private int _counterOne; - private int _counterTwo; - private static readonly object SyncLock = new object(); - private readonly ServiceHandler _serviceHandler; - - public StickySessionsTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_use_same_downstream_host() - { - var downstreamPortOne = RandomPortFinder.GetRandomPort(); - var downstreamPortTwo = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; - var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - LoadBalancerOptions = new FileLoadBalancerOptions - { - Type = "CookieStickySessions", - Key = "sessionid", - Expiry = 300000 - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortOne - }, - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortTwo - } - } - } - } - }; - - this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) - .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10, "sessionid", "123")) - .Then(x => x.ThenTheFirstServiceIsCalled(10)) - .Then(x => x.ThenTheSecondServiceIsCalled(0)) - .BDDfy(); - } - - [Fact] - public void should_use_different_downstream_host_for_different_re_route() - { - var downstreamPortOne = RandomPortFinder.GetRandomPort(); - var downstreamPortTwo = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; - var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - LoadBalancerOptions = new FileLoadBalancerOptions - { - Type = "CookieStickySessions", - Key = "sessionid", - Expiry = 300000 - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortOne - }, - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortTwo - } - } - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/test", - UpstreamHttpMethod = new List { "Get" }, - LoadBalancerOptions = new FileLoadBalancerOptions - { - Type = "CookieStickySessions", - Key = "bestid", - Expiry = 300000 - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortTwo - }, - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortOne - } - } - } - } - }; - - this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) - .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", "sessionid", "123")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/test", "bestid", "123")) - .Then(x => x.ThenTheFirstServiceIsCalled(1)) - .Then(x => x.ThenTheSecondServiceIsCalled(1)) - .BDDfy(); - } - - [Fact] - public void should_use_same_downstream_host_for_different_re_route() - { - var downstreamPortOne = RandomPortFinder.GetRandomPort(); - var downstreamPortTwo = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; - var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - LoadBalancerOptions = new FileLoadBalancerOptions - { - Type = "CookieStickySessions", - Key = "sessionid", - Expiry = 300000 - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortOne - }, - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortTwo - } - } - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/test", - UpstreamHttpMethod = new List { "Get" }, - LoadBalancerOptions = new FileLoadBalancerOptions - { - Type = "CookieStickySessions", - Key = "sessionid", - Expiry = 300000 - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortTwo - }, - new FileHostAndPort - { - Host = "localhost", - Port = downstreamPortOne - } - } - } - } - }; - - this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) - .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", "sessionid", "123")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/test", "sessionid", "123")) - .Then(x => x.ThenTheFirstServiceIsCalled(2)) - .Then(x => x.ThenTheSecondServiceIsCalled(0)) - .BDDfy(); - } - - private void ThenTheFirstServiceIsCalled(int expected) - { - _counterOne.ShouldBe(expected); - } - - private void ThenTheSecondServiceIsCalled(int expected) - { - _counterTwo.ShouldBe(expected); - } - - private void GivenProductServiceOneIsRunning(string url, int statusCode) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - try - { - var response = string.Empty; - lock (SyncLock) - { - _counterOne++; - response = _counterOne.ToString(); - } - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(response); - } - catch (Exception exception) - { - await context.Response.WriteAsync(exception.StackTrace); - } - }); - } - - private void GivenProductServiceTwoIsRunning(string url, int statusCode) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - try - { - var response = string.Empty; - lock (SyncLock) - { - _counterTwo++; - response = _counterTwo.ToString(); - } - - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(response); - } - catch (Exception exception) - { - await context.Response.WriteAsync(exception.StackTrace); - } - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using TestStack.BDDfy; + using Xunit; + + public class StickySessionsTests : IDisposable + { + private readonly Steps _steps; + private int _counterOne; + private int _counterTwo; + private static readonly object SyncLock = new object(); + private readonly ServiceHandler _serviceHandler; + + public StickySessionsTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_use_same_downstream_host() + { + var downstreamPortOne = RandomPortFinder.GetRandomPort(); + var downstreamPortTwo = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; + var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + LoadBalancerOptions = new FileLoadBalancerOptions + { + Type = "CookieStickySessions", + Key = "sessionid", + Expiry = 300000 + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortOne + }, + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortTwo + } + } + } + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGatewayMultipleTimes("/", 10, "sessionid", "123")) + .Then(x => x.ThenTheFirstServiceIsCalled(10)) + .Then(x => x.ThenTheSecondServiceIsCalled(0)) + .BDDfy(); + } + + [Fact] + public void should_use_different_downstream_host_for_different_re_route() + { + var downstreamPortOne = RandomPortFinder.GetRandomPort(); + var downstreamPortTwo = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; + var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + LoadBalancerOptions = new FileLoadBalancerOptions + { + Type = "CookieStickySessions", + Key = "sessionid", + Expiry = 300000 + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortOne + }, + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortTwo + } + } + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/test", + UpstreamHttpMethod = new List { "Get" }, + LoadBalancerOptions = new FileLoadBalancerOptions + { + Type = "CookieStickySessions", + Key = "bestid", + Expiry = 300000 + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortTwo + }, + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortOne + } + } + } + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", "sessionid", "123")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/test", "bestid", "123")) + .Then(x => x.ThenTheFirstServiceIsCalled(1)) + .Then(x => x.ThenTheSecondServiceIsCalled(1)) + .BDDfy(); + } + + [Fact] + public void should_use_same_downstream_host_for_different_re_route() + { + var downstreamPortOne = RandomPortFinder.GetRandomPort(); + var downstreamPortTwo = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{downstreamPortOne}"; + var downstreamServiceTwoUrl = $"http://localhost:{downstreamPortTwo}"; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + LoadBalancerOptions = new FileLoadBalancerOptions + { + Type = "CookieStickySessions", + Key = "sessionid", + Expiry = 300000 + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortOne + }, + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortTwo + } + } + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/test", + UpstreamHttpMethod = new List { "Get" }, + LoadBalancerOptions = new FileLoadBalancerOptions + { + Type = "CookieStickySessions", + Key = "sessionid", + Expiry = 300000 + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortTwo + }, + new FileHostAndPort + { + Host = "localhost", + Port = downstreamPortOne + } + } + } + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, 200)) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, 200)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/", "sessionid", "123")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/test", "sessionid", "123")) + .Then(x => x.ThenTheFirstServiceIsCalled(2)) + .Then(x => x.ThenTheSecondServiceIsCalled(0)) + .BDDfy(); + } + + private void ThenTheFirstServiceIsCalled(int expected) + { + _counterOne.ShouldBe(expected); + } + + private void ThenTheSecondServiceIsCalled(int expected) + { + _counterTwo.ShouldBe(expected); + } + + private void GivenProductServiceOneIsRunning(string url, int statusCode) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + try + { + var response = string.Empty; + lock (SyncLock) + { + _counterOne++; + response = _counterOne.ToString(); + } + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(response); + } + catch (Exception exception) + { + await context.Response.WriteAsync(exception.StackTrace); + } + }); + } + + private void GivenProductServiceTwoIsRunning(string url, int statusCode) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + try + { + var response = string.Empty; + lock (SyncLock) + { + _counterTwo++; + response = _counterTwo.ToString(); + } + + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(response); + } + catch (Exception exception) + { + await context.Response.WriteAsync(exception.StackTrace); + } + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/TwoDownstreamServicesTests.cs b/test/Ocelot.AcceptanceTests/TwoDownstreamServicesTests.cs index e26fff9fe..18cc53eb3 100644 --- a/test/Ocelot.AcceptanceTests/TwoDownstreamServicesTests.cs +++ b/test/Ocelot.AcceptanceTests/TwoDownstreamServicesTests.cs @@ -1,157 +1,157 @@ -namespace Ocelot.AcceptanceTests -{ - using Configuration.File; - using Consul; - using Microsoft.AspNetCore.Http; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class TwoDownstreamServicesTests : IDisposable - { - private readonly Steps _steps; - private readonly List _serviceEntries; - private string _downstreamPathOne; - private string _downstreamPathTwo; - private readonly ServiceHandler _serviceHandler; - - public TwoDownstreamServicesTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - _serviceEntries = new List(); - } - - [Fact] - public void should_fix_issue_194() - { - var consulPort = RandomPortFinder.GetRandomPort(); - var servicePort1 = RandomPortFinder.GetRandomPort(); - var servicePort2 = RandomPortFinder.GetRandomPort(); - var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; - var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; - var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/user/{user}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = servicePort1, - } - }, - UpstreamPathTemplate = "/api/user/{user}", - UpstreamHttpMethod = new List { "Get" }, - }, - new FileReRoute - { - DownstreamPathTemplate = "/api/product/{product}", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = servicePort2, - } - }, - UpstreamPathTemplate = "/api/product/{product}", - UpstreamHttpMethod = new List { "Get" }, - } - }, - GlobalConfiguration = new FileGlobalConfiguration() - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() - { - Scheme = "https", - Host = "localhost", - Port = consulPort - } - } - }; - - this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, "/api/user/info", 200, "user")) - .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, "/api/product/info", 200, "product")) - .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/user/info?id=1")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("user")) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/product/info?id=1")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("product")) - .BDDfy(); - } - - private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url) - { - _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => - { - if (context.Request.Path.Value == "/v1/health/service/product") - { - var json = JsonConvert.SerializeObject(_serviceEntries); - context.Response.Headers.Add("Content-Type", "application/json"); - await context.Response.WriteAsync(json); - } - }); - } - - private void GivenProductServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) - ? context.Request.PathBase.Value - : context.Request.Path.Value; - - if (_downstreamPathOne != 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); - } - }); - } - - private void GivenProductServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => - { - _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; - - if (_downstreamPathTwo != 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); - } - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Configuration.File; + using Consul; + using Microsoft.AspNetCore.Http; + using Newtonsoft.Json; + using System; + using System.Collections.Generic; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class TwoDownstreamServicesTests : IDisposable + { + private readonly Steps _steps; + private readonly List _serviceEntries; + private string _downstreamPathOne; + private string _downstreamPathTwo; + private readonly ServiceHandler _serviceHandler; + + public TwoDownstreamServicesTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + _serviceEntries = new List(); + } + + [Fact] + public void should_fix_issue_194() + { + var consulPort = RandomPortFinder.GetRandomPort(); + var servicePort1 = RandomPortFinder.GetRandomPort(); + var servicePort2 = RandomPortFinder.GetRandomPort(); + var downstreamServiceOneUrl = $"http://localhost:{servicePort1}"; + var downstreamServiceTwoUrl = $"http://localhost:{servicePort2}"; + var fakeConsulServiceDiscoveryUrl = $"http://localhost:{consulPort}"; + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/user/{user}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = servicePort1, + } + }, + UpstreamPathTemplate = "/api/user/{user}", + UpstreamHttpMethod = new List { "Get" }, + }, + new FileRoute + { + DownstreamPathTemplate = "/api/product/{product}", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = servicePort2, + } + }, + UpstreamPathTemplate = "/api/product/{product}", + UpstreamHttpMethod = new List { "Get" }, + } + }, + GlobalConfiguration = new FileGlobalConfiguration() + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider() + { + Scheme = "https", + Host = "localhost", + Port = consulPort + } + } + }; + + this.Given(x => x.GivenProductServiceOneIsRunning(downstreamServiceOneUrl, "/api/user/info", 200, "user")) + .And(x => x.GivenProductServiceTwoIsRunning(downstreamServiceTwoUrl, "/api/product/info", 200, "product")) + .And(x => x.GivenThereIsAFakeConsulServiceDiscoveryProvider(fakeConsulServiceDiscoveryUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/user/info?id=1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("user")) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/api/product/info?id=1")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("product")) + .BDDfy(); + } + + private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url) + { + _serviceHandler.GivenThereIsAServiceRunningOn(url, async context => + { + if (context.Request.Path.Value == "/v1/health/service/product") + { + var json = JsonConvert.SerializeObject(_serviceEntries); + context.Response.Headers.Add("Content-Type", "application/json"); + await context.Response.WriteAsync(json); + } + }); + } + + private void GivenProductServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + _downstreamPathOne = !string.IsNullOrEmpty(context.Request.PathBase.Value) + ? context.Request.PathBase.Value + : context.Request.Path.Value; + + if (_downstreamPathOne != 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); + } + }); + } + + private void GivenProductServiceTwoIsRunning(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context => + { + _downstreamPathTwo = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value; + + if (_downstreamPathTwo != 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); + } + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs b/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs index ebfcf8058..276bf0f5d 100644 --- a/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs +++ b/test/Ocelot.AcceptanceTests/UpstreamHostTests.cs @@ -1,279 +1,279 @@ -namespace Ocelot.AcceptanceTests -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Configuration.File; - using System; - using System.Collections.Generic; - using System.Net; - using TestStack.BDDfy; - using Xunit; - - public class UpstreamHostTests : IDisposable - { - private readonly Steps _steps; - private string _downstreamPath; - private readonly ServiceHandler _serviceHandler; - - public UpstreamHostTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - } - - [Fact] - public void should_return_response_200_with_simple_url_and_hosts_match() - { - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - UpstreamHost = "localhost" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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_and_hosts_match_multiple_re_routes() - { - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - UpstreamHost = "localhost" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 50000, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - UpstreamHost = "DONTMATCH" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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_and_hosts_match_multiple_re_routes_reversed() - { - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 50000, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - UpstreamHost = "DONTMATCH" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - UpstreamHost = "localhost" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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_and_hosts_match_multiple_re_routes_reversed_with_no_host_first() - { - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 50000, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - UpstreamHost = "localhost" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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_404_with_simple_url_and_hosts_dont_match() - { - int port = RandomPortFinder.GetRandomPort(); - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = port, - } - }, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - UpstreamHost = "127.0.0.20:5000" - } - } - }; - - this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) - .BDDfy(); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) - { - _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); - } - }); - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Configuration.File; + using System; + using System.Collections.Generic; + using System.Net; + using TestStack.BDDfy; + using Xunit; + + public class UpstreamHostTests : IDisposable + { + private readonly Steps _steps; + private string _downstreamPath; + private readonly ServiceHandler _serviceHandler; + + public UpstreamHostTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + } + + [Fact] + public void should_return_response_200_with_simple_url_and_hosts_match() + { + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + UpstreamHost = "localhost" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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_and_hosts_match_multiple_re_routes() + { + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + UpstreamHost = "localhost" + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 50000, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + UpstreamHost = "DONTMATCH" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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_and_hosts_match_multiple_re_routes_reversed() + { + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 50000, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + UpstreamHost = "DONTMATCH" + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + UpstreamHost = "localhost" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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_and_hosts_match_multiple_re_routes_reversed_with_no_host_first() + { + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 50000, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + UpstreamHost = "localhost" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 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_404_with_simple_url_and_hosts_dont_match() + { + int port = RandomPortFinder.GetRandomPort(); + + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = port, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + UpstreamHost = "127.0.0.20:5000" + } + } + }; + + this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", "/", 200, "Hello from Laura")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.NotFound)) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) + { + _serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, 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); + } + }); + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.AcceptanceTests/WebSocketTests.cs b/test/Ocelot.AcceptanceTests/WebSocketTests.cs index a8a391669..048484b6a 100644 --- a/test/Ocelot.AcceptanceTests/WebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/WebSocketTests.cs @@ -1,334 +1,334 @@ -namespace Ocelot.AcceptanceTests -{ - using Ocelot.Configuration.File; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Net.WebSockets; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class WebSocketTests : IDisposable - { - private readonly List _secondRecieved; - private readonly List _firstRecieved; - private readonly Steps _steps; - private readonly ServiceHandler _serviceHandler; - - public WebSocketTests() - { - _serviceHandler = new ServiceHandler(); - _steps = new Steps(); - _firstRecieved = new List(); - _secondRecieved = new List(); - } - - [Fact] - public void should_proxy_websocket_input_to_downstream_service() - { - var downstreamPort = RandomPortFinder.GetRandomPort(); - var downstreamHost = "localhost"; - - var config = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/", - DownstreamPathTemplate = "/ws", - DownstreamScheme = "ws", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = downstreamHost, - Port = downstreamPort - } - } - } - } - }; - - this.Given(_ => _steps.GivenThereIsAConfiguration(config)) - .And(_ => _steps.StartFakeOcelotWithWebSockets()) - .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) - .When(_ => StartClient("ws://localhost:5000/")) - .Then(_ => _firstRecieved.Count.ShouldBe(10)) - .BDDfy(); - } - - [Fact] - public void should_proxy_websocket_input_to_downstream_service_and_use_load_balancer() - { - var downstreamPort = RandomPortFinder.GetRandomPort(); - var downstreamHost = "localhost"; - var secondDownstreamPort = RandomPortFinder.GetRandomPort(); - var secondDownstreamHost = "localhost"; - - var config = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/", - DownstreamPathTemplate = "/ws", - DownstreamScheme = "ws", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = downstreamHost, - Port = downstreamPort - }, - new FileHostAndPort - { - Host = secondDownstreamHost, - Port = secondDownstreamPort - } - }, - LoadBalancerOptions = new FileLoadBalancerOptions { Type = "RoundRobin" } - } - } - }; - - this.Given(_ => _steps.GivenThereIsAConfiguration(config)) - .And(_ => _steps.StartFakeOcelotWithWebSockets()) - .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) - .And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws")) - .When(_ => WhenIStartTheClients()) - .Then(_ => ThenBothDownstreamServicesAreCalled()) - .BDDfy(); - } - - private void ThenBothDownstreamServicesAreCalled() - { - _firstRecieved.Count.ShouldBe(10); - _firstRecieved.ForEach(x => - { - x.ShouldBe("test"); - }); - - _secondRecieved.Count.ShouldBe(10); - _secondRecieved.ForEach(x => - { - x.ShouldBe("chocolate"); - }); - } - - private async Task WhenIStartTheClients() - { - var firstClient = StartClient("ws://localhost:5000/"); - - var secondClient = StartSecondClient("ws://localhost:5000/"); - - await Task.WhenAll(firstClient, secondClient); - } - - private async Task StartClient(string url) - { - var client = new ClientWebSocket(); - - await client.ConnectAsync(new Uri(url), CancellationToken.None); - - var sending = Task.Run(async () => - { - string line = "test"; - for (int i = 0; i < 10; i++) - { - var bytes = Encoding.UTF8.GetBytes(line); - - await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, - CancellationToken.None); - await Task.Delay(10); - } - - await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - }); - - var receiving = Task.Run(async () => - { - var buffer = new byte[1024 * 4]; - - while (true) - { - var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - if (result.MessageType == WebSocketMessageType.Text) - { - _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); - } - else if (result.MessageType == WebSocketMessageType.Close) - { - if (client.State != WebSocketState.Closed) - { - // Last version, the client state is CloseReceived - // Valid states are: Open, CloseReceived, CloseSent - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - } - - break; - } - } - }); - - await Task.WhenAll(sending, receiving); - } - - private async Task StartSecondClient(string url) - { - await Task.Delay(500); - - var client = new ClientWebSocket(); - - await client.ConnectAsync(new Uri(url), CancellationToken.None); - - var sending = Task.Run(async () => - { - string line = "test"; - for (int i = 0; i < 10; i++) - { - var bytes = Encoding.UTF8.GetBytes(line); - - await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, - CancellationToken.None); - await Task.Delay(10); - } - - await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - }); - - var receiving = Task.Run(async () => - { - var buffer = new byte[1024 * 4]; - - while (true) - { - var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - if (result.MessageType == WebSocketMessageType.Text) - { - _secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); - } - else if (result.MessageType == WebSocketMessageType.Close) - { - if (client.State != WebSocketState.Closed) - { - // Last version, the client state is CloseReceived - // Valid states are: Open, CloseReceived, CloseSent - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); - } - - break; - } - } - }); - - await Task.WhenAll(sending, receiving); - } - - private async Task StartFakeDownstreamService(string url, string path) - { - await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) => - { - if (context.Request.Path == path) - { - if (context.WebSockets.IsWebSocketRequest) - { - var webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Echo(webSocket); - } - else - { - context.Response.StatusCode = 400; - } - } - else - { - await next(); - } - }); - } - - private async Task StartSecondFakeDownstreamService(string url, string path) - { - await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) => - { - if (context.Request.Path == path) - { - if (context.WebSockets.IsWebSocketRequest) - { - WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await Message(webSocket); - } - else - { - context.Response.StatusCode = 400; - } - } - else - { - await next(); - } - }); - } - - private async Task Echo(WebSocket webSocket) - { - try - { - var buffer = new byte[1024 * 4]; - - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - while (!result.CloseStatus.HasValue) - { - await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); - - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } - - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - private async Task Message(WebSocket webSocket) - { - try - { - var buffer = new byte[1024 * 4]; - - var bytes = Encoding.UTF8.GetBytes("chocolate"); - - var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - while (!result.CloseStatus.HasValue) - { - await webSocket.SendAsync(new ArraySegment(bytes), result.MessageType, result.EndOfMessage, CancellationToken.None); - - result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - } - - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - - public void Dispose() - { - _serviceHandler?.Dispose(); - _steps.Dispose(); - } - } -} +namespace Ocelot.AcceptanceTests +{ + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Net.WebSockets; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class WebSocketTests : IDisposable + { + private readonly List _secondRecieved; + private readonly List _firstRecieved; + private readonly Steps _steps; + private readonly ServiceHandler _serviceHandler; + + public WebSocketTests() + { + _serviceHandler = new ServiceHandler(); + _steps = new Steps(); + _firstRecieved = new List(); + _secondRecieved = new List(); + } + + [Fact] + public void should_proxy_websocket_input_to_downstream_service() + { + var downstreamPort = RandomPortFinder.GetRandomPort(); + var downstreamHost = "localhost"; + + var config = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + UpstreamPathTemplate = "/", + DownstreamPathTemplate = "/ws", + DownstreamScheme = "ws", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = downstreamHost, + Port = downstreamPort + } + } + } + } + }; + + this.Given(_ => _steps.GivenThereIsAConfiguration(config)) + .And(_ => _steps.StartFakeOcelotWithWebSockets()) + .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) + .When(_ => StartClient("ws://localhost:5000/")) + .Then(_ => _firstRecieved.Count.ShouldBe(10)) + .BDDfy(); + } + + [Fact] + public void should_proxy_websocket_input_to_downstream_service_and_use_load_balancer() + { + var downstreamPort = RandomPortFinder.GetRandomPort(); + var downstreamHost = "localhost"; + var secondDownstreamPort = RandomPortFinder.GetRandomPort(); + var secondDownstreamHost = "localhost"; + + var config = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + UpstreamPathTemplate = "/", + DownstreamPathTemplate = "/ws", + DownstreamScheme = "ws", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = downstreamHost, + Port = downstreamPort + }, + new FileHostAndPort + { + Host = secondDownstreamHost, + Port = secondDownstreamPort + } + }, + LoadBalancerOptions = new FileLoadBalancerOptions { Type = "RoundRobin" } + } + } + }; + + this.Given(_ => _steps.GivenThereIsAConfiguration(config)) + .And(_ => _steps.StartFakeOcelotWithWebSockets()) + .And(_ => StartFakeDownstreamService($"http://{downstreamHost}:{downstreamPort}", "/ws")) + .And(_ => StartSecondFakeDownstreamService($"http://{secondDownstreamHost}:{secondDownstreamPort}", "/ws")) + .When(_ => WhenIStartTheClients()) + .Then(_ => ThenBothDownstreamServicesAreCalled()) + .BDDfy(); + } + + private void ThenBothDownstreamServicesAreCalled() + { + _firstRecieved.Count.ShouldBe(10); + _firstRecieved.ForEach(x => + { + x.ShouldBe("test"); + }); + + _secondRecieved.Count.ShouldBe(10); + _secondRecieved.ForEach(x => + { + x.ShouldBe("chocolate"); + }); + } + + private async Task WhenIStartTheClients() + { + var firstClient = StartClient("ws://localhost:5000/"); + + var secondClient = StartSecondClient("ws://localhost:5000/"); + + await Task.WhenAll(firstClient, secondClient); + } + + private async Task StartClient(string url) + { + var client = new ClientWebSocket(); + + await client.ConnectAsync(new Uri(url), CancellationToken.None); + + var sending = Task.Run(async () => + { + string line = "test"; + for (int i = 0; i < 10; i++) + { + var bytes = Encoding.UTF8.GetBytes(line); + + await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, + CancellationToken.None); + await Task.Delay(10); + } + + await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + }); + + var receiving = Task.Run(async () => + { + var buffer = new byte[1024 * 4]; + + while (true) + { + var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + if (result.MessageType == WebSocketMessageType.Text) + { + _firstRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + if (client.State != WebSocketState.Closed) + { + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + } + + break; + } + } + }); + + await Task.WhenAll(sending, receiving); + } + + private async Task StartSecondClient(string url) + { + await Task.Delay(500); + + var client = new ClientWebSocket(); + + await client.ConnectAsync(new Uri(url), CancellationToken.None); + + var sending = Task.Run(async () => + { + string line = "test"; + for (int i = 0; i < 10; i++) + { + var bytes = Encoding.UTF8.GetBytes(line); + + await client.SendAsync(new ArraySegment(bytes), WebSocketMessageType.Text, true, + CancellationToken.None); + await Task.Delay(10); + } + + await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + }); + + var receiving = Task.Run(async () => + { + var buffer = new byte[1024 * 4]; + + while (true) + { + var result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + if (result.MessageType == WebSocketMessageType.Text) + { + _secondRecieved.Add(Encoding.UTF8.GetString(buffer, 0, result.Count)); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + if (client.State != WebSocketState.Closed) + { + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + } + + break; + } + } + }); + + await Task.WhenAll(sending, receiving); + } + + private async Task StartFakeDownstreamService(string url, string path) + { + await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) => + { + if (context.Request.Path == path) + { + if (context.WebSockets.IsWebSocketRequest) + { + var webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Echo(webSocket); + } + else + { + context.Response.StatusCode = 400; + } + } + else + { + await next(); + } + }); + } + + private async Task StartSecondFakeDownstreamService(string url, string path) + { + await _serviceHandler.StartFakeDownstreamService(url, path, async (context, next) => + { + if (context.Request.Path == path) + { + if (context.WebSockets.IsWebSocketRequest) + { + WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); + await Message(webSocket); + } + else + { + context.Response.StatusCode = 400; + } + } + else + { + await next(); + } + }); + } + + private async Task Echo(WebSocket webSocket) + { + try + { + var buffer = new byte[1024 * 4]; + + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + while (!result.CloseStatus.HasValue) + { + await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + private async Task Message(WebSocket webSocket) + { + try + { + var buffer = new byte[1024 * 4]; + + var bytes = Encoding.UTF8.GetBytes("chocolate"); + + var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + + while (!result.CloseStatus.HasValue) + { + await webSocket.SendAsync(new ArraySegment(bytes), result.MessageType, result.EndOfMessage, CancellationToken.None); + + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + public void Dispose() + { + _serviceHandler?.Dispose(); + _steps.Dispose(); + } + } +} diff --git a/test/Ocelot.Benchmarks/AllTheThingsBenchmarks.cs b/test/Ocelot.Benchmarks/AllTheThingsBenchmarks.cs index 38cb3a063..f51aa5326 100644 --- a/test/Ocelot.Benchmarks/AllTheThingsBenchmarks.cs +++ b/test/Ocelot.Benchmarks/AllTheThingsBenchmarks.cs @@ -1,156 +1,156 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Columns; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Validators; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Ocelot.Configuration.File; -using Ocelot.DependencyInjection; -using Ocelot.Middleware; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Ocelot.Benchmarks -{ - [Config(typeof(AllTheThingsBenchmarks))] - public class AllTheThingsBenchmarks : ManualConfig - { - private IWebHost _service; - private IWebHost _ocelot; - private HttpClient _httpClient; - - public AllTheThingsBenchmarks() - { - Add(StatisticColumn.AllStatistics); - Add(MemoryDiagnoser.Default); - Add(BaselineValidator.FailOnError); - } - - [GlobalSetup] - public void SetUp() - { - 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" }, - } - } - }; - - GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty); - GivenThereIsAConfiguration(configuration); - GivenOcelotIsRunning("http://localhost:5000"); - - _httpClient = new HttpClient(); - } - - [Benchmark(Baseline = true)] - public async Task Baseline() - { - var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/"); - var response = await _httpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - } - - /* * Summary* - BenchmarkDotNet = v0.10.13, OS = macOS 10.12.6 (16G1212) [Darwin 16.7.0] - Intel Core i5-4278U CPU 2.60GHz(Haswell), 1 CPU, 4 logical cores and 2 physical cores - .NET Core SDK = 2.1.4 - - [Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT - DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT - Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Scaled | Gen 0 | Gen 1 | Allocated | - --------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|--------:|-------:|----------:| - Baseline | 2.102 ms | 0.0292 ms | 0.0273 ms | 0.0070 ms | 2.063 ms | 2.080 ms | 2.093 ms | 2.122 ms | 2.152 ms | 475.8 | 1.00 | 31.2500 | 3.9063 | 1.63 KB |*/ - - private void GivenOcelotIsRunning(string url) - { - _ocelot = new WebHostBuilder() - .UseKestrel() - .UseUrls(url) - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config - .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) - .AddJsonFile("ocelot.json", false, false) - .AddEnvironmentVariables(); - }) - .ConfigureServices(s => - { - s.AddOcelot(); - }) - .ConfigureLogging((hostingContext, logging) => - { - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - }) - .UseIISIntegration() - .Configure(app => - { - app.UseOcelot().Wait(); - }) - .Build(); - - _ocelot.Start(); - } - - public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) - { - var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json"); - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - } - - private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) - { - _service = new WebHostBuilder() - .UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.UsePathBase(basePath); - app.Run(async context => - { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - }) - .Build(); - - _service.Start(); - } - } -} +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Validators; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Ocelot.Configuration.File; +using Ocelot.DependencyInjection; +using Ocelot.Middleware; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Ocelot.Benchmarks +{ + [Config(typeof(AllTheThingsBenchmarks))] + public class AllTheThingsBenchmarks : ManualConfig + { + private IWebHost _service; + private IWebHost _ocelot; + private HttpClient _httpClient; + + public AllTheThingsBenchmarks() + { + Add(StatisticColumn.AllStatistics); + Add(MemoryDiagnoser.Default); + Add(BaselineValidator.FailOnError); + } + + [GlobalSetup] + public void SetUp() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51879, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + } + } + }; + + GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 201, string.Empty); + GivenThereIsAConfiguration(configuration); + GivenOcelotIsRunning("http://localhost:5000"); + + _httpClient = new HttpClient(); + } + + [Benchmark(Baseline = true)] + public async Task Baseline() + { + var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/"); + var response = await _httpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + } + + /* * Summary* + BenchmarkDotNet = v0.10.13, OS = macOS 10.12.6 (16G1212) [Darwin 16.7.0] + Intel Core i5-4278U CPU 2.60GHz(Haswell), 1 CPU, 4 logical cores and 2 physical cores + .NET Core SDK = 2.1.4 + + [Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT + DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT + Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Scaled | Gen 0 | Gen 1 | Allocated | + --------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|--------:|-------:|----------:| + Baseline | 2.102 ms | 0.0292 ms | 0.0273 ms | 0.0070 ms | 2.063 ms | 2.080 ms | 2.093 ms | 2.122 ms | 2.152 ms | 475.8 | 1.00 | 31.2500 | 3.9063 | 1.63 KB |*/ + + private void GivenOcelotIsRunning(string url) + { + _ocelot = new WebHostBuilder() + .UseKestrel() + .UseUrls(url) + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config + .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true) + .AddJsonFile("ocelot.json", false, false) + .AddEnvironmentVariables(); + }) + .ConfigureServices(s => + { + s.AddOcelot(); + }) + .ConfigureLogging((hostingContext, logging) => + { + logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); + }) + .UseIISIntegration() + .Configure(app => + { + app.UseOcelot().Wait(); + }) + .Build(); + + _ocelot.Start(); + } + + public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) + { + var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json"); + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + } + + private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody) + { + _service = new WebHostBuilder() + .UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase(basePath); + app.Run(async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }) + .Build(); + + _service.Start(); + } + } +} diff --git a/test/Ocelot.Benchmarks/DictionaryBenchmarks.cs b/test/Ocelot.Benchmarks/DictionaryBenchmarks.cs index 5799ca420..84540af30 100644 --- a/test/Ocelot.Benchmarks/DictionaryBenchmarks.cs +++ b/test/Ocelot.Benchmarks/DictionaryBenchmarks.cs @@ -1,77 +1,76 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Columns; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Diagnosers; -using BenchmarkDotNet.Validators; -using System.Net.Http; - -namespace Ocelot.Benchmarks -{ - using Configuration; - using Configuration.Builder; - using Requester; - using System.Collections.Concurrent; - - [Config(typeof(DictionaryBenchmarks))] - public class DictionaryBenchmarks : ManualConfig - { - private ConcurrentDictionary _downstreamReRouteDictionary; - private ConcurrentDictionary _stringReRouteDictionary; - private HttpClientWrapper _client; - private string _stringKey; - private DownstreamReRoute _downstreamReRouteKey; - - public DictionaryBenchmarks() - { - Add(StatisticColumn.AllStatistics); - Add(MemoryDiagnoser.Default); - Add(BaselineValidator.FailOnError); - } - - [GlobalSetup] - public void SetUp() - { - _downstreamReRouteKey = new DownstreamReRouteBuilder().Build(); - _stringKey = "test"; - _client = new HttpClientWrapper(new HttpClient()); - _downstreamReRouteDictionary = new ConcurrentDictionary(); - - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - _downstreamReRouteDictionary.TryAdd(new DownstreamReRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); - - _stringReRouteDictionary = new ConcurrentDictionary(); - _stringReRouteDictionary.TryAdd("1", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("2", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("3", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("4", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("5", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("6", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("7", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("8", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("9", new HttpClientWrapper(new HttpClient())); - _stringReRouteDictionary.TryAdd("10", new HttpClientWrapper(new HttpClient())); - } - - [Benchmark(Baseline = true)] - public IHttpClient StringKey() - { - _stringReRouteDictionary.AddOrUpdate(_stringKey, _client, (k, oldValue) => _client); - return _stringReRouteDictionary.TryGetValue(_stringKey, out var client) ? client : null; - } - - [Benchmark] - public IHttpClient DownstreamReRouteKey() - { - _downstreamReRouteDictionary.AddOrUpdate(_downstreamReRouteKey, _client, (k, oldValue) => _client); - return _downstreamReRouteDictionary.TryGetValue(_downstreamReRouteKey, out var client) ? client : null; - } - } -} +namespace Ocelot.Benchmarks +{ + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Requester; + using System.Collections.Concurrent; + using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Columns; + using BenchmarkDotNet.Configs; + using BenchmarkDotNet.Diagnosers; + using BenchmarkDotNet.Validators; + using System.Net.Http; + + [Config(typeof(DictionaryBenchmarks))] + public class DictionaryBenchmarks : ManualConfig + { + private ConcurrentDictionary _downstreamRouteDictionary; + private ConcurrentDictionary _stringRouteDictionary; + private HttpClientWrapper _client; + private string _stringKey; + private DownstreamRoute _downstreamRouteKey; + + public DictionaryBenchmarks() + { + Add(StatisticColumn.AllStatistics); + Add(MemoryDiagnoser.Default); + Add(BaselineValidator.FailOnError); + } + + [GlobalSetup] + public void SetUp() + { + _downstreamRouteKey = new DownstreamRouteBuilder().Build(); + _stringKey = "test"; + _client = new HttpClientWrapper(new HttpClient()); + _downstreamRouteDictionary = new ConcurrentDictionary(); + + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + _downstreamRouteDictionary.TryAdd(new DownstreamRouteBuilder().Build(), new HttpClientWrapper(new HttpClient())); + + _stringRouteDictionary = new ConcurrentDictionary(); + _stringRouteDictionary.TryAdd("1", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("2", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("3", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("4", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("5", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("6", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("7", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("8", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("9", new HttpClientWrapper(new HttpClient())); + _stringRouteDictionary.TryAdd("10", new HttpClientWrapper(new HttpClient())); + } + + [Benchmark(Baseline = true)] + public IHttpClient StringKey() + { + _stringRouteDictionary.AddOrUpdate(_stringKey, _client, (k, oldValue) => _client); + return _stringRouteDictionary.TryGetValue(_stringKey, out var client) ? client : null; + } + + [Benchmark] + public IHttpClient DownstreamRouteKey() + { + _downstreamRouteDictionary.AddOrUpdate(_downstreamRouteKey, _client, (k, oldValue) => _client); + return _downstreamRouteDictionary.TryGetValue(_downstreamRouteKey, out var client) ? client : null; + } + } +} diff --git a/test/Ocelot.Benchmarks/DownstreamRouteFinderMiddlewareBenchmarks.cs b/test/Ocelot.Benchmarks/DownstreamRouteFinderMiddlewareBenchmarks.cs index 053b0332f..7e8d7aec4 100644 --- a/test/Ocelot.Benchmarks/DownstreamRouteFinderMiddlewareBenchmarks.cs +++ b/test/Ocelot.Benchmarks/DownstreamRouteFinderMiddlewareBenchmarks.cs @@ -55,7 +55,7 @@ public void SetUp() httpContext.Request.Path = new PathString("/test"); httpContext.Request.QueryString = new QueryString("?a=b"); httpContext.Request.Headers.Add("Host", "most"); - httpContext.Items.SetIInternalConfiguration(new InternalConfiguration(new List(), null, null, null, null, null, null, null, null)); + httpContext.Items.SetIInternalConfiguration(new InternalConfiguration(new List(), null, null, null, null, null, null, null, null)); _httpContext = httpContext; } diff --git a/test/Ocelot.IntegrationTests/AdministrationTests.cs b/test/Ocelot.IntegrationTests/AdministrationTests.cs index 4d3f464d8..d45bac237 100644 --- a/test/Ocelot.IntegrationTests/AdministrationTests.cs +++ b/test/Ocelot.IntegrationTests/AdministrationTests.cs @@ -1,912 +1,912 @@ -using IdentityServer4.AccessTokenValidation; -using IdentityServer4.Models; -using IdentityServer4.Test; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Newtonsoft.Json; -using Ocelot.Administration; -using Ocelot.Cache; -using Ocelot.Configuration.File; -using Ocelot.DependencyInjection; -using Ocelot.Middleware; -using Shouldly; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using TestStack.BDDfy; -using Ocelot.Configuration.ChangeTracking; -using Xunit; - -namespace Ocelot.IntegrationTests -{ - public class AdministrationTests : IDisposable - { - private HttpClient _httpClient; - private readonly HttpClient _httpClientTwo; - private HttpResponseMessage _response; - private IHost _builder; - private IHostBuilder _webHostBuilder; - private string _ocelotBaseUrl; - private BearerToken _token; - private IHostBuilder _webHostBuilderTwo; - private IHost _builderTwo; - private IHost _identityServerBuilder; - private IHost _fooServiceBuilder; - private IHost _barServiceBuilder; - - public AdministrationTests() - { - _httpClient = new HttpClient(); - _httpClientTwo = new HttpClient(); - _ocelotBaseUrl = "http://localhost:5000"; - _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); - } - - [Fact] - public void should_return_response_401_with_call_re_routes_controller() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller_using_base_url_added_in_file_config() - { - _httpClient = new HttpClient(); - _ocelotBaseUrl = "http://localhost:5011"; - _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - BaseUrl = _ocelotBaseUrl - } - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunningWithNoWebHostBuilder(_ocelotBaseUrl)) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b() - { - var configuration = new FileConfiguration(); - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet()) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenAnotherOcelotIsRunning("http://localhost:5017")) - .When(x => WhenIGetUrlOnTheSecondOcelot("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - [Fact] - public void should_return_file_configuration() - { - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - RequestIdKey = "RequestId", - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "127.0.0.1", - } - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10, - Region = "Geoff" - } - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10, - Region = "Dave" - } - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(configuration)) - .BDDfy(); - } - - [Fact] - public void should_get_file_configuration_edit_and_post_updated_version() - { - var initialConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test" - } - }, - }; - - var updatedConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/geoffrey", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.123.123", - Port = 443, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/blooper/{productId}", - UpstreamHttpMethod = new List { "post" }, - UpstreamPathTemplate = "/test" - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .And(_ => ThenTheConfigurationIsSavedCorrectly(updatedConfiguration)) - .BDDfy(); - } - - [Fact] - public void should_activate_change_token_when_configuration_is_updated() - { - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration(), - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - }, - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - }, - }, - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", configuration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => TheChangeTokenShouldBeActive()) - .And(x => ThenTheResponseShouldBe(configuration)) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .And(x => ThenTheResponseShouldBe(configuration)) - .And(_ => ThenTheConfigurationIsSavedCorrectly(configuration)) - .BDDfy(); - } - - private void TheChangeTokenShouldBeActive() - { - _builder.Services.GetRequiredService().ChangeToken.HasChanged.ShouldBeTrue(); - } - - private void ThenTheConfigurationIsSavedCorrectly(FileConfiguration expected) - { - var ocelotJsonPath = $"{AppContext.BaseDirectory}ocelot.json"; - var resultText = File.ReadAllText(ocelotJsonPath); - var expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented); - resultText.ShouldBe(expectedText); - - var environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot.Production.json"; - resultText = File.ReadAllText(environmentSpecificPath); - expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented); - resultText.ShouldBe(expectedText); - } - - [Fact] - public void should_get_file_configuration_edit_and_post_updated_version_redirecting_reroute() - { - var fooPort = 47689; - var barPort = 27654; - - var initialConfiguration = new FileConfiguration - { - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = fooPort, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/foo", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/foo" - } - } - }; - - var updatedConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = barPort, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/bar", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/foo" - } - } - }; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}")) - .And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}")) - .And(x => GivenOcelotIsRunning()) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("foo")) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(updatedConfiguration)) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("bar")) - .When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration)) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => ThenTheResponseShouldBe(initialConfiguration)) - .And(x => WhenIGetUrlOnTheApiGateway("/foo")) - .Then(x => ThenTheResponseBodyShouldBe("foo")) - .BDDfy(); - } - - [Fact] - public void should_clear_region() - { - var initialConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10 - } - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10 - } - } - } - }; - - var regionToClear = "gettest"; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) - .BDDfy(); - } - - [Fact] - public void should_return_response_200_with_call_re_routes_controller_when_using_own_identity_server_to_secure_admin_area() - { - var configuration = new FileConfiguration(); - - var identityServerRootUrl = "http://localhost:5123"; - - Action options = o => - { - o.Authority = identityServerRootUrl; - o.ApiName = "api"; - o.RequireHttpsMetadata = false; - o.SupportedTokens = SupportedTokens.Both; - o.ApiSecret = "secret"; - }; - - this.Given(x => GivenThereIsAConfiguration(configuration)) - .And(x => GivenThereIsAnIdentityServerOn(identityServerRootUrl, "api")) - .And(x => GivenOcelotIsRunningWithIdentityServerSettings(options)) - .And(x => GivenIHaveAToken(identityServerRootUrl)) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); - } - - private void GivenIHaveAToken(string url) - { - var formData = new List> - { - new KeyValuePair("client_id", "api"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "api"), - new KeyValuePair("username", "test"), - new KeyValuePair("password", "test"), - new KeyValuePair("grant_type", "password") - }; - var content = new FormUrlEncodedContent(formData); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.PostAsync($"{url}/connect/token", content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - } - } - - private void GivenThereIsAnIdentityServerOn(string url, string apiName) - { - _identityServerBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureServices(services => - { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() - .AddInMemoryApiResources(new List - { - new ApiResource - { - Name = apiName, - Description = apiName, - Enabled = true, - DisplayName = apiName, - Scopes = new List() - { - new Scope(apiName), - }, - }, - }) - .AddInMemoryClients(new List - { - new Client - { - ClientId = apiName, - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List { new Secret("secret".Sha256()) }, - AllowedScopes = new List { apiName }, - AccessTokenType = AccessTokenType.Jwt, - Enabled = true - }, - }) - .AddTestUsers(new List - { - new TestUser - { - Username = "test", - Password = "test", - SubjectId = "1231231" - }, - }); - }) - .Configure(app => - { - app.UseIdentityServer(); - } - ); - }).Build(); - - _identityServerBuilder.Start(); - - using (var httpClient = new HttpClient()) - { - var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result; - response.EnsureSuccessStatusCode(); - } - } - - private void GivenAnotherOcelotIsRunning(string baseUrl) - { - _httpClientTwo.BaseAddress = new Uri(baseUrl); - - _webHostBuilderTwo = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builderTwo = _webHostBuilderTwo.Build(); - - _builderTwo.Start(); - } - - private void GivenIdentityServerSigningEnvironmentalVariablesAreSet() - { - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "idsrv3test.pfx"); - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "idsrv3test"); - } - - private void WhenIGetUrlOnTheSecondOcelot(string url) - { - _httpClientTwo.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - _response = _httpClientTwo.GetAsync(url).Result; - } - - private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) - { - var json = JsonConvert.SerializeObject(updatedConfiguration); - var content = new StringContent(json); - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - _response = _httpClient.PostAsync(url, content).Result; - } - - private void ThenTheResponseShouldBe(List expected) - { - var content = _response.Content.ReadAsStringAsync().Result; - var result = JsonConvert.DeserializeObject(content); - result.Value.ShouldBe(expected); - } - - private void ThenTheResponseBodyShouldBe(string expected) - { - var content = _response.Content.ReadAsStringAsync().Result; - content.ShouldBe(expected); - } - - private void ThenTheResponseShouldBe(FileConfiguration expecteds) - { - var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); - - response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); - response.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); - response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); - response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); - - for (var i = 0; i < response.ReRoutes.Count; i++) - { - for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++) - { - var result = response.ReRoutes[i].DownstreamHostAndPorts[j]; - var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j]; - result.Host.ShouldBe(expected.Host); - result.Port.ShouldBe(expected.Port); - } - - response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate); - response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme); - response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate); - response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod); - } - } - - private void GivenIHaveAddedATokenToMyRequest() - { - _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - } - - private void GivenIHaveAnOcelotToken(string adminPath) - { - var tokenUrl = $"{adminPath}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "admin"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "admin"), - new KeyValuePair("grant_type", "client_credentials") - }; - var content = new FormUrlEncodedContent(formData); - - var response = _httpClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - var configPath = $"{adminPath}/.well-known/openid-configuration"; - response = _httpClient.GetAsync(configPath).Result; - response.EnsureSuccessStatusCode(); - } - - private void GivenOcelotIsRunningWithIdentityServerSettings(Action configOptions) - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddSingleton(_webHostBuilder); - x.AddOcelot() - .AddAdministration("/administration", configOptions); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenOcelotIsRunning() - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddMvc(s => s.EnableEndpointRouting = false); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenOcelotIsRunningWithNoWebHostBuilder(string baseUrl) - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddSingleton(_webHostBuilder); - x.AddOcelot() - .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) - { - var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - var text = File.ReadAllText(configurationPath); - - configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - text = File.ReadAllText(configurationPath); - } - - private void WhenIGetUrlOnTheApiGateway(string url) - { - _response = _httpClient.GetAsync(url).Result; - } - - private void WhenIDeleteOnTheApiGateway(string url) - { - _response = _httpClient.DeleteAsync(url).Result; - } - - private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) - { - _response.StatusCode.ShouldBe(expectedHttpStatusCode); - } - - public void Dispose() - { - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", ""); - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); - _builder?.Dispose(); - _httpClient?.Dispose(); - _identityServerBuilder?.Dispose(); - } - - private void GivenThereIsAFooServiceRunningOn(string baseUrl) - { - _fooServiceBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.UsePathBase("/foo"); - app.Run(async context => - { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("foo"); - }); - }); - }).Build(); - - _fooServiceBuilder.Start(); - } - - private void GivenThereIsABarServiceRunningOn(string baseUrl) - { - _barServiceBuilder = Host.CreateDefaultBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.UsePathBase("/bar"); - app.Run(async context => - { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("bar"); - }); - }); - }).Build(); - - _barServiceBuilder.Start(); - } - } -} +using IdentityServer4.AccessTokenValidation; +using IdentityServer4.Models; +using IdentityServer4.Test; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Newtonsoft.Json; +using Ocelot.Administration; +using Ocelot.Cache; +using Ocelot.Configuration.File; +using Ocelot.DependencyInjection; +using Ocelot.Middleware; +using Shouldly; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using TestStack.BDDfy; +using Ocelot.Configuration.ChangeTracking; +using Xunit; + +namespace Ocelot.IntegrationTests +{ + public class AdministrationTests : IDisposable + { + private HttpClient _httpClient; + private readonly HttpClient _httpClientTwo; + private HttpResponseMessage _response; + private IHost _builder; + private IHostBuilder _webHostBuilder; + private string _ocelotBaseUrl; + private BearerToken _token; + private IHostBuilder _webHostBuilderTwo; + private IHost _builderTwo; + private IHost _identityServerBuilder; + private IHost _fooServiceBuilder; + private IHost _barServiceBuilder; + + public AdministrationTests() + { + _httpClient = new HttpClient(); + _httpClientTwo = new HttpClient(); + _ocelotBaseUrl = "http://localhost:5000"; + _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); + } + + [Fact] + public void should_return_response_401_with_call_re_routes_controller() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_call_re_routes_controller() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_call_re_routes_controller_using_base_url_added_in_file_config() + { + _httpClient = new HttpClient(); + _ocelotBaseUrl = "http://localhost:5011"; + _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + BaseUrl = _ocelotBaseUrl + } + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunningWithNoWebHostBuilder(_ocelotBaseUrl)) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b() + { + var configuration = new FileConfiguration(); + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet()) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenAnotherOcelotIsRunning("http://localhost:5017")) + .When(x => WhenIGetUrlOnTheSecondOcelot("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + [Fact] + public void should_return_file_configuration() + { + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + RequestIdKey = "RequestId", + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "127.0.0.1", + } + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + Region = "Geoff" + } + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + Region = "Dave" + } + } + } + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(configuration)) + .BDDfy(); + } + + [Fact] + public void should_get_file_configuration_edit_and_post_updated_version() + { + var initialConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/" + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test" + } + }, + }; + + var updatedConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/geoffrey", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/" + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "123.123.123", + Port = 443, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/blooper/{productId}", + UpstreamHttpMethod = new List { "post" }, + UpstreamPathTemplate = "/test" + } + } + }; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .And(_ => ThenTheConfigurationIsSavedCorrectly(updatedConfiguration)) + .BDDfy(); + } + + [Fact] + public void should_activate_change_token_when_configuration_is_updated() + { + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration(), + Routes = new List + { + new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + }, + }, + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", configuration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => TheChangeTokenShouldBeActive()) + .And(x => ThenTheResponseShouldBe(configuration)) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .And(x => ThenTheResponseShouldBe(configuration)) + .And(_ => ThenTheConfigurationIsSavedCorrectly(configuration)) + .BDDfy(); + } + + private void TheChangeTokenShouldBeActive() + { + _builder.Services.GetRequiredService().ChangeToken.HasChanged.ShouldBeTrue(); + } + + private void ThenTheConfigurationIsSavedCorrectly(FileConfiguration expected) + { + var ocelotJsonPath = $"{AppContext.BaseDirectory}ocelot.json"; + var resultText = File.ReadAllText(ocelotJsonPath); + var expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented); + resultText.ShouldBe(expectedText); + + var environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot.Production.json"; + resultText = File.ReadAllText(environmentSpecificPath); + expectedText = JsonConvert.SerializeObject(expected, Formatting.Indented); + resultText.ShouldBe(expectedText); + } + + [Fact] + public void should_get_file_configuration_edit_and_post_updated_version_redirecting_route() + { + var fooPort = 47689; + var barPort = 27654; + + var initialConfiguration = new FileConfiguration + { + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = fooPort, + } + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/foo", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/foo" + } + } + }; + + var updatedConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = barPort, + } + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/bar", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/foo" + } + } + }; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenThereIsAFooServiceRunningOn($"http://localhost:{fooPort}")) + .And(x => GivenThereIsABarServiceRunningOn($"http://localhost:{barPort}")) + .And(x => GivenOcelotIsRunning()) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("foo")) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(updatedConfiguration)) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("bar")) + .When(x => WhenIPostOnTheApiGateway("/administration/configuration", initialConfiguration)) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenTheResponseShouldBe(initialConfiguration)) + .And(x => WhenIGetUrlOnTheApiGateway("/foo")) + .Then(x => ThenTheResponseBodyShouldBe("foo")) + .BDDfy(); + } + + [Fact] + public void should_clear_region() + { + var initialConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10 + } + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10 + } + } + } + }; + + var regionToClear = "gettest"; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) + .BDDfy(); + } + + [Fact] + public void should_return_response_200_with_call_re_routes_controller_when_using_own_identity_server_to_secure_admin_area() + { + var configuration = new FileConfiguration(); + + var identityServerRootUrl = "http://localhost:5123"; + + Action options = o => + { + o.Authority = identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + + this.Given(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenThereIsAnIdentityServerOn(identityServerRootUrl, "api")) + .And(x => GivenOcelotIsRunningWithIdentityServerSettings(options)) + .And(x => GivenIHaveAToken(identityServerRootUrl)) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIGetUrlOnTheApiGateway("/administration/configuration")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); + } + + private void GivenIHaveAToken(string url) + { + var formData = new List> + { + new KeyValuePair("client_id", "api"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync($"{url}/connect/token", content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName) + { + _identityServerBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = apiName, + Enabled = true, + DisplayName = apiName, + Scopes = new List() + { + new Scope(apiName), + }, + }, + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = apiName, + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List { new Secret("secret".Sha256()) }, + AllowedScopes = new List { apiName }, + AccessTokenType = AccessTokenType.Jwt, + Enabled = true + }, + }) + .AddTestUsers(new List + { + new TestUser + { + Username = "test", + Password = "test", + SubjectId = "1231231" + }, + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + } + ); + }).Build(); + + _identityServerBuilder.Start(); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result; + response.EnsureSuccessStatusCode(); + } + } + + private void GivenAnotherOcelotIsRunning(string baseUrl) + { + _httpClientTwo.BaseAddress = new Uri(baseUrl); + + _webHostBuilderTwo = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builderTwo = _webHostBuilderTwo.Build(); + + _builderTwo.Start(); + } + + private void GivenIdentityServerSigningEnvironmentalVariablesAreSet() + { + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "idsrv3test.pfx"); + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "idsrv3test"); + } + + private void WhenIGetUrlOnTheSecondOcelot(string url) + { + _httpClientTwo.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + _response = _httpClientTwo.GetAsync(url).Result; + } + + private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) + { + var json = JsonConvert.SerializeObject(updatedConfiguration); + var content = new StringContent(json); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + _response = _httpClient.PostAsync(url, content).Result; + } + + private void ThenTheResponseShouldBe(List expected) + { + var content = _response.Content.ReadAsStringAsync().Result; + var result = JsonConvert.DeserializeObject(content); + result.Value.ShouldBe(expected); + } + + private void ThenTheResponseBodyShouldBe(string expected) + { + var content = _response.Content.ReadAsStringAsync().Result; + content.ShouldBe(expected); + } + + private void ThenTheResponseShouldBe(FileConfiguration expecteds) + { + var response = JsonConvert.DeserializeObject(_response.Content.ReadAsStringAsync().Result); + + response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); + response.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); + response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); + response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); + + for (var i = 0; i < response.Routes.Count; i++) + { + for (var j = 0; j < response.Routes[i].DownstreamHostAndPorts.Count; j++) + { + var result = response.Routes[i].DownstreamHostAndPorts[j]; + var expected = expecteds.Routes[i].DownstreamHostAndPorts[j]; + result.Host.ShouldBe(expected.Host); + result.Port.ShouldBe(expected.Port); + } + + response.Routes[i].DownstreamPathTemplate.ShouldBe(expecteds.Routes[i].DownstreamPathTemplate); + response.Routes[i].DownstreamScheme.ShouldBe(expecteds.Routes[i].DownstreamScheme); + response.Routes[i].UpstreamPathTemplate.ShouldBe(expecteds.Routes[i].UpstreamPathTemplate); + response.Routes[i].UpstreamHttpMethod.ShouldBe(expecteds.Routes[i].UpstreamHttpMethod); + } + } + + private void GivenIHaveAddedATokenToMyRequest() + { + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + } + + private void GivenIHaveAnOcelotToken(string adminPath) + { + var tokenUrl = $"{adminPath}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "admin"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "admin"), + new KeyValuePair("grant_type", "client_credentials") + }; + var content = new FormUrlEncodedContent(formData); + + var response = _httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + var configPath = $"{adminPath}/.well-known/openid-configuration"; + response = _httpClient.GetAsync(configPath).Result; + response.EnsureSuccessStatusCode(); + } + + private void GivenOcelotIsRunningWithIdentityServerSettings(Action configOptions) + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddSingleton(_webHostBuilder); + x.AddOcelot() + .AddAdministration("/administration", configOptions); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenOcelotIsRunning() + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(s => s.EnableEndpointRouting = false); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenOcelotIsRunningWithNoWebHostBuilder(string baseUrl) + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddSingleton(_webHostBuilder); + x.AddOcelot() + .AddAdministration("/administration", "secret"); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) + { + var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + var text = File.ReadAllText(configurationPath); + + configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + text = File.ReadAllText(configurationPath); + } + + private void WhenIGetUrlOnTheApiGateway(string url) + { + _response = _httpClient.GetAsync(url).Result; + } + + private void WhenIDeleteOnTheApiGateway(string url) + { + _response = _httpClient.DeleteAsync(url).Result; + } + + private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) + { + _response.StatusCode.ShouldBe(expectedHttpStatusCode); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", ""); + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); + _builder?.Dispose(); + _httpClient?.Dispose(); + _identityServerBuilder?.Dispose(); + } + + private void GivenThereIsAFooServiceRunningOn(string baseUrl) + { + _fooServiceBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase("/foo"); + app.Run(async context => + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync("foo"); + }); + }); + }).Build(); + + _fooServiceBuilder.Start(); + } + + private void GivenThereIsABarServiceRunningOn(string baseUrl) + { + _barServiceBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.UsePathBase("/bar"); + app.Run(async context => + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync("bar"); + }); + }); + }).Build(); + + _barServiceBuilder.Start(); + } + } +} diff --git a/test/Ocelot.IntegrationTests/CacheManagerTests.cs b/test/Ocelot.IntegrationTests/CacheManagerTests.cs index 6be90f01f..98748d247 100644 --- a/test/Ocelot.IntegrationTests/CacheManagerTests.cs +++ b/test/Ocelot.IntegrationTests/CacheManagerTests.cs @@ -1,220 +1,220 @@ -namespace Ocelot.IntegrationTests -{ - using Configuration.File; - using DependencyInjection; - using global::CacheManager.Core; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - using Microsoft.Extensions.Logging; - using Newtonsoft.Json; - using Ocelot.Administration; - using Ocelot.Cache.CacheManager; - using Ocelot.Middleware; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Net.Http; - using System.Net.Http.Headers; - using TestStack.BDDfy; - using Xunit; - - public class CacheManagerTests : IDisposable - { - private HttpClient _httpClient; - private readonly HttpClient _httpClientTwo; - private HttpResponseMessage _response; - private IHost _builder; - private IHostBuilder _webHostBuilder; - private string _ocelotBaseUrl; - private BearerToken _token; - - public CacheManagerTests() - { - _httpClient = new HttpClient(); - _httpClientTwo = new HttpClient(); - _ocelotBaseUrl = "http://localhost:5000"; - _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); - } - - [Fact] - public void should_clear_region() - { - var initialConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - }, - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10, - }, - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - }, - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/test", - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 10, - }, - }, - }, - }; - - var regionToClear = "gettest"; - - this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) - .And(x => GivenOcelotIsRunning()) - .And(x => GivenIHaveAnOcelotToken("/administration")) - .And(x => GivenIHaveAddedATokenToMyRequest()) - .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) - .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) - .BDDfy(); - } - - private void GivenIHaveAddedATokenToMyRequest() - { - _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - } - - private void GivenIHaveAnOcelotToken(string adminPath) - { - var tokenUrl = $"{adminPath}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "admin"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "admin"), - new KeyValuePair("grant_type", "client_credentials"), - }; - var content = new FormUrlEncodedContent(formData); - - var response = _httpClient.PostAsync(tokenUrl, content).Result; - var responseContent = response.Content.ReadAsStringAsync().Result; - response.EnsureSuccessStatusCode(); - _token = JsonConvert.DeserializeObject(responseContent); - var configPath = $"{adminPath}/.well-known/openid-configuration"; - response = _httpClient.GetAsync(configPath).Result; - response.EnsureSuccessStatusCode(); - } - - private void GivenOcelotIsRunning() - { - _webHostBuilder = Host.CreateDefaultBuilder() - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - Action settings = (s) => - { - s.WithMicrosoftLogging(log => - { - //log.AddConsole(LogLevel.Debug); - }) - .WithDictionaryHandle(); - }; - x.AddMvc(option => option.EnableEndpointRouting = false); - x.AddOcelot() - .AddCacheManager(settings) - .AddAdministration("/administration", "secret"); - }) - .ConfigureWebHost(webBuilder => - { - webBuilder.UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - }); - - _builder = _webHostBuilder.Build(); - - _builder.Start(); - } - - private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) - { - var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - var text = File.ReadAllText(configurationPath); - - configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - text = File.ReadAllText(configurationPath); - } - - private void WhenIDeleteOnTheApiGateway(string url) - { - _response = _httpClient.DeleteAsync(url).Result; - } - - private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) - { - _response.StatusCode.ShouldBe(expectedHttpStatusCode); - } - - public void Dispose() - { - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", ""); - Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); - _builder?.Dispose(); - _httpClient?.Dispose(); - //_identityServerBuilder?.Dispose(); - } - } -} +namespace Ocelot.IntegrationTests +{ + using Configuration.File; + using DependencyInjection; + using global::CacheManager.Core; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Logging; + using Newtonsoft.Json; + using Ocelot.Administration; + using Ocelot.Cache.CacheManager; + using Ocelot.Middleware; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Net.Http; + using System.Net.Http.Headers; + using TestStack.BDDfy; + using Xunit; + + public class CacheManagerTests : IDisposable + { + private HttpClient _httpClient; + private readonly HttpClient _httpClientTwo; + private HttpResponseMessage _response; + private IHost _builder; + private IHostBuilder _webHostBuilder; + private string _ocelotBaseUrl; + private BearerToken _token; + + public CacheManagerTests() + { + _httpClient = new HttpClient(); + _httpClientTwo = new HttpClient(); + _ocelotBaseUrl = "http://localhost:5000"; + _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); + } + + [Fact] + public void should_clear_region() + { + var initialConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + }, + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/test", + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 10, + }, + }, + }, + }; + + var regionToClear = "gettest"; + + this.Given(x => GivenThereIsAConfiguration(initialConfiguration)) + .And(x => GivenOcelotIsRunning()) + .And(x => GivenIHaveAnOcelotToken("/administration")) + .And(x => GivenIHaveAddedATokenToMyRequest()) + .When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent)) + .BDDfy(); + } + + private void GivenIHaveAddedATokenToMyRequest() + { + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + } + + private void GivenIHaveAnOcelotToken(string adminPath) + { + var tokenUrl = $"{adminPath}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "admin"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "admin"), + new KeyValuePair("grant_type", "client_credentials"), + }; + var content = new FormUrlEncodedContent(formData); + + var response = _httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + var configPath = $"{adminPath}/.well-known/openid-configuration"; + response = _httpClient.GetAsync(configPath).Result; + response.EnsureSuccessStatusCode(); + } + + private void GivenOcelotIsRunning() + { + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + Action settings = (s) => + { + s.WithMicrosoftLogging(log => + { + //log.AddConsole(LogLevel.Debug); + }) + .WithDictionaryHandle(); + }; + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddOcelot() + .AddCacheManager(settings) + .AddAdministration("/administration", "secret"); + }) + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) + { + var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + var text = File.ReadAllText(configurationPath); + + configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + text = File.ReadAllText(configurationPath); + } + + private void WhenIDeleteOnTheApiGateway(string url) + { + _response = _httpClient.DeleteAsync(url).Result; + } + + private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) + { + _response.StatusCode.ShouldBe(expectedHttpStatusCode); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", ""); + Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); + _builder?.Dispose(); + _httpClient?.Dispose(); + //_identityServerBuilder?.Dispose(); + } + } +} diff --git a/test/Ocelot.IntegrationTests/HeaderTests.cs b/test/Ocelot.IntegrationTests/HeaderTests.cs index 6b6d8e362..7cae57f91 100644 --- a/test/Ocelot.IntegrationTests/HeaderTests.cs +++ b/test/Ocelot.IntegrationTests/HeaderTests.cs @@ -43,9 +43,9 @@ public void should_pass_remote_ip_address_if_as_x_forwarded_for_header() { var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", diff --git a/test/Ocelot.IntegrationTests/RaftTests.cs b/test/Ocelot.IntegrationTests/RaftTests.cs index 4fbbb3dbb..359aa6f5e 100644 --- a/test/Ocelot.IntegrationTests/RaftTests.cs +++ b/test/Ocelot.IntegrationTests/RaftTests.cs @@ -1,513 +1,513 @@ -namespace Ocelot.IntegrationTests -{ - using Administration; - using Configuration.File; - using DependencyInjection; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Data.Sqlite; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Middleware; - using Newtonsoft.Json; - using Ocelot.Provider.Rafty; - using Rafty.Infrastructure; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Threading; - using System.Threading.Tasks; - using Xunit; - using Xunit.Abstractions; - using Wait = Rafty.Infrastructure.Wait; - - public class RaftTests : IDisposable - { - private readonly List _builders; - private readonly List _webHostBuilders; - private readonly List _threads; - private FilePeers _peers; - private HttpClient _httpClient; - private readonly HttpClient _httpClientForAssertions; - private BearerToken _token; - private HttpResponseMessage _response; - private static readonly object _lock = new object(); - private ITestOutputHelper _output; - - public RaftTests(ITestOutputHelper output) - { - _output = output; - _httpClientForAssertions = new HttpClient(); - _webHostBuilders = new List(); - _builders = new List(); - _threads = new List(); - } - - [Fact(Skip = "Still not stable, more work required in rafty..")] - public async Task should_persist_command_to_five_servers() - { - var peers = new List - { - new FilePeer {HostAndPort = "http://localhost:5000"}, - - new FilePeer {HostAndPort = "http://localhost:5001"}, - - new FilePeer {HostAndPort = "http://localhost:5002"}, - - new FilePeer {HostAndPort = "http://localhost:5003"}, - - new FilePeer {HostAndPort = "http://localhost:5004"} - }; - - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - } - }; - - var updatedConfiguration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - }, - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - Port = 80, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/geoffrey", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.123.123", - Port = 443, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/blooper/{productId}", - UpstreamHttpMethod = new List { "post" }, - UpstreamPathTemplate = "/test" - } - } - }; - - var command = new UpdateFileConfiguration(updatedConfiguration); - GivenThePeersAre(peers); - GivenThereIsAConfiguration(configuration); - GivenFiveServersAreRunning(); - await GivenIHaveAnOcelotToken("/administration"); - await WhenISendACommandIntoTheCluster(command); - Thread.Sleep(5000); - await ThenTheCommandIsReplicatedToAllStateMachines(command); - } - - [Fact(Skip = "Still not stable, more work required in rafty..")] - public async Task should_persist_command_to_five_servers_when_using_administration_api() - { - var peers = new List - { - new FilePeer {HostAndPort = "http://localhost:5005"}, - - new FilePeer {HostAndPort = "http://localhost:5006"}, - - new FilePeer {HostAndPort = "http://localhost:5007"}, - - new FilePeer {HostAndPort = "http://localhost:5008"}, - - new FilePeer {HostAndPort = "http://localhost:5009"} - }; - - var configuration = new FileConfiguration - { - }; - - var updatedConfiguration = new FileConfiguration - { - ReRoutes = new List() - { - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "127.0.0.1", - Port = 80, - } - }, - DownstreamScheme = "http", - DownstreamPathTemplate = "/geoffrey", - UpstreamHttpMethod = new List { "get" }, - UpstreamPathTemplate = "/" - }, - new FileReRoute() - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.123.123", - Port = 443, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/blooper/{productId}", - UpstreamHttpMethod = new List { "post" }, - UpstreamPathTemplate = "/test" - } - } - }; - - var command = new UpdateFileConfiguration(updatedConfiguration); - GivenThePeersAre(peers); - GivenThereIsAConfiguration(configuration); - GivenFiveServersAreRunning(); - await GivenIHaveAnOcelotToken("/administration"); - GivenIHaveAddedATokenToMyRequest(); - await WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration); - await ThenTheCommandIsReplicatedToAllStateMachines(command); - } - - private void GivenThePeersAre(List peers) - { - FilePeers filePeers = new FilePeers(); - filePeers.Peers.AddRange(peers); - var json = JsonConvert.SerializeObject(filePeers); - File.WriteAllText("peers.json", json); - _httpClient = new HttpClient(); - var ocelotBaseUrl = peers[0].HostAndPort; - _httpClient.BaseAddress = new Uri(ocelotBaseUrl); - } - - private async Task WhenISendACommandIntoTheCluster(UpdateFileConfiguration command) - { - async Task SendCommand() - { - try - { - var p = _peers.Peers.First(); - var json = JsonConvert.SerializeObject(command, new JsonSerializerSettings() - { - TypeNameHandling = TypeNameHandling.All - }); - var httpContent = new StringContent(json); - httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - using (var httpClient = new HttpClient()) - { - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - var response = await httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent); - response.EnsureSuccessStatusCode(); - var content = await response.Content.ReadAsStringAsync(); - - var errorResult = JsonConvert.DeserializeObject>(content); - - if (!string.IsNullOrEmpty(errorResult.Error)) - { - return false; - } - - var okResult = JsonConvert.DeserializeObject>(content); - - if (okResult.Command.Configuration.ReRoutes.Count == 2) - { - return true; - } - } - - return false; - } - catch (Exception e) - { - Console.WriteLine(e); - return false; - } - } - - var commandSent = await Wait.WaitFor(40000).Until(async () => - { - var result = await SendCommand(); - Thread.Sleep(1000); - return result; - }); - - commandSent.ShouldBeTrue(); - } - - private async Task ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds) - { - async Task CommandCalledOnAllStateMachines() - { - try - { - var passed = 0; - foreach (var peer in _peers.Peers) - { - var path = $"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"; - using (var connection = new SqliteConnection($"Data Source={path};")) - { - connection.Open(); - var sql = @"select count(id) from logs"; - using (var command = new SqliteCommand(sql, connection)) - { - var index = Convert.ToInt32(command.ExecuteScalar()); - index.ShouldBe(1); - } - } - - _httpClientForAssertions.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - var result = await _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration"); - var json = await result.Content.ReadAsStringAsync(); - var response = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); - response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.Configuration.GlobalConfiguration.RequestIdKey); - response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host); - response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Port); - - for (var i = 0; i < response.ReRoutes.Count; i++) - { - for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++) - { - var res = response.ReRoutes[i].DownstreamHostAndPorts[j]; - var expected = expecteds.Configuration.ReRoutes[i].DownstreamHostAndPorts[j]; - res.Host.ShouldBe(expected.Host); - res.Port.ShouldBe(expected.Port); - } - - response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.Configuration.ReRoutes[i].DownstreamPathTemplate); - response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.Configuration.ReRoutes[i].DownstreamScheme); - response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.Configuration.ReRoutes[i].UpstreamPathTemplate); - response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.Configuration.ReRoutes[i].UpstreamHttpMethod); - } - - passed++; - } - - return passed == 5; - } - catch (Exception e) - { - //_output.WriteLine($"{e.Message}, {e.StackTrace}"); - Console.WriteLine(e); - return false; - } - } - - var commandOnAllStateMachines = await Wait.WaitFor(40000).Until(async () => - { - var result = await CommandCalledOnAllStateMachines(); - Thread.Sleep(1000); - return result; - }); - - commandOnAllStateMachines.ShouldBeTrue(); - } - - private async Task WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) - { - async Task SendCommand() - { - var json = JsonConvert.SerializeObject(updatedConfiguration); - - var content = new StringContent(json); - - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - - _response = await _httpClient.PostAsync(url, content); - - var responseContent = await _response.Content.ReadAsStringAsync(); - - if (responseContent == "There was a problem. This error message sucks raise an issue in GitHub.") - { - return false; - } - - if (string.IsNullOrEmpty(responseContent)) - { - return false; - } - - return _response.IsSuccessStatusCode; - } - - var commandSent = await Wait.WaitFor(40000).Until(async () => - { - var result = await SendCommand(); - Thread.Sleep(1000); - return result; - }); - - commandSent.ShouldBeTrue(); - } - - private void GivenIHaveAddedATokenToMyRequest() - { - _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); - } - - private async Task GivenIHaveAnOcelotToken(string adminPath) - { - async Task AddToken() - { - try - { - var tokenUrl = $"{adminPath}/connect/token"; - var formData = new List> - { - new KeyValuePair("client_id", "admin"), - new KeyValuePair("client_secret", "secret"), - new KeyValuePair("scope", "admin"), - new KeyValuePair("grant_type", "client_credentials") - }; - var content = new FormUrlEncodedContent(formData); - - var response = await _httpClient.PostAsync(tokenUrl, content); - var responseContent = await response.Content.ReadAsStringAsync(); - if (!response.IsSuccessStatusCode) - { - return false; - } - - _token = JsonConvert.DeserializeObject(responseContent); - var configPath = $"{adminPath}/.well-known/openid-configuration"; - response = await _httpClient.GetAsync(configPath); - return response.IsSuccessStatusCode; - } - catch (Exception) - { - return false; - } - } - - var addToken = await Wait.WaitFor(40000).Until(async () => - { - var result = await AddToken(); - Thread.Sleep(1000); - return result; - }); - - addToken.ShouldBeTrue(); - } - - private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) - { - var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - var text = File.ReadAllText(configurationPath); - - configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; - - if (File.Exists(configurationPath)) - { - File.Delete(configurationPath); - } - - File.WriteAllText(configurationPath, jsonConfiguration); - - text = File.ReadAllText(configurationPath); - } - - private void GivenAServerIsRunning(string url) - { - lock (_lock) - { - IWebHostBuilder webHostBuilder = new WebHostBuilder(); - webHostBuilder.UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddJsonFile("peers.json", optional: true, reloadOnChange: false); -#pragma warning disable CS0618 - config.AddOcelotBaseUrl(url); -#pragma warning restore CS0618 - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddSingleton(new NodeId(url)); - x - .AddOcelot() - .AddAdministration("/administration", "secret") - .AddRafty(); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - - var builder = webHostBuilder.Build(); - builder.Start(); - - _webHostBuilders.Add(webHostBuilder); - _builders.Add(builder); - } - } - - private void GivenFiveServersAreRunning() - { - var bytes = File.ReadAllText("peers.json"); - _peers = JsonConvert.DeserializeObject(bytes); - - foreach (var peer in _peers.Peers) - { - File.Delete(peer.HostAndPort.Replace("/", "").Replace(":", "")); - File.Delete($"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"); - var thread = new Thread(() => GivenAServerIsRunning(peer.HostAndPort)); - thread.Start(); - _threads.Add(thread); - } - } - - public void Dispose() - { - foreach (var builder in _builders) - { - builder?.Dispose(); - } - - foreach (var peer in _peers.Peers) - { - try - { - File.Delete(peer.HostAndPort.Replace("/", "").Replace(":", "")); - File.Delete($"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - } - } -} +namespace Ocelot.IntegrationTests +{ + using Administration; + using Configuration.File; + using DependencyInjection; + using Microsoft.AspNetCore.Hosting; + using Microsoft.Data.Sqlite; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Middleware; + using Newtonsoft.Json; + using Ocelot.Provider.Rafty; + using Rafty.Infrastructure; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Threading; + using System.Threading.Tasks; + using Xunit; + using Xunit.Abstractions; + using Wait = Rafty.Infrastructure.Wait; + + public class RaftTests : IDisposable + { + private readonly List _builders; + private readonly List _webHostBuilders; + private readonly List _threads; + private FilePeers _peers; + private HttpClient _httpClient; + private readonly HttpClient _httpClientForAssertions; + private BearerToken _token; + private HttpResponseMessage _response; + private static readonly object _lock = new object(); + private ITestOutputHelper _output; + + public RaftTests(ITestOutputHelper output) + { + _output = output; + _httpClientForAssertions = new HttpClient(); + _webHostBuilders = new List(); + _builders = new List(); + _threads = new List(); + } + + [Fact(Skip = "Still not stable, more work required in rafty..")] + public async Task should_persist_command_to_five_servers() + { + var peers = new List + { + new FilePeer {HostAndPort = "http://localhost:5000"}, + + new FilePeer {HostAndPort = "http://localhost:5001"}, + + new FilePeer {HostAndPort = "http://localhost:5002"}, + + new FilePeer {HostAndPort = "http://localhost:5003"}, + + new FilePeer {HostAndPort = "http://localhost:5004"} + }; + + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + } + }; + + var updatedConfiguration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + }, + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "127.0.0.1", + Port = 80, + } + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/geoffrey", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/" + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "123.123.123", + Port = 443, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/blooper/{productId}", + UpstreamHttpMethod = new List { "post" }, + UpstreamPathTemplate = "/test" + } + } + }; + + var command = new UpdateFileConfiguration(updatedConfiguration); + GivenThePeersAre(peers); + GivenThereIsAConfiguration(configuration); + GivenFiveServersAreRunning(); + await GivenIHaveAnOcelotToken("/administration"); + await WhenISendACommandIntoTheCluster(command); + Thread.Sleep(5000); + await ThenTheCommandIsReplicatedToAllStateMachines(command); + } + + [Fact(Skip = "Still not stable, more work required in rafty..")] + public async Task should_persist_command_to_five_servers_when_using_administration_api() + { + var peers = new List + { + new FilePeer {HostAndPort = "http://localhost:5005"}, + + new FilePeer {HostAndPort = "http://localhost:5006"}, + + new FilePeer {HostAndPort = "http://localhost:5007"}, + + new FilePeer {HostAndPort = "http://localhost:5008"}, + + new FilePeer {HostAndPort = "http://localhost:5009"} + }; + + var configuration = new FileConfiguration + { + }; + + var updatedConfiguration = new FileConfiguration + { + Routes = new List() + { + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "127.0.0.1", + Port = 80, + } + }, + DownstreamScheme = "http", + DownstreamPathTemplate = "/geoffrey", + UpstreamHttpMethod = new List { "get" }, + UpstreamPathTemplate = "/" + }, + new FileRoute() + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "123.123.123", + Port = 443, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/blooper/{productId}", + UpstreamHttpMethod = new List { "post" }, + UpstreamPathTemplate = "/test" + } + } + }; + + var command = new UpdateFileConfiguration(updatedConfiguration); + GivenThePeersAre(peers); + GivenThereIsAConfiguration(configuration); + GivenFiveServersAreRunning(); + await GivenIHaveAnOcelotToken("/administration"); + GivenIHaveAddedATokenToMyRequest(); + await WhenIPostOnTheApiGateway("/administration/configuration", updatedConfiguration); + await ThenTheCommandIsReplicatedToAllStateMachines(command); + } + + private void GivenThePeersAre(List peers) + { + FilePeers filePeers = new FilePeers(); + filePeers.Peers.AddRange(peers); + var json = JsonConvert.SerializeObject(filePeers); + File.WriteAllText("peers.json", json); + _httpClient = new HttpClient(); + var ocelotBaseUrl = peers[0].HostAndPort; + _httpClient.BaseAddress = new Uri(ocelotBaseUrl); + } + + private async Task WhenISendACommandIntoTheCluster(UpdateFileConfiguration command) + { + async Task SendCommand() + { + try + { + var p = _peers.Peers.First(); + var json = JsonConvert.SerializeObject(command, new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }); + var httpContent = new StringContent(json); + httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + using (var httpClient = new HttpClient()) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + var response = await httpClient.PostAsync($"{p.HostAndPort}/administration/raft/command", httpContent); + response.EnsureSuccessStatusCode(); + var content = await response.Content.ReadAsStringAsync(); + + var errorResult = JsonConvert.DeserializeObject>(content); + + if (!string.IsNullOrEmpty(errorResult.Error)) + { + return false; + } + + var okResult = JsonConvert.DeserializeObject>(content); + + if (okResult.Command.Configuration.Routes.Count == 2) + { + return true; + } + } + + return false; + } + catch (Exception e) + { + Console.WriteLine(e); + return false; + } + } + + var commandSent = await Wait.WaitFor(40000).Until(async () => + { + var result = await SendCommand(); + Thread.Sleep(1000); + return result; + }); + + commandSent.ShouldBeTrue(); + } + + private async Task ThenTheCommandIsReplicatedToAllStateMachines(UpdateFileConfiguration expecteds) + { + async Task CommandCalledOnAllStateMachines() + { + try + { + var passed = 0; + foreach (var peer in _peers.Peers) + { + var path = $"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"; + using (var connection = new SqliteConnection($"Data Source={path};")) + { + connection.Open(); + var sql = @"select count(id) from logs"; + using (var command = new SqliteCommand(sql, connection)) + { + var index = Convert.ToInt32(command.ExecuteScalar()); + index.ShouldBe(1); + } + } + + _httpClientForAssertions.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + var result = await _httpClientForAssertions.GetAsync($"{peer.HostAndPort}/administration/configuration"); + var json = await result.Content.ReadAsStringAsync(); + var response = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); + response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.Configuration.GlobalConfiguration.RequestIdKey); + response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Host); + response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.Configuration.GlobalConfiguration.ServiceDiscoveryProvider.Port); + + for (var i = 0; i < response.Routes.Count; i++) + { + for (var j = 0; j < response.Routes[i].DownstreamHostAndPorts.Count; j++) + { + var res = response.Routes[i].DownstreamHostAndPorts[j]; + var expected = expecteds.Configuration.Routes[i].DownstreamHostAndPorts[j]; + res.Host.ShouldBe(expected.Host); + res.Port.ShouldBe(expected.Port); + } + + response.Routes[i].DownstreamPathTemplate.ShouldBe(expecteds.Configuration.Routes[i].DownstreamPathTemplate); + response.Routes[i].DownstreamScheme.ShouldBe(expecteds.Configuration.Routes[i].DownstreamScheme); + response.Routes[i].UpstreamPathTemplate.ShouldBe(expecteds.Configuration.Routes[i].UpstreamPathTemplate); + response.Routes[i].UpstreamHttpMethod.ShouldBe(expecteds.Configuration.Routes[i].UpstreamHttpMethod); + } + + passed++; + } + + return passed == 5; + } + catch (Exception e) + { + //_output.WriteLine($"{e.Message}, {e.StackTrace}"); + Console.WriteLine(e); + return false; + } + } + + var commandOnAllStateMachines = await Wait.WaitFor(40000).Until(async () => + { + var result = await CommandCalledOnAllStateMachines(); + Thread.Sleep(1000); + return result; + }); + + commandOnAllStateMachines.ShouldBeTrue(); + } + + private async Task WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) + { + async Task SendCommand() + { + var json = JsonConvert.SerializeObject(updatedConfiguration); + + var content = new StringContent(json); + + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + + _response = await _httpClient.PostAsync(url, content); + + var responseContent = await _response.Content.ReadAsStringAsync(); + + if (responseContent == "There was a problem. This error message sucks raise an issue in GitHub.") + { + return false; + } + + if (string.IsNullOrEmpty(responseContent)) + { + return false; + } + + return _response.IsSuccessStatusCode; + } + + var commandSent = await Wait.WaitFor(40000).Until(async () => + { + var result = await SendCommand(); + Thread.Sleep(1000); + return result; + }); + + commandSent.ShouldBeTrue(); + } + + private void GivenIHaveAddedATokenToMyRequest() + { + _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken); + } + + private async Task GivenIHaveAnOcelotToken(string adminPath) + { + async Task AddToken() + { + try + { + var tokenUrl = $"{adminPath}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "admin"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "admin"), + new KeyValuePair("grant_type", "client_credentials") + }; + var content = new FormUrlEncodedContent(formData); + + var response = await _httpClient.PostAsync(tokenUrl, content); + var responseContent = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + return false; + } + + _token = JsonConvert.DeserializeObject(responseContent); + var configPath = $"{adminPath}/.well-known/openid-configuration"; + response = await _httpClient.GetAsync(configPath); + return response.IsSuccessStatusCode; + } + catch (Exception) + { + return false; + } + } + + var addToken = await Wait.WaitFor(40000).Until(async () => + { + var result = await AddToken(); + Thread.Sleep(1000); + return result; + }); + + addToken.ShouldBeTrue(); + } + + private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) + { + var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + var text = File.ReadAllText(configurationPath); + + configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + text = File.ReadAllText(configurationPath); + } + + private void GivenAServerIsRunning(string url) + { + lock (_lock) + { + IWebHostBuilder webHostBuilder = new WebHostBuilder(); + webHostBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddJsonFile("peers.json", optional: true, reloadOnChange: false); +#pragma warning disable CS0618 + config.AddOcelotBaseUrl(url); +#pragma warning restore CS0618 + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddSingleton(new NodeId(url)); + x + .AddOcelot() + .AddAdministration("/administration", "secret") + .AddRafty(); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + var builder = webHostBuilder.Build(); + builder.Start(); + + _webHostBuilders.Add(webHostBuilder); + _builders.Add(builder); + } + } + + private void GivenFiveServersAreRunning() + { + var bytes = File.ReadAllText("peers.json"); + _peers = JsonConvert.DeserializeObject(bytes); + + foreach (var peer in _peers.Peers) + { + File.Delete(peer.HostAndPort.Replace("/", "").Replace(":", "")); + File.Delete($"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"); + var thread = new Thread(() => GivenAServerIsRunning(peer.HostAndPort)); + thread.Start(); + _threads.Add(thread); + } + } + + public void Dispose() + { + foreach (var builder in _builders) + { + builder?.Dispose(); + } + + foreach (var peer in _peers.Peers) + { + try + { + File.Delete(peer.HostAndPort.Replace("/", "").Replace(":", "")); + File.Delete($"{peer.HostAndPort.Replace("/", "").Replace(":", "")}.db"); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } + } +} diff --git a/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs b/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs index d99eb2e08..83a51ce90 100644 --- a/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs +++ b/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs @@ -42,9 +42,9 @@ public void should_return_same_response_for_each_different_header_under_load_to_ { var configuration = new FileConfiguration { - ReRoutes = new List + Routes = new List { - new FileReRoute + new FileRoute { DownstreamPathTemplate = "/", DownstreamScheme = "http", diff --git a/test/Ocelot.IntegrationTests/ocelot.json b/test/Ocelot.IntegrationTests/ocelot.json index a4ca4ce5c..39b71c1bb 100644 --- a/test/Ocelot.IntegrationTests/ocelot.json +++ b/test/Ocelot.IntegrationTests/ocelot.json @@ -1,57 +1,57 @@ -{ - "ReRoutes": [ - { - "DownstreamPathTemplate": "/", - "UpstreamPathTemplate": "/", - "UpstreamHttpMethod": "Get", - "AuthenticationOptions": { - "Provider": null, - "ProviderRootUrl": null, - "ApiName": null, - "RequireHttps": false, - "AllowedScopes": [], - "ApiSecret": null - }, - "AddHeadersToRequest": {}, - "AddClaimsToRequest": {}, - "RouteClaimsRequirement": {}, - "AddQueriesToRequest": {}, - "RequestIdKey": null, - "FileCacheOptions": { "TtlSeconds": 0 }, - "ReRouteIsCaseSensitive": false, - "ServiceName": null, - "DownstreamScheme": "http", - "DownstreamHost": "localhost", - "DownstreamPort": 51879, - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 0, - "DurationOfBreak": 0, - "TimeoutValue": 0 - }, - "LoadBalancer": null, - "RateLimitOptions": { - "ClientWhitelist": [], - "EnableRateLimiting": false, - "Period": null, - "PeriodTimespan": 0.0, - "Limit": 0 - } - } - ], - "GlobalConfiguration": { - "RequestIdKey": null, - "ServiceDiscoveryProvider": { - "Provider": null, - "Host": null, - "Port": 0 - }, - "AdministrationPath": null, - "RateLimitOptions": { - "ClientIdHeader": "ClientId", - "QuotaExceededMessage": null, - "RateLimitCounterPrefix": "ocelot", - "DisableRateLimitHeaders": false, - "HttpStatusCode": 429 - } - } -} +{ + "Routes": [ + { + "DownstreamPathTemplate": "/", + "UpstreamPathTemplate": "/", + "UpstreamHttpMethod": "Get", + "AuthenticationOptions": { + "Provider": null, + "ProviderRootUrl": null, + "ApiName": null, + "RequireHttps": false, + "AllowedScopes": [], + "ApiSecret": null + }, + "AddHeadersToRequest": {}, + "AddClaimsToRequest": {}, + "RouteClaimsRequirement": {}, + "AddQueriesToRequest": {}, + "RequestIdKey": null, + "FileCacheOptions": { "TtlSeconds": 0 }, + "RouteIsCaseSensitive": false, + "ServiceName": null, + "DownstreamScheme": "http", + "DownstreamHost": "localhost", + "DownstreamPort": 51879, + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 0, + "DurationOfBreak": 0, + "TimeoutValue": 0 + }, + "LoadBalancer": null, + "RateLimitOptions": { + "ClientWhitelist": [], + "EnableRateLimiting": false, + "Period": null, + "PeriodTimespan": 0.0, + "Limit": 0 + } + } + ], + "GlobalConfiguration": { + "RequestIdKey": null, + "ServiceDiscoveryProvider": { + "Provider": null, + "Host": null, + "Port": 0 + }, + "AdministrationPath": null, + "RateLimitOptions": { + "ClientIdHeader": "ClientId", + "QuotaExceededMessage": null, + "RateLimitCounterPrefix": "ocelot", + "DisableRateLimitHeaders": false, + "HttpStatusCode": 429 + } + } +} diff --git a/test/Ocelot.ManualTest/ocelot.json b/test/Ocelot.ManualTest/ocelot.json index 7bb8978bd..df8b1f976 100644 --- a/test/Ocelot.ManualTest/ocelot.json +++ b/test/Ocelot.ManualTest/ocelot.json @@ -1,345 +1,345 @@ -{ - "ReRoutes": [ - { - "DownstreamPathTemplate": "/profile", - "DownstreamScheme": "http", - "UpstreamPathTemplate": "/profile", - "UpstreamHttpMethod": [ "Get" ], - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 5001 - } - ], - "QoSOptions": { - "TimeoutValue": 360000 - } - }, - { - "DownstreamPathTemplate": "/api/v1/todo/", - "DownstreamScheme": "http", - "UpstreamPathTemplate": "/api/v1/todo/", - "UpstreamHttpMethod": [ "Get", "Post" ], - "DownstreamHostAndPorts": [ - { - "Host": "lxtodo.azurewebsites.net", - "Port": 80 - } - - ], - "DownstreamHeaderTransform": { - "Location": "{DownstreamBaseUrl}, {BaseUrl}" - } - }, - { - "DownstreamPathTemplate": "/api/values", - "DownstreamScheme": "https", - "UpstreamPathTemplate": "/api/values", - "UpstreamHttpMethod": [ "Get" ], - "DownstreamHostAndPorts": [ - { - "Host": "testapivalues.azurewebsites.net", - "Port": 443 - } - ] - }, - { - "DownstreamPathTemplate": "/", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "localhost", - "Port": 52876 - } - ], - "UpstreamPathTemplate": "/identityserverexample", - "UpstreamHttpMethod": [ "Get" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "AuthenticationOptions": { - "AuthenticationProviderKey": "TestKey", - "AllowedScopes": [ - "openid", - "offline_access" - ] - }, - "AddHeadersToRequest": { - "CustomerId": "Claims[CustomerId] > value", - "LocationId": "Claims[LocationId] > value", - "UserType": "Claims[sub] > value[0] > |", - "UserId": "Claims[sub] > value[1] > |" - }, - "AddClaimsToRequest": { - "CustomerId": "Claims[CustomerId] > value", - "LocationId": "Claims[LocationId] > value", - "UserType": "Claims[sub] > value[0] > |", - "UserId": "Claims[sub] > value[1] > |" - }, - "AddQueriesToRequest": { - "CustomerId": "Claims[CustomerId] > value", - "LocationId": "Claims[LocationId] > value", - "UserType": "Claims[sub] > value[0] > |", - "UserId": "Claims[sub] > value[1] > |" - }, - "RouteClaimsRequirement": { - "UserType": "registered" - }, - "RequestIdKey": "OcRequestId" - }, - { - "DownstreamPathTemplate": "/posts", - "DownstreamScheme": "https", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 443 - } - ], - "UpstreamPathTemplate": "/posts", - "UpstreamHttpMethod": [ "Get" ], - "HttpHandlerOptions": { - "AllowAutoRedirect": true, - "UseCookieContainer": true - }, - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/posts/{postId}", - "UpstreamHttpMethod": [ "Get" ], - "RequestIdKey": "ReRouteRequestId", - "HttpHandlerOptions": { - "AllowAutoRedirect": true, - "UseCookieContainer": true, - "UseTracing": true, - "UseProxy": true - }, - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}/comments", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/posts/{postId}/comments", - "UpstreamHttpMethod": [ "Get" ], - "HttpHandlerOptions": { - "AllowAutoRedirect": true, - "UseCookieContainer": true, - "UseTracing": false - }, - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/comments", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/comments", - "UpstreamHttpMethod": [ "Get" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/posts", - "UpstreamHttpMethod": [ "Post" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/posts/{postId}", - "UpstreamHttpMethod": [ "Put" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/posts/{postId}", - "UpstreamHttpMethod": [ "Patch" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/posts/{postId}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/posts/{postId}", - "UpstreamHttpMethod": [ "Delete" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/api/products", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/products", - "UpstreamHttpMethod": [ "Get" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/products/{productId}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/products/{productId}", - "UpstreamHttpMethod": [ "Get" ], - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/api/products", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/products", - "UpstreamHttpMethod": [ "Post" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - } - }, - { - "DownstreamPathTemplate": "/api/products/{productId}", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/products/{productId}", - "UpstreamHttpMethod": [ "Put" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/posts", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "jsonplaceholder.typicode.com", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/posts/", - "UpstreamHttpMethod": [ "Get" ], - "QoSOptions": { - "ExceptionsAllowedBeforeBreaking": 3, - "DurationOfBreak": 10, - "TimeoutValue": 5000 - }, - "FileCacheOptions": { "TtlSeconds": 15 } - }, - { - "DownstreamPathTemplate": "/", - "DownstreamScheme": "http", - "DownstreamHostAndPorts": [ - { - "Host": "www.bbc.co.uk", - "Port": 80 - } - ], - "UpstreamPathTemplate": "/bbc/", - "UpstreamHttpMethod": [ "Get" ] - } - ], - - "GlobalConfiguration": { - "RequestIdKey": "ot-traceid" - } -} +{ + "Routes": [ + { + "DownstreamPathTemplate": "/profile", + "DownstreamScheme": "http", + "UpstreamPathTemplate": "/profile", + "UpstreamHttpMethod": [ "Get" ], + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 5001 + } + ], + "QoSOptions": { + "TimeoutValue": 360000 + } + }, + { + "DownstreamPathTemplate": "/api/v1/todo/", + "DownstreamScheme": "http", + "UpstreamPathTemplate": "/api/v1/todo/", + "UpstreamHttpMethod": [ "Get", "Post" ], + "DownstreamHostAndPorts": [ + { + "Host": "lxtodo.azurewebsites.net", + "Port": 80 + } + + ], + "DownstreamHeaderTransform": { + "Location": "{DownstreamBaseUrl}, {BaseUrl}" + } + }, + { + "DownstreamPathTemplate": "/api/values", + "DownstreamScheme": "https", + "UpstreamPathTemplate": "/api/values", + "UpstreamHttpMethod": [ "Get" ], + "DownstreamHostAndPorts": [ + { + "Host": "testapivalues.azurewebsites.net", + "Port": 443 + } + ] + }, + { + "DownstreamPathTemplate": "/", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "localhost", + "Port": 52876 + } + ], + "UpstreamPathTemplate": "/identityserverexample", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "AuthenticationOptions": { + "AuthenticationProviderKey": "TestKey", + "AllowedScopes": [ + "openid", + "offline_access" + ] + }, + "AddHeadersToRequest": { + "CustomerId": "Claims[CustomerId] > value", + "LocationId": "Claims[LocationId] > value", + "UserType": "Claims[sub] > value[0] > |", + "UserId": "Claims[sub] > value[1] > |" + }, + "AddClaimsToRequest": { + "CustomerId": "Claims[CustomerId] > value", + "LocationId": "Claims[LocationId] > value", + "UserType": "Claims[sub] > value[0] > |", + "UserId": "Claims[sub] > value[1] > |" + }, + "AddQueriesToRequest": { + "CustomerId": "Claims[CustomerId] > value", + "LocationId": "Claims[LocationId] > value", + "UserType": "Claims[sub] > value[0] > |", + "UserId": "Claims[sub] > value[1] > |" + }, + "RouteClaimsRequirement": { + "UserType": "registered" + }, + "RequestIdKey": "OcRequestId" + }, + { + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "https", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 443 + } + ], + "UpstreamPathTemplate": "/posts", + "UpstreamHttpMethod": [ "Get" ], + "HttpHandlerOptions": { + "AllowAutoRedirect": true, + "UseCookieContainer": true + }, + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/posts/{postId}", + "UpstreamHttpMethod": [ "Get" ], + "RequestIdKey": "RouteRequestId", + "HttpHandlerOptions": { + "AllowAutoRedirect": true, + "UseCookieContainer": true, + "UseTracing": true, + "UseProxy": true + }, + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}/comments", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/posts/{postId}/comments", + "UpstreamHttpMethod": [ "Get" ], + "HttpHandlerOptions": { + "AllowAutoRedirect": true, + "UseCookieContainer": true, + "UseTracing": false + }, + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/comments", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/comments", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/posts", + "UpstreamHttpMethod": [ "Post" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/posts/{postId}", + "UpstreamHttpMethod": [ "Put" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/posts/{postId}", + "UpstreamHttpMethod": [ "Patch" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/posts/{postId}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/posts/{postId}", + "UpstreamHttpMethod": [ "Delete" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/api/products", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/products", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/products/{productId}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/products/{productId}", + "UpstreamHttpMethod": [ "Get" ], + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/api/products", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/products", + "UpstreamHttpMethod": [ "Post" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + } + }, + { + "DownstreamPathTemplate": "/api/products/{productId}", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/products/{productId}", + "UpstreamHttpMethod": [ "Put" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/posts", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "jsonplaceholder.typicode.com", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/posts/", + "UpstreamHttpMethod": [ "Get" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 3, + "DurationOfBreak": 10, + "TimeoutValue": 5000 + }, + "FileCacheOptions": { "TtlSeconds": 15 } + }, + { + "DownstreamPathTemplate": "/", + "DownstreamScheme": "http", + "DownstreamHostAndPorts": [ + { + "Host": "www.bbc.co.uk", + "Port": 80 + } + ], + "UpstreamPathTemplate": "/bbc/", + "UpstreamHttpMethod": [ "Get" ] + } + ], + + "GlobalConfiguration": { + "RequestIdKey": "ot-traceid" + } +} diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs index 9cdea86c7..ce5549bd5 100644 --- a/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authentication/AuthenticationMiddlewareTests.cs @@ -1,119 +1,119 @@ -using Xunit; - -[assembly: CollectionBehavior(DisableTestParallelization = true)] - -namespace Ocelot.UnitTests.Authentication -{ - using Microsoft.AspNetCore.Http; - using Moq; - using Ocelot.Authentication.Middleware; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Logging; - using Ocelot.Middleware; - using Shouldly; - using System.Collections.Generic; - using System.IO; - using System.Text; - using System.Threading.Tasks; - using Ocelot.Infrastructure.RequestData; - using TestStack.BDDfy; +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] + +namespace Ocelot.UnitTests.Authentication +{ + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Authentication.Middleware; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Logging; + using Ocelot.Middleware; + using Shouldly; + using System.Collections.Generic; + using System.IO; + using System.Text; + using System.Threading.Tasks; + using Ocelot.Infrastructure.RequestData; + using TestStack.BDDfy; using Xunit; using Ocelot.DownstreamRouteFinder.Middleware; - public class AuthenticationMiddlewareTests - { - private AuthenticationMiddleware _middleware; - private readonly Mock _factory; - private Mock _logger; - private RequestDelegate _next; - private HttpContext _httpContext; - private Mock _repo; - - public AuthenticationMiddlewareTests() - { - _repo = new Mock(); - _httpContext = new DefaultHttpContext(); - _factory = new Mock(); - _logger = new Mock(); - _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - } - - [Fact] - public void should_call_next_middleware_if_route_is_not_authenticated() - { - this.Given(x => GivenTheDownStreamRouteIs( - new DownstreamReRouteBuilder().WithUpstreamHttpMethod(new List { "Get" }).Build())) - .And(x => GivenTheTestServerPipelineIsConfigured()) - .When(x => WhenICallTheMiddleware()) - .Then(x => ThenTheUserIsAuthenticated()) - .BDDfy(); - } - - [Fact] - public void should_call_next_middleware_if_route_is_using_options_method() - { - this.Given(x => GivenTheDownStreamRouteIs( - new DownstreamReRouteBuilder() - .WithUpstreamHttpMethod(new List { "Options" }) - .WithIsAuthenticated(true) - .Build())) - .And(x => GivenTheRequestIsUsingOptionsMethod()) - .When(x => WhenICallTheMiddleware()) - .Then(x => ThenTheUserIsAuthenticated()) - .BDDfy(); - } - - private void WhenICallTheMiddleware() - { - _next = (context) => - { - byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); - var stream = new MemoryStream(byteArray); - _httpContext.Response.Body = stream; - return Task.CompletedTask; - }; - _middleware = new AuthenticationMiddleware(_next, _factory.Object); - _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); - } - - private void GivenTheTestServerPipelineIsConfigured() - { - _next = (context) => - { - byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); - var stream = new MemoryStream(byteArray); - _httpContext.Response.Body = stream; - return Task.CompletedTask; - }; - } - - private void GivenTheRequestIsUsingOptionsMethod() - { - _httpContext.Request.Method = "OPTIONS"; - } - - private void ThenTheUserIsAuthenticated() - { - var content = _httpContext.Response.Body.AsString(); - content.ShouldBe("The user is authenticated"); - } - - private void GivenTheDownStreamRouteIs(DownstreamReRoute downstreamRoute) + public class AuthenticationMiddlewareTests + { + private AuthenticationMiddleware _middleware; + private readonly Mock _factory; + private Mock _logger; + private RequestDelegate _next; + private HttpContext _httpContext; + private Mock _repo; + + public AuthenticationMiddlewareTests() + { + _repo = new Mock(); + _httpContext = new DefaultHttpContext(); + _factory = new Mock(); + _logger = new Mock(); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + } + + [Fact] + public void should_call_next_middleware_if_route_is_not_authenticated() + { + this.Given(x => GivenTheDownStreamRouteIs( + new DownstreamRouteBuilder().WithUpstreamHttpMethod(new List { "Get" }).Build())) + .And(x => GivenTheTestServerPipelineIsConfigured()) + .When(x => WhenICallTheMiddleware()) + .Then(x => ThenTheUserIsAuthenticated()) + .BDDfy(); + } + + [Fact] + public void should_call_next_middleware_if_route_is_using_options_method() + { + this.Given(x => GivenTheDownStreamRouteIs( + new DownstreamRouteBuilder() + .WithUpstreamHttpMethod(new List { "Options" }) + .WithIsAuthenticated(true) + .Build())) + .And(x => GivenTheRequestIsUsingOptionsMethod()) + .When(x => WhenICallTheMiddleware()) + .Then(x => ThenTheUserIsAuthenticated()) + .BDDfy(); + } + + private void WhenICallTheMiddleware() + { + _next = (context) => + { + byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); + var stream = new MemoryStream(byteArray); + _httpContext.Response.Body = stream; + return Task.CompletedTask; + }; + _middleware = new AuthenticationMiddleware(_next, _factory.Object); + _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); + } + + private void GivenTheTestServerPipelineIsConfigured() + { + _next = (context) => + { + byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); + var stream = new MemoryStream(byteArray); + _httpContext.Response.Body = stream; + return Task.CompletedTask; + }; + } + + private void GivenTheRequestIsUsingOptionsMethod() + { + _httpContext.Request.Method = "OPTIONS"; + } + + private void ThenTheUserIsAuthenticated() + { + var content = _httpContext.Response.Body.AsString(); + content.ShouldBe("The user is authenticated"); + } + + private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + { + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute); + } + } + + public static class StreamExtensions + { + public static string AsString(this Stream stream) { - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute); - } - } - - public static class StreamExtensions - { - public static string AsString(this Stream stream) - { - using (var reader = new StreamReader(stream)) - { - string text = reader.ReadToEnd(); - return text; - } - } - } -} + using (var reader = new StreamReader(stream)) + { + string text = reader.ReadToEnd(); + return text; + } + } + } +} diff --git a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs index 737b8b2d5..4c72849e7 100644 --- a/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Authorization/AuthorisationMiddlewareTests.cs @@ -44,7 +44,7 @@ public AuthorisationMiddlewareTests() public void should_call_authorisation_service() { this.Given(x => x.GivenTheDownStreamRouteIs(new List(), - new DownstreamReRouteBuilder() + new DownstreamRouteBuilder() .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().Build()) .WithIsAuthorised(true) .WithUpstreamHttpMethod(new List { "Get" }) @@ -60,10 +60,10 @@ private void WhenICallTheMiddleware() _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); } - private void GivenTheDownStreamRouteIs(List templatePlaceholderNameAndValues, DownstreamReRoute downstreamReRoute) + private void GivenTheDownStreamRouteIs(List templatePlaceholderNameAndValues, DownstreamRoute downstreamRoute) { _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(templatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(downstreamReRoute); + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute); } private void GivenTheAuthServiceReturns(Response expected) diff --git a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs index abd457579..067217e98 100644 --- a/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Cache/OutputCacheMiddlewareTests.cs @@ -1,144 +1,144 @@ -namespace Ocelot.UnitTests.Cache -{ - using Microsoft.AspNetCore.Http; - using Moq; - using Ocelot.Cache; - using Ocelot.Cache.Middleware; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.Logging; - using Ocelot.Middleware; - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Http; - using System.Threading.Tasks; - using Ocelot.Infrastructure.RequestData; - using TestStack.BDDfy; +namespace Ocelot.UnitTests.Cache +{ + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Cache; + using Ocelot.Cache.Middleware; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.UrlMatcher; + using Ocelot.Logging; + using Ocelot.Middleware; + using System; + using System.Collections.Generic; + using System.Net; + using System.Net.Http; + using System.Threading.Tasks; + using Ocelot.Infrastructure.RequestData; + using TestStack.BDDfy; using Xunit; using Ocelot.DownstreamRouteFinder.Middleware; - public class OutputCacheMiddlewareTests - { - private readonly Mock> _cache; - private readonly Mock _loggerFactory; - private Mock _logger; - private OutputCacheMiddleware _middleware; - private readonly RequestDelegate _next; - private readonly ICacheKeyGenerator _cacheKeyGenerator; - private CachedResponse _response; - private HttpContext _httpContext; - private Mock _repo; - - public OutputCacheMiddlewareTests() - { - _repo = new Mock(); - _httpContext = new DefaultHttpContext(); - _cache = new Mock>(); - _loggerFactory = new Mock(); - _logger = new Mock(); - _cacheKeyGenerator = new CacheKeyGenerator(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + public class OutputCacheMiddlewareTests + { + private readonly Mock> _cache; + private readonly Mock _loggerFactory; + private Mock _logger; + private OutputCacheMiddleware _middleware; + private readonly RequestDelegate _next; + private readonly ICacheKeyGenerator _cacheKeyGenerator; + private CachedResponse _response; + private HttpContext _httpContext; + private Mock _repo; + + public OutputCacheMiddlewareTests() + { + _repo = new Mock(); + _httpContext = new DefaultHttpContext(); + _cache = new Mock>(); + _loggerFactory = new Mock(); + _logger = new Mock(); + _cacheKeyGenerator = new CacheKeyGenerator(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); _next = context => Task.CompletedTask; - _httpContext.Items.UpsertDownstreamRequest(new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"))); - } - - [Fact] - public void should_returned_cached_item_when_it_is_in_cache() - { - var headers = new Dictionary> - { - { "test", new List { "test" } } - }; - - var contentHeaders = new Dictionary> - { - { "content-type", new List { "application/json" } } - }; - - var cachedResponse = new CachedResponse(HttpStatusCode.OK, headers, "", contentHeaders, "some reason"); - this.Given(x => x.GivenThereIsACachedResponse(cachedResponse)) - .And(x => x.GivenTheDownstreamRouteIs()) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheCacheGetIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_returned_cached_item_when_it_is_in_cache_expires_header() - { - var contentHeaders = new Dictionary> - { - { "Expires", new List { "-1" } } - }; - - var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary>(), "", contentHeaders, "some reason"); - this.Given(x => x.GivenThereIsACachedResponse(cachedResponse)) - .And(x => x.GivenTheDownstreamRouteIs()) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheCacheGetIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_continue_with_pipeline_and_cache_response() - { - this.Given(x => x.GivenResponseIsNotCached(new HttpResponseMessage())) - .And(x => x.GivenTheDownstreamRouteIs()) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheCacheAddIsCalledCorrectly()) - .BDDfy(); - } - - private void WhenICallTheMiddleware() - { - _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cache.Object, _cacheKeyGenerator); - _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); - } - - private void GivenThereIsACachedResponse(CachedResponse response) - { - _response = response; - _cache - .Setup(x => x.Get(It.IsAny(), It.IsAny())) - .Returns(_response); - } - - private void GivenResponseIsNotCached(HttpResponseMessage responseMessage) + _httpContext.Items.UpsertDownstreamRequest(new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"))); + } + + [Fact] + public void should_returned_cached_item_when_it_is_in_cache() + { + var headers = new Dictionary> + { + { "test", new List { "test" } } + }; + + var contentHeaders = new Dictionary> + { + { "content-type", new List { "application/json" } } + }; + + var cachedResponse = new CachedResponse(HttpStatusCode.OK, headers, "", contentHeaders, "some reason"); + this.Given(x => x.GivenThereIsACachedResponse(cachedResponse)) + .And(x => x.GivenTheDownstreamRouteIs()) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheCacheGetIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_returned_cached_item_when_it_is_in_cache_expires_header() + { + var contentHeaders = new Dictionary> + { + { "Expires", new List { "-1" } } + }; + + var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary>(), "", contentHeaders, "some reason"); + this.Given(x => x.GivenThereIsACachedResponse(cachedResponse)) + .And(x => x.GivenTheDownstreamRouteIs()) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheCacheGetIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_continue_with_pipeline_and_cache_response() + { + this.Given(x => x.GivenResponseIsNotCached(new HttpResponseMessage())) + .And(x => x.GivenTheDownstreamRouteIs()) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheCacheAddIsCalledCorrectly()) + .BDDfy(); + } + + private void WhenICallTheMiddleware() { - _httpContext.Items.UpsertDownstreamResponse(new DownstreamResponse(responseMessage)); - } - - private void GivenTheDownstreamRouteIs() - { - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithIsCached(true) - .WithCacheOptions(new CacheOptions(100, "kanken")) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build(); - - var downstreamRoute = new DownstreamRoute(new List(), reRoute); + _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cache.Object, _cacheKeyGenerator); + _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); + } + + private void GivenThereIsACachedResponse(CachedResponse response) + { + _response = response; + _cache + .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .Returns(_response); + } + + private void GivenResponseIsNotCached(HttpResponseMessage responseMessage) + { + _httpContext.Items.UpsertDownstreamResponse(new DownstreamResponse(responseMessage)); + } + + private void GivenTheDownstreamRouteIs() + { + var route = new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithIsCached(true) + .WithCacheOptions(new CacheOptions(100, "kanken")) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build(); + + var downstreamRoute = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(new List(), route); _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); - } - - private void ThenTheCacheGetIsCalledCorrectly() - { - _cache - .Verify(x => x.Get(It.IsAny(), It.IsAny()), Times.Once); - } - - private void ThenTheCacheAddIsCalledCorrectly() - { - _cache - .Verify(x => x.Add(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); - } - } -} + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); + } + + private void ThenTheCacheGetIsCalledCorrectly() + { + _cache + .Verify(x => x.Get(It.IsAny(), It.IsAny()), Times.Once); + } + + private void ThenTheCacheAddIsCalledCorrectly() + { + _cache + .Verify(x => x.Add(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + } +} diff --git a/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs b/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs index ebcd6e25e..8d9bffc41 100644 --- a/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs +++ b/test/Ocelot.UnitTests/Cache/RegionCreatorTests.cs @@ -10,18 +10,18 @@ namespace Ocelot.UnitTests.Cache public class RegionCreatorTests { private string _result; - private FileReRoute _reRoute; + private FileRoute _route; [Fact] public void should_create_region() { - var reRoute = new FileReRoute + var route = new FileRoute { UpstreamHttpMethod = new List { "Get" }, UpstreamPathTemplate = "/testdummy" }; - this.Given(_ => GivenTheReRoute(reRoute)) + this.Given(_ => GivenTheRoute(route)) .When(_ => WhenICreateTheRegion()) .Then(_ => ThenTheRegionIs("Gettestdummy")) .BDDfy(); @@ -30,7 +30,7 @@ public void should_create_region() [Fact] public void should_use_region() { - var reRoute = new FileReRoute + var route = new FileRoute { FileCacheOptions = new FileCacheOptions { @@ -38,21 +38,21 @@ public void should_use_region() } }; - this.Given(_ => GivenTheReRoute(reRoute)) + this.Given(_ => GivenTheRoute(route)) .When(_ => WhenICreateTheRegion()) .Then(_ => ThenTheRegionIs("region")) .BDDfy(); - } - - private void GivenTheReRoute(FileReRoute reRoute) + } + + private void GivenTheRoute(FileRoute route) { - _reRoute = reRoute; + _route = route; } private void WhenICreateTheRegion() - { + { RegionCreator regionCreator = new RegionCreator(); - _result = regionCreator.Create(_reRoute); + _result = regionCreator.Create(_route); } private void ThenTheRegionIs(string expected) @@ -60,4 +60,4 @@ private void ThenTheRegionIs(string expected) _result.ShouldBe(expected); } } -} +} diff --git a/test/Ocelot.UnitTests/CacheManager/OutputCacheMiddlewareRealCacheTests.cs b/test/Ocelot.UnitTests/CacheManager/OutputCacheMiddlewareRealCacheTests.cs index 8d0c5b31e..9fbcb22a1 100644 --- a/test/Ocelot.UnitTests/CacheManager/OutputCacheMiddlewareRealCacheTests.cs +++ b/test/Ocelot.UnitTests/CacheManager/OutputCacheMiddlewareRealCacheTests.cs @@ -7,8 +7,8 @@ using Ocelot.Cache.CacheManager; using Ocelot.Cache.Middleware; using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder.Middleware; + using Ocelot.Configuration.Builder; + using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Logging; using Ocelot.Middleware; using Shouldly; @@ -42,7 +42,7 @@ public OutputCacheMiddlewareRealCacheTests() x.WithDictionaryHandle(); }); _cacheManager = new OcelotCacheManagerCache(cacheManagerOutputCache); - _cacheKeyGenerator = new CacheKeyGenerator(); + _cacheKeyGenerator = new CacheKeyGenerator(); _httpContext.Items.UpsertDownstreamRequest(new Ocelot.Request.Middleware.DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"))); _next = context => Task.CompletedTask; _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _cacheKeyGenerator); @@ -85,13 +85,13 @@ private void GivenResponseIsNotCached(DownstreamResponse response) private void GivenTheDownstreamRouteIs() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithIsCached(true) .WithCacheOptions(new CacheOptions(100, "kanken")) .WithUpstreamHttpMethod(new List { "Get" }) - .Build(); - - _httpContext.Items.UpsertDownstreamReRoute(reRoute); + .Build(); + + _httpContext.Items.UpsertDownstreamRoute(route); } } } diff --git a/test/Ocelot.UnitTests/Claims/ClaimsToClaimsMiddlewareTests.cs b/test/Ocelot.UnitTests/Claims/ClaimsToClaimsMiddlewareTests.cs index 29ee3f4e1..ec08da0d8 100644 --- a/test/Ocelot.UnitTests/Claims/ClaimsToClaimsMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Claims/ClaimsToClaimsMiddlewareTests.cs @@ -1,91 +1,91 @@ -using Ocelot.Middleware; - -namespace Ocelot.UnitTests.Claims -{ - using Microsoft.AspNetCore.Http; - using Moq; - using Ocelot.Claims; - using Ocelot.Claims.Middleware; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.Claims +{ + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Claims; + using Ocelot.Claims.Middleware; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder.Middleware; - using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.Logging; - using Ocelot.Responses; - using System.Collections.Generic; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class ClaimsToClaimsMiddlewareTests - { - private readonly Mock _addHeaders; - private Mock _loggerFactory; - private Mock _logger; - private readonly ClaimsToClaimsMiddleware _middleware; - private RequestDelegate _next; - private HttpContext _httpContext; - - public ClaimsToClaimsMiddlewareTests() - { - _httpContext = new DefaultHttpContext(); - _addHeaders = new Mock(); - _loggerFactory = new Mock(); - _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = context => Task.CompletedTask; - _middleware = new ClaimsToClaimsMiddleware(_next, _loggerFactory.Object, _addHeaders.Object); - } - - [Fact] - public void should_call_claims_to_request_correctly() - { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithClaimsToClaims(new List - { - new ClaimToThing("sub", "UserType", "|", 0) - }) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); - - this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheAddClaimsToRequestReturns()) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheClaimsToRequestIsCalledCorrectly()) - .BDDfy(); - } - - private void WhenICallTheMiddleware() - { - _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); - } - - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + using Ocelot.DownstreamRouteFinder.UrlMatcher; + using Ocelot.Logging; + using Ocelot.Responses; + using System.Collections.Generic; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class ClaimsToClaimsMiddlewareTests + { + private readonly Mock _addHeaders; + private Mock _loggerFactory; + private Mock _logger; + private readonly ClaimsToClaimsMiddleware _middleware; + private RequestDelegate _next; + private HttpContext _httpContext; + + public ClaimsToClaimsMiddlewareTests() + { + _httpContext = new DefaultHttpContext(); + _addHeaders = new Mock(); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = context => Task.CompletedTask; + _middleware = new ClaimsToClaimsMiddleware(_next, _loggerFactory.Object, _addHeaders.Object); + } + + [Fact] + public void should_call_claims_to_request_correctly() + { + var downstreamRoute = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithClaimsToClaims(new List + { + new ClaimToThing("sub", "UserType", "|", 0) + }) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) + .And(x => x.GivenTheAddClaimsToRequestReturns()) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheClaimsToRequestIsCalledCorrectly()) + .BDDfy(); + } + + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); + } + + private void GivenTheDownStreamRouteIs(Ocelot.DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) { _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); - } - - private void GivenTheAddClaimsToRequestReturns() - { - _addHeaders - .Setup(x => x.SetClaimsOnContext(It.IsAny>(), - It.IsAny())) - .Returns(new OkResponse()); - } - - private void ThenTheClaimsToRequestIsCalledCorrectly() - { - _addHeaders - .Verify(x => x.SetClaimsOnContext(It.IsAny>(), - It.IsAny()), Times.Once); - } - } -} + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); + } + + private void GivenTheAddClaimsToRequestReturns() + { + _addHeaders + .Setup(x => x.SetClaimsOnContext(It.IsAny>(), + It.IsAny())) + .Returns(new OkResponse()); + } + + private void ThenTheClaimsToRequestIsCalledCorrectly() + { + _addHeaders + .Verify(x => x.SetClaimsOnContext(It.IsAny>(), + It.IsAny()), Times.Once); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs index 7395e1b3e..139a7f670 100644 --- a/test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs @@ -1,164 +1,164 @@ -namespace Ocelot.UnitTests.Configuration -{ - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.Creator; - using Ocelot.Configuration.File; - using Shouldly; - using System.Collections.Generic; - using System.Net.Http; - using TestStack.BDDfy; - using Values; - using Xunit; - - public class AggregatesCreatorTests - { - private readonly AggregatesCreator _creator; - private readonly Mock _utpCreator; - private FileConfiguration _fileConfiguration; - private List _reRoutes; - private List _result; - private UpstreamPathTemplate _aggregate1Utp; - private UpstreamPathTemplate _aggregate2Utp; - - public AggregatesCreatorTests() - { - _utpCreator = new Mock(); - _creator = new AggregatesCreator(_utpCreator.Object); - } - - [Fact] - public void should_return_no_aggregates() - { - var fileConfig = new FileConfiguration - { - Aggregates = new List - { - new FileAggregateReRoute - { - ReRouteKeys = new List{"key1"} - } - } - }; - var reRoutes = new List(); - - this.Given(_ => GivenThe(fileConfig)) - .And(_ => GivenThe(reRoutes)) - .When(_ => WhenICreate()) - .Then(_ => TheUtpCreatorIsNotCalled()) - .And(_ => ThenTheResultIsNotNull()) - .And(_ => ThenTheResultIsEmpty()) - .BDDfy(); - } - - [Fact] - public void should_create_aggregates() - { - var fileConfig = new FileConfiguration - { - Aggregates = new List - { - new FileAggregateReRoute - { - ReRouteKeys = new List{"key1", "key2"}, - UpstreamHost = "hosty", - UpstreamPathTemplate = "templatey", - Aggregator = "aggregatory", - ReRouteIsCaseSensitive = true - }, - new FileAggregateReRoute - { - ReRouteKeys = new List{"key3", "key4"}, - UpstreamHost = "hosty", - UpstreamPathTemplate = "templatey", - Aggregator = "aggregatory", - ReRouteIsCaseSensitive = true - } - } - }; - - var reRoutes = new List - { - new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key1").Build()).Build(), - new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key2").Build()).Build(), - new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key3").Build()).Build(), - new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key4").Build()).Build() - }; - - this.Given(_ => GivenThe(fileConfig)) - .And(_ => GivenThe(reRoutes)) - .And(_ => GivenTheUtpCreatorReturns()) - .When(_ => WhenICreate()) - .Then(_ => ThenTheUtpCreatorIsCalledCorrectly()) - .And(_ => ThenTheAggregatesAreCreated()) - .BDDfy(); - } - - private void ThenTheAggregatesAreCreated() - { - _result.ShouldNotBeNull(); - _result.Count.ShouldBe(2); - - _result[0].UpstreamHttpMethod.ShouldContain(x => x == HttpMethod.Get); - _result[0].UpstreamHost.ShouldBe(_fileConfiguration.Aggregates[0].UpstreamHost); - _result[0].UpstreamTemplatePattern.ShouldBe(_aggregate1Utp); - _result[0].Aggregator.ShouldBe(_fileConfiguration.Aggregates[0].Aggregator); - _result[0].DownstreamReRoute.ShouldContain(x => x == _reRoutes[0].DownstreamReRoute[0]); - _result[0].DownstreamReRoute.ShouldContain(x => x == _reRoutes[1].DownstreamReRoute[0]); - - _result[1].UpstreamHttpMethod.ShouldContain(x => x == HttpMethod.Get); - _result[1].UpstreamHost.ShouldBe(_fileConfiguration.Aggregates[1].UpstreamHost); - _result[1].UpstreamTemplatePattern.ShouldBe(_aggregate2Utp); - _result[1].Aggregator.ShouldBe(_fileConfiguration.Aggregates[1].Aggregator); - _result[1].DownstreamReRoute.ShouldContain(x => x == _reRoutes[2].DownstreamReRoute[0]); - _result[1].DownstreamReRoute.ShouldContain(x => x == _reRoutes[3].DownstreamReRoute[0]); - } - - private void ThenTheUtpCreatorIsCalledCorrectly() - { - _utpCreator.Verify(x => x.Create(_fileConfiguration.Aggregates[0]), Times.Once); - _utpCreator.Verify(x => x.Create(_fileConfiguration.Aggregates[1]), Times.Once); - } - - private void GivenTheUtpCreatorReturns() - { - _aggregate1Utp = new UpstreamPathTemplateBuilder().Build(); - _aggregate2Utp = new UpstreamPathTemplateBuilder().Build(); - - _utpCreator.SetupSequence(x => x.Create(It.IsAny())) - .Returns(_aggregate1Utp) - .Returns(_aggregate2Utp); - } - - private void ThenTheResultIsEmpty() - { - _result.Count.ShouldBe(0); - } - - private void ThenTheResultIsNotNull() - { - _result.ShouldNotBeNull(); - } - - private void TheUtpCreatorIsNotCalled() - { - _utpCreator.Verify(x => x.Create(It.IsAny()), Times.Never); - } - - private void GivenThe(FileConfiguration fileConfiguration) - { - _fileConfiguration = fileConfiguration; - } - - private void GivenThe(List reRoutes) - { - _reRoutes = reRoutes; - } - - private void WhenICreate() - { - _result = _creator.Create(_fileConfiguration, _reRoutes); - } - } -} +namespace Ocelot.UnitTests.Configuration +{ + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Shouldly; + using System.Collections.Generic; + using System.Net.Http; + using TestStack.BDDfy; + using Values; + using Xunit; + + public class AggregatesCreatorTests + { + private readonly AggregatesCreator _creator; + private readonly Mock _utpCreator; + private FileConfiguration _fileConfiguration; + private List _routes; + private List _result; + private UpstreamPathTemplate _aggregate1Utp; + private UpstreamPathTemplate _aggregate2Utp; + + public AggregatesCreatorTests() + { + _utpCreator = new Mock(); + _creator = new AggregatesCreator(_utpCreator.Object); + } + + [Fact] + public void should_return_no_aggregates() + { + var fileConfig = new FileConfiguration + { + Aggregates = new List + { + new FileAggregateRoute + { + RouteKeys = new List{"key1"} + } + } + }; + var routes = new List(); + + this.Given(_ => GivenThe(fileConfig)) + .And(_ => GivenThe(routes)) + .When(_ => WhenICreate()) + .Then(_ => TheUtpCreatorIsNotCalled()) + .And(_ => ThenTheResultIsNotNull()) + .And(_ => ThenTheResultIsEmpty()) + .BDDfy(); + } + + [Fact] + public void should_create_aggregates() + { + var fileConfig = new FileConfiguration + { + Aggregates = new List + { + new FileAggregateRoute + { + RouteKeys = new List{"key1", "key2"}, + UpstreamHost = "hosty", + UpstreamPathTemplate = "templatey", + Aggregator = "aggregatory", + RouteIsCaseSensitive = true + }, + new FileAggregateRoute + { + RouteKeys = new List{"key3", "key4"}, + UpstreamHost = "hosty", + UpstreamPathTemplate = "templatey", + Aggregator = "aggregatory", + RouteIsCaseSensitive = true + } + } + }; + + var routes = new List + { + new RouteBuilder().WithDownstreamRoute(new DownstreamRouteBuilder().WithKey("key1").Build()).Build(), + new RouteBuilder().WithDownstreamRoute(new DownstreamRouteBuilder().WithKey("key2").Build()).Build(), + new RouteBuilder().WithDownstreamRoute(new DownstreamRouteBuilder().WithKey("key3").Build()).Build(), + new RouteBuilder().WithDownstreamRoute(new DownstreamRouteBuilder().WithKey("key4").Build()).Build() + }; + + this.Given(_ => GivenThe(fileConfig)) + .And(_ => GivenThe(routes)) + .And(_ => GivenTheUtpCreatorReturns()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheUtpCreatorIsCalledCorrectly()) + .And(_ => ThenTheAggregatesAreCreated()) + .BDDfy(); + } + + private void ThenTheAggregatesAreCreated() + { + _result.ShouldNotBeNull(); + _result.Count.ShouldBe(2); + + _result[0].UpstreamHttpMethod.ShouldContain(x => x == HttpMethod.Get); + _result[0].UpstreamHost.ShouldBe(_fileConfiguration.Aggregates[0].UpstreamHost); + _result[0].UpstreamTemplatePattern.ShouldBe(_aggregate1Utp); + _result[0].Aggregator.ShouldBe(_fileConfiguration.Aggregates[0].Aggregator); + _result[0].DownstreamRoute.ShouldContain(x => x == _routes[0].DownstreamRoute[0]); + _result[0].DownstreamRoute.ShouldContain(x => x == _routes[1].DownstreamRoute[0]); + + _result[1].UpstreamHttpMethod.ShouldContain(x => x == HttpMethod.Get); + _result[1].UpstreamHost.ShouldBe(_fileConfiguration.Aggregates[1].UpstreamHost); + _result[1].UpstreamTemplatePattern.ShouldBe(_aggregate2Utp); + _result[1].Aggregator.ShouldBe(_fileConfiguration.Aggregates[1].Aggregator); + _result[1].DownstreamRoute.ShouldContain(x => x == _routes[2].DownstreamRoute[0]); + _result[1].DownstreamRoute.ShouldContain(x => x == _routes[3].DownstreamRoute[0]); + } + + private void ThenTheUtpCreatorIsCalledCorrectly() + { + _utpCreator.Verify(x => x.Create(_fileConfiguration.Aggregates[0]), Times.Once); + _utpCreator.Verify(x => x.Create(_fileConfiguration.Aggregates[1]), Times.Once); + } + + private void GivenTheUtpCreatorReturns() + { + _aggregate1Utp = new UpstreamPathTemplateBuilder().Build(); + _aggregate2Utp = new UpstreamPathTemplateBuilder().Build(); + + _utpCreator.SetupSequence(x => x.Create(It.IsAny())) + .Returns(_aggregate1Utp) + .Returns(_aggregate2Utp); + } + + private void ThenTheResultIsEmpty() + { + _result.Count.ShouldBe(0); + } + + private void ThenTheResultIsNotNull() + { + _result.ShouldNotBeNull(); + } + + private void TheUtpCreatorIsNotCalled() + { + _utpCreator.Verify(x => x.Create(It.IsAny()), Times.Never); + } + + private void GivenThe(FileConfiguration fileConfiguration) + { + _fileConfiguration = fileConfiguration; + } + + private void GivenThe(List routes) + { + _routes = routes; + } + + private void WhenICreate() + { + _result = _creator.Create(_fileConfiguration, _routes); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/AuthenticationOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/AuthenticationOptionsCreatorTests.cs index 1056dca59..24222a377 100644 --- a/test/Ocelot.UnitTests/Configuration/AuthenticationOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/AuthenticationOptionsCreatorTests.cs @@ -1,62 +1,62 @@ -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Shouldly; -using System.Collections.Generic; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - public class AuthenticationOptionsCreatorTests - { - private readonly AuthenticationOptionsCreator _authOptionsCreator; - private FileReRoute _fileReRoute; - private AuthenticationOptions _result; - - public AuthenticationOptionsCreatorTests() - { - _authOptionsCreator = new AuthenticationOptionsCreator(); - } - - [Fact] - public void should_return_auth_options() - { - var fileReRoute = new FileReRoute() - { - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "Test", - AllowedScopes = new List { "cheese" }, - } - }; - - var expected = new AuthenticationOptionsBuilder() - .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) - .WithAuthenticationProviderKey("Test") - .Build(); - - this.Given(x => x.GivenTheFollowing(fileReRoute)) - .When(x => x.WhenICreateTheAuthenticationOptions()) - .Then(x => x.ThenTheFollowingConfigIsReturned(expected)) - .BDDfy(); - } - - private void GivenTheFollowing(FileReRoute fileReRoute) - { - _fileReRoute = fileReRoute; - } - - private void WhenICreateTheAuthenticationOptions() - { - _result = _authOptionsCreator.Create(_fileReRoute); - } - - private void ThenTheFollowingConfigIsReturned(AuthenticationOptions expected) +namespace Ocelot.UnitTests.Configuration +{ + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Shouldly; + using System.Collections.Generic; + using TestStack.BDDfy; + using Xunit; + + public class AuthenticationOptionsCreatorTests + { + private readonly AuthenticationOptionsCreator _authOptionsCreator; + private FileRoute _fileRoute; + private AuthenticationOptions _result; + + public AuthenticationOptionsCreatorTests() { - _result.AllowedScopes.ShouldBe(expected.AllowedScopes); - _result.AuthenticationProviderKey.ShouldBe(expected.AuthenticationProviderKey); - } - } + _authOptionsCreator = new AuthenticationOptionsCreator(); + } + + [Fact] + public void should_return_auth_options() + { + var fileRoute = new FileRoute() + { + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List { "cheese" }, + } + }; + + var expected = new AuthenticationOptionsBuilder() + .WithAllowedScopes(fileRoute.AuthenticationOptions?.AllowedScopes) + .WithAuthenticationProviderKey("Test") + .Build(); + + this.Given(x => x.GivenTheFollowing(fileRoute)) + .When(x => x.WhenICreateTheAuthenticationOptions()) + .Then(x => x.ThenTheFollowingConfigIsReturned(expected)) + .BDDfy(); + } + + private void GivenTheFollowing(FileRoute fileRoute) + { + _fileRoute = fileRoute; + } + + private void WhenICreateTheAuthenticationOptions() + { + _result = _authOptionsCreator.Create(_fileRoute); + } + + private void ThenTheFollowingConfigIsReturned(AuthenticationOptions expected) + { + _result.AllowedScopes.ShouldBe(expected.AllowedScopes); + _result.AuthenticationProviderKey.ShouldBe(expected.AuthenticationProviderKey); + } + } } diff --git a/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs index 7736bf4e6..7634680f8 100644 --- a/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs @@ -1,126 +1,126 @@ -namespace Ocelot.UnitTests.Configuration -{ - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Creator; - using Ocelot.Configuration.File; - using Ocelot.DependencyInjection; - using Shouldly; - using System.Collections.Generic; - using TestStack.BDDfy; - using Xunit; - - public class ConfigurationCreatorTests - { - private ConfigurationCreator _creator; - private InternalConfiguration _result; - private readonly Mock _spcCreator; - private readonly Mock _qosCreator; - private readonly Mock _hhoCreator; - private readonly Mock _lboCreator; - private readonly Mock _vCreator; - private FileConfiguration _fileConfig; - private List _reRoutes; - private ServiceProviderConfiguration _spc; - private LoadBalancerOptions _lbo; - private QoSOptions _qoso; - private HttpHandlerOptions _hho; - private AdministrationPath _adminPath; - private readonly ServiceCollection _serviceCollection; - - public ConfigurationCreatorTests() - { - _vCreator = new Mock(); - _lboCreator = new Mock(); - _hhoCreator = new Mock(); - _qosCreator = new Mock(); - _spcCreator = new Mock(); - _serviceCollection = new ServiceCollection(); - } - - [Fact] - public void should_build_configuration_with_no_admin_path() - { - this.Given(_ => GivenTheDependenciesAreSetUp()) - .When(_ => WhenICreate()) - .Then(_ => ThenTheDepdenciesAreCalledCorrectly()) - .And(_ => ThenThePropertiesAreSetCorrectly()) - .And(_ => ThenTheAdminPathIsNull()) - .BDDfy(); - } - - [Fact] - public void should_build_configuration_with_admin_path() - { - this.Given(_ => GivenTheDependenciesAreSetUp()) - .And(_ => GivenTheAdminPath()) - .When(_ => WhenICreate()) - .Then(_ => ThenTheDepdenciesAreCalledCorrectly()) - .And(_ => ThenThePropertiesAreSetCorrectly()) - .And(_ => ThenTheAdminPathIsSet()) - .BDDfy(); - } - - private void ThenTheAdminPathIsNull() - { - _result.AdministrationPath.ShouldBeNull(); - } - - private void ThenThePropertiesAreSetCorrectly() - { - _result.ShouldNotBeNull(); - _result.ServiceProviderConfiguration.ShouldBe(_spc); - _result.LoadBalancerOptions.ShouldBe(_lbo); - _result.QoSOptions.ShouldBe(_qoso); - _result.HttpHandlerOptions.ShouldBe(_hho); - _result.ReRoutes.ShouldBe(_reRoutes); - _result.RequestId.ShouldBe(_fileConfig.GlobalConfiguration.RequestIdKey); - _result.DownstreamScheme.ShouldBe(_fileConfig.GlobalConfiguration.DownstreamScheme); - } - - private void ThenTheAdminPathIsSet() - { - _result.AdministrationPath.ShouldBe("wooty"); - } - - private void ThenTheDepdenciesAreCalledCorrectly() - { - _spcCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration), Times.Once); - _lboCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.LoadBalancerOptions), Times.Once); - _qosCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.QoSOptions), Times.Once); - _hhoCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.HttpHandlerOptions), Times.Once); - } - - private void GivenTheAdminPath() - { - _adminPath = new AdministrationPath("wooty"); - _serviceCollection.AddSingleton(_adminPath); - } - - private void GivenTheDependenciesAreSetUp() - { - _fileConfig = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration() - }; - _reRoutes = new List(); - _spc = new ServiceProviderConfiguration("", "", "", 1, "", "", 1); - _lbo = new LoadBalancerOptionsBuilder().Build(); - _qoso = new QoSOptions(1, 1, 1, ""); - _hho = new HttpHandlerOptionsBuilder().Build(); - - _spcCreator.Setup(x => x.Create(It.IsAny())).Returns(_spc); - _lboCreator.Setup(x => x.Create(It.IsAny())).Returns(_lbo); - _qosCreator.Setup(x => x.Create(It.IsAny())).Returns(_qoso); - _hhoCreator.Setup(x => x.Create(It.IsAny())).Returns(_hho); - } - - private void WhenICreate() - { - var serviceProvider = _serviceCollection.BuildServiceProvider(); - _creator = new ConfigurationCreator(_spcCreator.Object, _qosCreator.Object, _hhoCreator.Object, serviceProvider, _lboCreator.Object, _vCreator.Object); - _result = _creator.Create(_fileConfig, _reRoutes); - } - } -} +namespace Ocelot.UnitTests.Configuration +{ + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Ocelot.DependencyInjection; + using Shouldly; + using System.Collections.Generic; + using TestStack.BDDfy; + using Xunit; + + public class ConfigurationCreatorTests + { + private ConfigurationCreator _creator; + private InternalConfiguration _result; + private readonly Mock _spcCreator; + private readonly Mock _qosCreator; + private readonly Mock _hhoCreator; + private readonly Mock _lboCreator; + private readonly Mock _vCreator; + private FileConfiguration _fileConfig; + private List _routes; + private ServiceProviderConfiguration _spc; + private LoadBalancerOptions _lbo; + private QoSOptions _qoso; + private HttpHandlerOptions _hho; + private AdministrationPath _adminPath; + private readonly ServiceCollection _serviceCollection; + + public ConfigurationCreatorTests() + { + _vCreator = new Mock(); + _lboCreator = new Mock(); + _hhoCreator = new Mock(); + _qosCreator = new Mock(); + _spcCreator = new Mock(); + _serviceCollection = new ServiceCollection(); + } + + [Fact] + public void should_build_configuration_with_no_admin_path() + { + this.Given(_ => GivenTheDependenciesAreSetUp()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDepdenciesAreCalledCorrectly()) + .And(_ => ThenThePropertiesAreSetCorrectly()) + .And(_ => ThenTheAdminPathIsNull()) + .BDDfy(); + } + + [Fact] + public void should_build_configuration_with_admin_path() + { + this.Given(_ => GivenTheDependenciesAreSetUp()) + .And(_ => GivenTheAdminPath()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDepdenciesAreCalledCorrectly()) + .And(_ => ThenThePropertiesAreSetCorrectly()) + .And(_ => ThenTheAdminPathIsSet()) + .BDDfy(); + } + + private void ThenTheAdminPathIsNull() + { + _result.AdministrationPath.ShouldBeNull(); + } + + private void ThenThePropertiesAreSetCorrectly() + { + _result.ShouldNotBeNull(); + _result.ServiceProviderConfiguration.ShouldBe(_spc); + _result.LoadBalancerOptions.ShouldBe(_lbo); + _result.QoSOptions.ShouldBe(_qoso); + _result.HttpHandlerOptions.ShouldBe(_hho); + _result.Routes.ShouldBe(_routes); + _result.RequestId.ShouldBe(_fileConfig.GlobalConfiguration.RequestIdKey); + _result.DownstreamScheme.ShouldBe(_fileConfig.GlobalConfiguration.DownstreamScheme); + } + + private void ThenTheAdminPathIsSet() + { + _result.AdministrationPath.ShouldBe("wooty"); + } + + private void ThenTheDepdenciesAreCalledCorrectly() + { + _spcCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration), Times.Once); + _lboCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.LoadBalancerOptions), Times.Once); + _qosCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.QoSOptions), Times.Once); + _hhoCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.HttpHandlerOptions), Times.Once); + } + + private void GivenTheAdminPath() + { + _adminPath = new AdministrationPath("wooty"); + _serviceCollection.AddSingleton(_adminPath); + } + + private void GivenTheDependenciesAreSetUp() + { + _fileConfig = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration() + }; + _routes = new List(); + _spc = new ServiceProviderConfiguration("", "", "", 1, "", "", 1); + _lbo = new LoadBalancerOptionsBuilder().Build(); + _qoso = new QoSOptions(1, 1, 1, ""); + _hho = new HttpHandlerOptionsBuilder().Build(); + + _spcCreator.Setup(x => x.Create(It.IsAny())).Returns(_spc); + _lboCreator.Setup(x => x.Create(It.IsAny())).Returns(_lbo); + _qosCreator.Setup(x => x.Create(It.IsAny())).Returns(_qoso); + _hhoCreator.Setup(x => x.Create(It.IsAny())).Returns(_hho); + } + + private void WhenICreate() + { + var serviceProvider = _serviceCollection.BuildServiceProvider(); + _creator = new ConfigurationCreator(_spcCreator.Object, _qosCreator.Object, _hhoCreator.Object, serviceProvider, _lboCreator.Object, _vCreator.Object); + _result = _creator.Create(_fileConfig, _routes); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs index dcf51ce45..6e4600b22 100644 --- a/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs @@ -1,302 +1,302 @@ -namespace Ocelot.UnitTests.Configuration -{ - using Microsoft.AspNetCore.Hosting; - using Moq; - using Newtonsoft.Json; - using Ocelot.Configuration.ChangeTracking; - using Ocelot.Configuration.File; - using Ocelot.Configuration.Repository; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Threading; - using TestStack.BDDfy; - using Xunit; - - public class DiskFileConfigurationRepositoryTests : IDisposable - { - private readonly Mock _hostingEnvironment; - private readonly Mock _changeTokenSource; - private IFileConfigurationRepository _repo; - private string _environmentSpecificPath; - private string _ocelotJsonPath; - private FileConfiguration _result; - private FileConfiguration _fileConfiguration; - - // This is a bit dirty and it is dev.dev so that the ConfigurationBuilderExtensionsTests - // cant pick it up if they run in parralel..and the semaphore stops them running at the same time...sigh - // these are not really unit tests but whatever... - private string _environmentName = "DEV.DEV"; - - private static SemaphoreSlim _semaphore; - - public DiskFileConfigurationRepositoryTests() - { - _semaphore = new SemaphoreSlim(1, 1); - _semaphore.Wait(); - _hostingEnvironment = new Mock(); - _hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName); - _changeTokenSource = new Mock(MockBehavior.Strict); - _changeTokenSource.Setup(m => m.Activate()); - _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object, _changeTokenSource.Object); - } - - [Fact] - public void should_return_file_configuration() - { - var config = FakeFileConfigurationForGet(); - - this.Given(_ => GivenTheConfigurationIs(config)) - .When(_ => WhenIGetTheReRoutes()) - .Then(_ => ThenTheFollowingIsReturned(config)) - .BDDfy(); - } - - [Fact] - public void should_return_file_configuration_if_environment_name_is_unavailable() - { - var config = FakeFileConfigurationForGet(); - - this.Given(_ => GivenTheEnvironmentNameIsUnavailable()) - .And(_ => GivenTheConfigurationIs(config)) - .When(_ => WhenIGetTheReRoutes()) - .Then(_ => ThenTheFollowingIsReturned(config)) - .BDDfy(); - } - - [Fact] - public void should_set_file_configuration() - { - var config = FakeFileConfigurationForSet(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .When(_ => WhenISetTheConfiguration()) - .Then(_ => ThenTheConfigurationIsStoredAs(config)) - .And(_ => ThenTheConfigurationJsonIsIndented(config)) - .And(x => AndTheChangeTokenIsActivated()) - .BDDfy(); - } - - [Fact] - public void should_set_file_configuration_if_environment_name_is_unavailable() - { - var config = FakeFileConfigurationForSet(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .And(_ => GivenTheEnvironmentNameIsUnavailable()) - .When(_ => WhenISetTheConfiguration()) - .Then(_ => ThenTheConfigurationIsStoredAs(config)) - .And(_ => ThenTheConfigurationJsonIsIndented(config)) - .BDDfy(); - } - - [Fact] - public void should_set_environment_file_configuration_and_ocelot_file_configuration() - { - var config = FakeFileConfigurationForSet(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .And(_ => GivenTheConfigurationIs(config)) - .And(_ => GivenTheUserAddedOcelotJson()) - .When(_ => WhenISetTheConfiguration()) - .Then(_ => ThenTheConfigurationIsStoredAs(config)) - .And(_ => ThenTheConfigurationJsonIsIndented(config)) - .Then(_ => ThenTheOcelotJsonIsStoredAs(config)) - .BDDfy(); - } - - private void GivenTheUserAddedOcelotJson() - { - _ocelotJsonPath = $"{AppContext.BaseDirectory}/ocelot.json"; - - if (File.Exists(_ocelotJsonPath)) - { - File.Delete(_ocelotJsonPath); - } - - File.WriteAllText(_ocelotJsonPath, "Doesnt matter"); - } - - private void GivenTheEnvironmentNameIsUnavailable() - { - _environmentName = null; - _hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName); - _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object, _changeTokenSource.Object); - } - - private void GivenIHaveAConfiguration(FileConfiguration fileConfiguration) - { - _fileConfiguration = fileConfiguration; - } - - private void WhenISetTheConfiguration() - { - _repo.Set(_fileConfiguration); - _result = _repo.Get().Result.Data; - } - - private void ThenTheConfigurationIsStoredAs(FileConfiguration expecteds) - { - _result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); - _result.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); - _result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); - _result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); - - for (var i = 0; i < _result.ReRoutes.Count; i++) - { - for (int j = 0; j < _result.ReRoutes[i].DownstreamHostAndPorts.Count; j++) - { - var result = _result.ReRoutes[i].DownstreamHostAndPorts[j]; - var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j]; - - result.Host.ShouldBe(expected.Host); - result.Port.ShouldBe(expected.Port); - } - - _result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate); - _result.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme); - } - } - - private void ThenTheOcelotJsonIsStoredAs(FileConfiguration expecteds) - { - var resultText = File.ReadAllText(_ocelotJsonPath); - var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented); - resultText.ShouldBe(expectedText); - } - - private void GivenTheConfigurationIs(FileConfiguration fileConfiguration) - { - _environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json"; - - var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented); - - if (File.Exists(_environmentSpecificPath)) - { - File.Delete(_environmentSpecificPath); - } - - File.WriteAllText(_environmentSpecificPath, jsonConfiguration); - } - - private void ThenTheConfigurationJsonIsIndented(FileConfiguration expecteds) - { - var path = !string.IsNullOrEmpty(_environmentSpecificPath) ? _environmentSpecificPath : _environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json"; - - var resultText = File.ReadAllText(path); - var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented); - resultText.ShouldBe(expectedText); - } - - private void WhenIGetTheReRoutes() - { - _result = _repo.Get().Result.Data; - } - - private void ThenTheFollowingIsReturned(FileConfiguration expecteds) - { - _result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); - _result.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); - _result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); - _result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); - - for (var i = 0; i < _result.ReRoutes.Count; i++) - { - for (int j = 0; j < _result.ReRoutes[i].DownstreamHostAndPorts.Count; j++) - { - var result = _result.ReRoutes[i].DownstreamHostAndPorts[j]; - var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j]; - - result.Host.ShouldBe(expected.Host); - result.Port.ShouldBe(expected.Port); - } - - _result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate); - _result.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme); - } - } - - private void AndTheChangeTokenIsActivated() - { - _changeTokenSource.Verify(m => m.Activate(), Times.Once); - } - - private FileConfiguration FakeFileConfigurationForSet() - { - var reRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.12.12.12", - Port = 80, - }, - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/asdfs/test/{test}", - }, - }; - - var globalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Port = 198, - Host = "blah" - } - }; - - return new FileConfiguration - { - GlobalConfiguration = globalConfiguration, - ReRoutes = reRoutes - }; - } - - private FileConfiguration FakeFileConfigurationForGet() - { - var reRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/test/test/{test}" - } - }; - - var globalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Port = 198, - Host = "blah" - } - }; - - return new FileConfiguration - { - GlobalConfiguration = globalConfiguration, - ReRoutes = reRoutes - }; - } - - public void Dispose() - { - _semaphore.Release(); - } - } -} +namespace Ocelot.UnitTests.Configuration +{ + using Microsoft.AspNetCore.Hosting; + using Moq; + using Newtonsoft.Json; + using Ocelot.Configuration.ChangeTracking; + using Ocelot.Configuration.File; + using Ocelot.Configuration.Repository; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Threading; + using TestStack.BDDfy; + using Xunit; + + public class DiskFileConfigurationRepositoryTests : IDisposable + { + private readonly Mock _hostingEnvironment; + private readonly Mock _changeTokenSource; + private IFileConfigurationRepository _repo; + private string _environmentSpecificPath; + private string _ocelotJsonPath; + private FileConfiguration _result; + private FileConfiguration _fileConfiguration; + + // This is a bit dirty and it is dev.dev so that the ConfigurationBuilderExtensionsTests + // cant pick it up if they run in parralel..and the semaphore stops them running at the same time...sigh + // these are not really unit tests but whatever... + private string _environmentName = "DEV.DEV"; + + private static SemaphoreSlim _semaphore; + + public DiskFileConfigurationRepositoryTests() + { + _semaphore = new SemaphoreSlim(1, 1); + _semaphore.Wait(); + _hostingEnvironment = new Mock(); + _hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName); + _changeTokenSource = new Mock(MockBehavior.Strict); + _changeTokenSource.Setup(m => m.Activate()); + _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object, _changeTokenSource.Object); + } + + [Fact] + public void should_return_file_configuration() + { + var config = FakeFileConfigurationForGet(); + + this.Given(_ => GivenTheConfigurationIs(config)) + .When(_ => WhenIGetTheRoutes()) + .Then(_ => ThenTheFollowingIsReturned(config)) + .BDDfy(); + } + + [Fact] + public void should_return_file_configuration_if_environment_name_is_unavailable() + { + var config = FakeFileConfigurationForGet(); + + this.Given(_ => GivenTheEnvironmentNameIsUnavailable()) + .And(_ => GivenTheConfigurationIs(config)) + .When(_ => WhenIGetTheRoutes()) + .Then(_ => ThenTheFollowingIsReturned(config)) + .BDDfy(); + } + + [Fact] + public void should_set_file_configuration() + { + var config = FakeFileConfigurationForSet(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .When(_ => WhenISetTheConfiguration()) + .Then(_ => ThenTheConfigurationIsStoredAs(config)) + .And(_ => ThenTheConfigurationJsonIsIndented(config)) + .And(x => AndTheChangeTokenIsActivated()) + .BDDfy(); + } + + [Fact] + public void should_set_file_configuration_if_environment_name_is_unavailable() + { + var config = FakeFileConfigurationForSet(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .And(_ => GivenTheEnvironmentNameIsUnavailable()) + .When(_ => WhenISetTheConfiguration()) + .Then(_ => ThenTheConfigurationIsStoredAs(config)) + .And(_ => ThenTheConfigurationJsonIsIndented(config)) + .BDDfy(); + } + + [Fact] + public void should_set_environment_file_configuration_and_ocelot_file_configuration() + { + var config = FakeFileConfigurationForSet(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .And(_ => GivenTheConfigurationIs(config)) + .And(_ => GivenTheUserAddedOcelotJson()) + .When(_ => WhenISetTheConfiguration()) + .Then(_ => ThenTheConfigurationIsStoredAs(config)) + .And(_ => ThenTheConfigurationJsonIsIndented(config)) + .Then(_ => ThenTheOcelotJsonIsStoredAs(config)) + .BDDfy(); + } + + private void GivenTheUserAddedOcelotJson() + { + _ocelotJsonPath = $"{AppContext.BaseDirectory}/ocelot.json"; + + if (File.Exists(_ocelotJsonPath)) + { + File.Delete(_ocelotJsonPath); + } + + File.WriteAllText(_ocelotJsonPath, "Doesnt matter"); + } + + private void GivenTheEnvironmentNameIsUnavailable() + { + _environmentName = null; + _hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName); + _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object, _changeTokenSource.Object); + } + + private void GivenIHaveAConfiguration(FileConfiguration fileConfiguration) + { + _fileConfiguration = fileConfiguration; + } + + private void WhenISetTheConfiguration() + { + _repo.Set(_fileConfiguration); + _result = _repo.Get().Result.Data; + } + + private void ThenTheConfigurationIsStoredAs(FileConfiguration expecteds) + { + _result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); + _result.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); + _result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); + _result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); + + for (var i = 0; i < _result.Routes.Count; i++) + { + for (int j = 0; j < _result.Routes[i].DownstreamHostAndPorts.Count; j++) + { + var result = _result.Routes[i].DownstreamHostAndPorts[j]; + var expected = expecteds.Routes[i].DownstreamHostAndPorts[j]; + + result.Host.ShouldBe(expected.Host); + result.Port.ShouldBe(expected.Port); + } + + _result.Routes[i].DownstreamPathTemplate.ShouldBe(expecteds.Routes[i].DownstreamPathTemplate); + _result.Routes[i].DownstreamScheme.ShouldBe(expecteds.Routes[i].DownstreamScheme); + } + } + + private void ThenTheOcelotJsonIsStoredAs(FileConfiguration expecteds) + { + var resultText = File.ReadAllText(_ocelotJsonPath); + var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented); + resultText.ShouldBe(expectedText); + } + + private void GivenTheConfigurationIs(FileConfiguration fileConfiguration) + { + _environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json"; + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration, Formatting.Indented); + + if (File.Exists(_environmentSpecificPath)) + { + File.Delete(_environmentSpecificPath); + } + + File.WriteAllText(_environmentSpecificPath, jsonConfiguration); + } + + private void ThenTheConfigurationJsonIsIndented(FileConfiguration expecteds) + { + var path = !string.IsNullOrEmpty(_environmentSpecificPath) ? _environmentSpecificPath : _environmentSpecificPath = $"{AppContext.BaseDirectory}/ocelot{(string.IsNullOrEmpty(_environmentName) ? string.Empty : ".")}{_environmentName}.json"; + + var resultText = File.ReadAllText(path); + var expectedText = JsonConvert.SerializeObject(expecteds, Formatting.Indented); + resultText.ShouldBe(expectedText); + } + + private void WhenIGetTheRoutes() + { + _result = _repo.Get().Result.Data; + } + + private void ThenTheFollowingIsReturned(FileConfiguration expecteds) + { + _result.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey); + _result.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); + _result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host); + _result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port); + + for (var i = 0; i < _result.Routes.Count; i++) + { + for (int j = 0; j < _result.Routes[i].DownstreamHostAndPorts.Count; j++) + { + var result = _result.Routes[i].DownstreamHostAndPorts[j]; + var expected = expecteds.Routes[i].DownstreamHostAndPorts[j]; + + result.Host.ShouldBe(expected.Host); + result.Port.ShouldBe(expected.Port); + } + + _result.Routes[i].DownstreamPathTemplate.ShouldBe(expecteds.Routes[i].DownstreamPathTemplate); + _result.Routes[i].DownstreamScheme.ShouldBe(expecteds.Routes[i].DownstreamScheme); + } + } + + private void AndTheChangeTokenIsActivated() + { + _changeTokenSource.Verify(m => m.Activate(), Times.Once); + } + + private FileConfiguration FakeFileConfigurationForSet() + { + var routes = new List + { + new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "123.12.12.12", + Port = 80, + }, + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/asdfs/test/{test}", + }, + }; + + var globalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Port = 198, + Host = "blah" + } + }; + + return new FileConfiguration + { + GlobalConfiguration = globalConfiguration, + Routes = routes + }; + } + + private FileConfiguration FakeFileConfigurationForGet() + { + var routes = new List + { + new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/test/test/{test}" + } + }; + + var globalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Port = 198, + Host = "blah" + } + }; + + return new FileConfiguration + { + GlobalConfiguration = globalConfiguration, + Routes = routes + }; + } + + public void Dispose() + { + _semaphore.Release(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/DownstreamAddressesCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/DownstreamAddressesCreatorTests.cs index ef6bfede5..c8bbabcbb 100644 --- a/test/Ocelot.UnitTests/Configuration/DownstreamAddressesCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/DownstreamAddressesCreatorTests.cs @@ -1,121 +1,121 @@ -using Ocelot.Configuration; -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Shouldly; -using System.Collections.Generic; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - public class DownstreamAddressesCreatorTests - { - public DownstreamAddressesCreator _creator; - private FileReRoute _reRoute; - private List _result; - - public DownstreamAddressesCreatorTests() - { - _creator = new DownstreamAddressesCreator(); - } - - [Fact] - public void should_do_nothing() - { - var reRoute = new FileReRoute - { - }; - - var expected = new List - { - }; - - this.Given(x => GivenTheFollowingReRoute(reRoute)) - .When(x => WhenICreate()) - .Then(x => TheThenFollowingIsReturned(expected)) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_addresses_from_old_downstream_path_and_port() - { - var reRoute = new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "test", +namespace Ocelot.UnitTests.Configuration +{ + using Ocelot.Configuration; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Shouldly; + using System.Collections.Generic; + using TestStack.BDDfy; + using Xunit; + + public class DownstreamAddressesCreatorTests + { + public DownstreamAddressesCreator _creator; + private FileRoute _route; + private List _result; + + public DownstreamAddressesCreatorTests() + { + _creator = new DownstreamAddressesCreator(); + } + + [Fact] + public void should_do_nothing() + { + var route = new FileRoute + { + }; + + var expected = new List + { + }; + + this.Given(x => GivenTheFollowingRoute(route)) + .When(x => WhenICreate()) + .Then(x => TheThenFollowingIsReturned(expected)) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_addresses_from_old_downstream_path_and_port() + { + var route = new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "test", Port = 80 - } - }, - }; - - var expected = new List - { - new DownstreamHostAndPort("test", 80), - }; - - this.Given(x => GivenTheFollowingReRoute(reRoute)) - .When(x => WhenICreate()) - .Then(x => TheThenFollowingIsReturned(expected)) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_addresses_from_downstream_host_and_ports() - { - var reRoute = new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "test", - Port = 80 - }, - new FileHostAndPort - { - Host = "west", - Port = 443 - } - } - }; - - var expected = new List - { - new DownstreamHostAndPort("test", 80), - new DownstreamHostAndPort("west", 443) + } + }, }; - this.Given(x => GivenTheFollowingReRoute(reRoute)) - .When(x => WhenICreate()) - .Then(x => TheThenFollowingIsReturned(expected)) - .BDDfy(); - } - - private void GivenTheFollowingReRoute(FileReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenICreate() - { - _result = _creator.Create(_reRoute); - } - - private void TheThenFollowingIsReturned(List expecteds) - { - _result.Count.ShouldBe(expecteds.Count); - - for (int i = 0; i < _result.Count; i++) - { - var result = _result[i]; - var expected = expecteds[i]; - - result.Host.ShouldBe(expected.Host); - result.Port.ShouldBe(expected.Port); - } - } - } + var expected = new List + { + new DownstreamHostAndPort("test", 80), + }; + + this.Given(x => GivenTheFollowingRoute(route)) + .When(x => WhenICreate()) + .Then(x => TheThenFollowingIsReturned(expected)) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_addresses_from_downstream_host_and_ports() + { + var route = new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "test", + Port = 80 + }, + new FileHostAndPort + { + Host = "west", + Port = 443 + } + } + }; + + var expected = new List + { + new DownstreamHostAndPort("test", 80), + new DownstreamHostAndPort("west", 443) + }; + + this.Given(x => GivenTheFollowingRoute(route)) + .When(x => WhenICreate()) + .Then(x => TheThenFollowingIsReturned(expected)) + .BDDfy(); + } + + private void GivenTheFollowingRoute(FileRoute route) + { + _route = route; + } + + private void WhenICreate() + { + _result = _creator.Create(_route); + } + + private void TheThenFollowingIsReturned(List expecteds) + { + _result.Count.ShouldBe(expecteds.Count); + + for (int i = 0; i < _result.Count; i++) + { + var result = _result[i]; + var expected = expecteds[i]; + + result.Host.ShouldBe(expected.Host); + result.Port.ShouldBe(expected.Port); + } + } + } } diff --git a/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs index b502aa73c..16b14c005 100644 --- a/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs @@ -1,148 +1,148 @@ -namespace Ocelot.UnitTests.Configuration -{ - using System; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.Creator; - using Ocelot.Configuration.File; - using Shouldly; - using System.Collections.Generic; - using TestStack.BDDfy; - using Xunit; - - public class DynamicsCreatorTests - { - private readonly DynamicsCreator _creator; - private readonly Mock _rloCreator; - private readonly Mock _versionCreator; - private List _result; - private FileConfiguration _fileConfig; - private RateLimitOptions _rlo1; - private RateLimitOptions _rlo2; - private Version _version; - - public DynamicsCreatorTests() - { - _versionCreator = new Mock(); - _rloCreator = new Mock(); - _creator = new DynamicsCreator(_rloCreator.Object, _versionCreator.Object); - } - - [Fact] - public void should_return_nothing() - { - var fileConfig = new FileConfiguration(); - - this.Given(_ => GivenThe(fileConfig)) - .When(_ => WhenICreate()) - .Then(_ => ThenNothingIsReturned()) - .And(_ => ThenTheRloCreatorIsNotCalled()) - .BDDfy(); - } - - [Fact] - public void should_return_re_routes() - { - var fileConfig = new FileConfiguration - { - DynamicReRoutes = new List - { - new FileDynamicReRoute - { - ServiceName = "1", - RateLimitRule = new FileRateLimitRule - { - EnableRateLimiting = false - }, - DownstreamHttpVersion = "1.1" - }, - new FileDynamicReRoute - { - ServiceName = "2", - RateLimitRule = new FileRateLimitRule - { - EnableRateLimiting = true - }, - DownstreamHttpVersion = "2.0" - } - } - }; - - this.Given(_ => GivenThe(fileConfig)) - .And(_ => GivenTheRloCreatorReturns()) - .And(_ => GivenTheVersionCreatorReturns()) - .When(_ => WhenICreate()) - .Then(_ => ThenTheReRoutesAreReturned()) - .And(_ => ThenTheRloCreatorIsCalledCorrectly()) - .And(_ => ThenTheVersionCreatorIsCalledCorrectly()) - .BDDfy(); - } - - private void ThenTheRloCreatorIsCalledCorrectly() - { - _rloCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[0].RateLimitRule, - _fileConfig.GlobalConfiguration), Times.Once); - - _rloCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[1].RateLimitRule, - _fileConfig.GlobalConfiguration), Times.Once); - } - - private void ThenTheVersionCreatorIsCalledCorrectly() - { - _versionCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[0].DownstreamHttpVersion), Times.Once); - _versionCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[1].DownstreamHttpVersion), Times.Once); - } - - private void ThenTheReRoutesAreReturned() - { - _result.Count.ShouldBe(2); - _result[0].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeFalse(); - _result[0].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo1); - _result[0].DownstreamReRoute[0].DownstreamHttpVersion.ShouldBe(_version); - _result[0].DownstreamReRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicReRoutes[0].ServiceName); - - _result[1].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeTrue(); - _result[1].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo2); - _result[1].DownstreamReRoute[0].DownstreamHttpVersion.ShouldBe(_version); - _result[1].DownstreamReRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicReRoutes[1].ServiceName); - } - - private void GivenTheVersionCreatorReturns() - { - _version = new Version("1.1"); - _versionCreator.Setup(x => x.Create(It.IsAny())).Returns(_version); - } - - private void GivenTheRloCreatorReturns() - { - _rlo1 = new RateLimitOptionsBuilder().Build(); - _rlo2 = new RateLimitOptionsBuilder().WithEnableRateLimiting(true).Build(); - - _rloCreator - .SetupSequence(x => x.Create(It.IsAny(), It.IsAny())) - .Returns(_rlo1) - .Returns(_rlo2); - } - - private void ThenTheRloCreatorIsNotCalled() - { - _rloCreator.Verify(x => x.Create(It.IsAny(), It.IsAny()), Times.Never); - } - - private void ThenNothingIsReturned() - { - _result.Count.ShouldBe(0); - } - - private void WhenICreate() - { - _result = _creator.Create(_fileConfig); - } - - private void GivenThe(FileConfiguration fileConfig) - { - _fileConfig = fileConfig; - } - } -} +namespace Ocelot.UnitTests.Configuration +{ + using System; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Shouldly; + using System.Collections.Generic; + using TestStack.BDDfy; + using Xunit; + + public class DynamicsCreatorTests + { + private readonly DynamicsCreator _creator; + private readonly Mock _rloCreator; + private readonly Mock _versionCreator; + private List _result; + private FileConfiguration _fileConfig; + private RateLimitOptions _rlo1; + private RateLimitOptions _rlo2; + private Version _version; + + public DynamicsCreatorTests() + { + _versionCreator = new Mock(); + _rloCreator = new Mock(); + _creator = new DynamicsCreator(_rloCreator.Object, _versionCreator.Object); + } + + [Fact] + public void should_return_nothing() + { + var fileConfig = new FileConfiguration(); + + this.Given(_ => GivenThe(fileConfig)) + .When(_ => WhenICreate()) + .Then(_ => ThenNothingIsReturned()) + .And(_ => ThenTheRloCreatorIsNotCalled()) + .BDDfy(); + } + + [Fact] + public void should_return_re_routes() + { + var fileConfig = new FileConfiguration + { + DynamicRoutes = new List + { + new FileDynamicRoute + { + ServiceName = "1", + RateLimitRule = new FileRateLimitRule + { + EnableRateLimiting = false + }, + DownstreamHttpVersion = "1.1" + }, + new FileDynamicRoute + { + ServiceName = "2", + RateLimitRule = new FileRateLimitRule + { + EnableRateLimiting = true + }, + DownstreamHttpVersion = "2.0" + } + } + }; + + this.Given(_ => GivenThe(fileConfig)) + .And(_ => GivenTheRloCreatorReturns()) + .And(_ => GivenTheVersionCreatorReturns()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheRoutesAreReturned()) + .And(_ => ThenTheRloCreatorIsCalledCorrectly()) + .And(_ => ThenTheVersionCreatorIsCalledCorrectly()) + .BDDfy(); + } + + private void ThenTheRloCreatorIsCalledCorrectly() + { + _rloCreator.Verify(x => x.Create(_fileConfig.DynamicRoutes[0].RateLimitRule, + _fileConfig.GlobalConfiguration), Times.Once); + + _rloCreator.Verify(x => x.Create(_fileConfig.DynamicRoutes[1].RateLimitRule, + _fileConfig.GlobalConfiguration), Times.Once); + } + + private void ThenTheVersionCreatorIsCalledCorrectly() + { + _versionCreator.Verify(x => x.Create(_fileConfig.DynamicRoutes[0].DownstreamHttpVersion), Times.Once); + _versionCreator.Verify(x => x.Create(_fileConfig.DynamicRoutes[1].DownstreamHttpVersion), Times.Once); + } + + private void ThenTheRoutesAreReturned() + { + _result.Count.ShouldBe(2); + _result[0].DownstreamRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeFalse(); + _result[0].DownstreamRoute[0].RateLimitOptions.ShouldBe(_rlo1); + _result[0].DownstreamRoute[0].DownstreamHttpVersion.ShouldBe(_version); + _result[0].DownstreamRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicRoutes[0].ServiceName); + + _result[1].DownstreamRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeTrue(); + _result[1].DownstreamRoute[0].RateLimitOptions.ShouldBe(_rlo2); + _result[1].DownstreamRoute[0].DownstreamHttpVersion.ShouldBe(_version); + _result[1].DownstreamRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicRoutes[1].ServiceName); + } + + private void GivenTheVersionCreatorReturns() + { + _version = new Version("1.1"); + _versionCreator.Setup(x => x.Create(It.IsAny())).Returns(_version); + } + + private void GivenTheRloCreatorReturns() + { + _rlo1 = new RateLimitOptionsBuilder().Build(); + _rlo2 = new RateLimitOptionsBuilder().WithEnableRateLimiting(true).Build(); + + _rloCreator + .SetupSequence(x => x.Create(It.IsAny(), It.IsAny())) + .Returns(_rlo1) + .Returns(_rlo2); + } + + private void ThenTheRloCreatorIsNotCalled() + { + _rloCreator.Verify(x => x.Create(It.IsAny(), It.IsAny()), Times.Never); + } + + private void ThenNothingIsReturned() + { + _result.Count.ShouldBe(0); + } + + private void WhenICreate() + { + _result = _creator.Create(_fileConfig); + } + + private void GivenThe(FileConfiguration fileConfig) + { + _fileConfig = fileConfig; + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs index bfc0c98c6..e5304ca47 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationPollerTests.cs @@ -1,205 +1,205 @@ -using Moq; -using Ocelot.Configuration; -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Ocelot.Configuration.Repository; -using Ocelot.Logging; -using Ocelot.Responses; -using Ocelot.UnitTests.Responder; -using Shouldly; -using System; -using System.Collections.Generic; -using System.Threading; -using TestStack.BDDfy; -using Xunit; -using static Ocelot.Infrastructure.Wait; - -namespace Ocelot.UnitTests.Configuration -{ - public class FileConfigurationPollerTests : IDisposable - { - private readonly FileConfigurationPoller _poller; - private Mock _factory; - private readonly Mock _repo; - private readonly FileConfiguration _fileConfig; - private Mock _config; - private readonly Mock _internalConfigRepo; - private readonly Mock _internalConfigCreator; - private IInternalConfiguration _internalConfig; - - public FileConfigurationPollerTests() - { - var logger = new Mock(); - _factory = new Mock(); - _factory.Setup(x => x.CreateLogger()).Returns(logger.Object); - _repo = new Mock(); - _fileConfig = new FileConfiguration(); - _config = new Mock(); - _repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse(_fileConfig)); - _config.Setup(x => x.Delay).Returns(100); - _internalConfigRepo = new Mock(); - _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); - } - - [Fact] - public void should_start() - { - this.Given(x => GivenPollerHasStarted()) - .Given(x => ThenTheSetterIsCalled(_fileConfig, 1)) - .BDDfy(); - } - - [Fact] - public void should_call_setter_when_gets_new_config() - { - var newConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "test" - } - }, - } - } - }; - - this.Given(x => GivenPollerHasStarted()) - .Given(x => WhenTheConfigIsChanged(newConfig, 0)) - .Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1)) - .BDDfy(); - } - - [Fact] - public void should_not_poll_if_already_polling() - { - var newConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "test" - } - }, - } - } - }; - - this.Given(x => GivenPollerHasStarted()) - .Given(x => WhenTheConfigIsChanged(newConfig, 10)) - .Then(x => ThenTheSetterIsCalled(newConfig, 1)) - .BDDfy(); - } - - [Fact] - public void should_do_nothing_if_call_to_provider_fails() - { - var newConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "test" - } - }, - } - } - }; - - 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 - .Setup(x => x.Get()) - .ReturnsAsync(new ErrorResponse(new AnyError())); - } - - private void WhenTheConfigIsChanged(FileConfiguration newConfig, int delay) - { - _repo - .Setup(x => x.Get()) - .Callback(() => Thread.Sleep(delay)) - .ReturnsAsync(new OkResponse(newConfig)); - } - - private void WhenPollerIsDisposed() - { - _poller.Dispose(); - } - - private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times) - { - var result = WaitFor(4000).Until(() => - { - try - { - _internalConfigRepo.Verify(x => x.AddOrReplace(_internalConfig), Times.Exactly(times)); - _internalConfigCreator.Verify(x => x.Create(fileConfig), Times.Exactly(times)); - return true; - } - catch (Exception) - { - return false; - } - }); - result.ShouldBeTrue(); - } - - private void ThenTheSetterIsCalledAtLeast(FileConfiguration fileConfig, int times) - { - var result = WaitFor(4000).Until(() => - { - try - { - _internalConfigRepo.Verify(x => x.AddOrReplace(_internalConfig), Times.AtLeast(times)); - _internalConfigCreator.Verify(x => x.Create(fileConfig), Times.AtLeast(times)); - return true; - } - catch (Exception) - { - return false; - } - }); - result.ShouldBeTrue(); - } - - public void Dispose() - { - _poller.Dispose(); - } - } -} +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Ocelot.Configuration.Repository; +using Ocelot.Logging; +using Ocelot.Responses; +using Ocelot.UnitTests.Responder; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Threading; +using TestStack.BDDfy; +using Xunit; +using static Ocelot.Infrastructure.Wait; + +namespace Ocelot.UnitTests.Configuration +{ + public class FileConfigurationPollerTests : IDisposable + { + private readonly FileConfigurationPoller _poller; + private Mock _factory; + private readonly Mock _repo; + private readonly FileConfiguration _fileConfig; + private Mock _config; + private readonly Mock _internalConfigRepo; + private readonly Mock _internalConfigCreator; + private IInternalConfiguration _internalConfig; + + public FileConfigurationPollerTests() + { + var logger = new Mock(); + _factory = new Mock(); + _factory.Setup(x => x.CreateLogger()).Returns(logger.Object); + _repo = new Mock(); + _fileConfig = new FileConfiguration(); + _config = new Mock(); + _repo.Setup(x => x.Get()).ReturnsAsync(new OkResponse(_fileConfig)); + _config.Setup(x => x.Delay).Returns(100); + _internalConfigRepo = new Mock(); + _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); + } + + [Fact] + public void should_start() + { + this.Given(x => GivenPollerHasStarted()) + .Given(x => ThenTheSetterIsCalled(_fileConfig, 1)) + .BDDfy(); + } + + [Fact] + public void should_call_setter_when_gets_new_config() + { + var newConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "test" + } + }, + } + } + }; + + this.Given(x => GivenPollerHasStarted()) + .Given(x => WhenTheConfigIsChanged(newConfig, 0)) + .Then(x => ThenTheSetterIsCalledAtLeast(newConfig, 1)) + .BDDfy(); + } + + [Fact] + public void should_not_poll_if_already_polling() + { + var newConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "test" + } + }, + } + } + }; + + this.Given(x => GivenPollerHasStarted()) + .Given(x => WhenTheConfigIsChanged(newConfig, 10)) + .Then(x => ThenTheSetterIsCalled(newConfig, 1)) + .BDDfy(); + } + + [Fact] + public void should_do_nothing_if_call_to_provider_fails() + { + var newConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "test" + } + }, + } + } + }; + + 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 + .Setup(x => x.Get()) + .ReturnsAsync(new ErrorResponse(new AnyError())); + } + + private void WhenTheConfigIsChanged(FileConfiguration newConfig, int delay) + { + _repo + .Setup(x => x.Get()) + .Callback(() => Thread.Sleep(delay)) + .ReturnsAsync(new OkResponse(newConfig)); + } + + private void WhenPollerIsDisposed() + { + _poller.Dispose(); + } + + private void ThenTheSetterIsCalled(FileConfiguration fileConfig, int times) + { + var result = WaitFor(4000).Until(() => + { + try + { + _internalConfigRepo.Verify(x => x.AddOrReplace(_internalConfig), Times.Exactly(times)); + _internalConfigCreator.Verify(x => x.Create(fileConfig), Times.Exactly(times)); + return true; + } + catch (Exception) + { + return false; + } + }); + result.ShouldBeTrue(); + } + + private void ThenTheSetterIsCalledAtLeast(FileConfiguration fileConfig, int times) + { + var result = WaitFor(4000).Until(() => + { + try + { + _internalConfigRepo.Verify(x => x.AddOrReplace(_internalConfig), Times.AtLeast(times)); + _internalConfigCreator.Verify(x => x.Create(fileConfig), Times.AtLeast(times)); + return true; + } + catch (Exception) + { + return false; + } + }); + result.ShouldBeTrue(); + } + + public void Dispose() + { + _poller.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs index c647f410a..ddde98514 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationSetterTests.cs @@ -40,7 +40,7 @@ public void should_set_configuration() { var fileConfig = new FileConfiguration(); var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - var config = new InternalConfiguration(new List(), string.Empty, serviceProviderConfig, "asdf", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); + var config = new InternalConfiguration(new List(), string.Empty, serviceProviderConfig, "asdf", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); this.Given(x => GivenTheFollowingConfiguration(fileConfig)) .And(x => GivenTheRepoReturns(new OkResponse())) diff --git a/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs index ccf3a677c..9088d08fd 100644 --- a/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileInternalConfigurationCreatorTests.cs @@ -1,127 +1,127 @@ -namespace Ocelot.UnitTests.Configuration -{ - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.Creator; - using Ocelot.Configuration.File; - using Ocelot.Configuration.Validator; - using Ocelot.Errors; - using Ocelot.Responses; - using Ocelot.UnitTests.Responder; - using Shouldly; - using System.Collections.Generic; - using System.Linq; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class FileInternalConfigurationCreatorTests - { - private readonly Mock _validator; - private readonly Mock _reRoutesCreator; - private readonly Mock _aggregatesCreator; - private readonly Mock _dynamicsCreator; - private readonly Mock _configCreator; - private Response _config; - private FileConfiguration _fileConfiguration; - private readonly FileInternalConfigurationCreator _creator; - private Response _result; - private List _reRoutes; - private List _aggregates; - private List _dynamics; - private InternalConfiguration _internalConfig; - - public FileInternalConfigurationCreatorTests() - { - _validator = new Mock(); - _reRoutesCreator = new Mock(); - _aggregatesCreator = new Mock(); - _dynamicsCreator = new Mock(); - _configCreator = new Mock(); - - _creator = new FileInternalConfigurationCreator(_validator.Object, _reRoutesCreator.Object, _aggregatesCreator.Object, _dynamicsCreator.Object, _configCreator.Object); - } - - [Fact] - public void should_return_validation_error() - { - var fileConfiguration = new FileConfiguration(); - - this.Given(_ => GivenThe(fileConfiguration)) - .And(_ => GivenTheValidationFails()) - .When(_ => WhenICreate()) - .Then(_ => ThenAnErrorIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_return_internal_configuration() - { - var fileConfiguration = new FileConfiguration(); - - this.Given(_ => GivenThe(fileConfiguration)) - .And(_ => GivenTheValidationSucceeds()) - .And(_ => GivenTheDependenciesAreSetUp()) - .When(_ => WhenICreate()) - .Then(_ => ThenTheDependenciesAreCalledCorrectly()) - .BDDfy(); - } - - private void ThenTheDependenciesAreCalledCorrectly() - { - _reRoutesCreator.Verify(x => x.Create(_fileConfiguration), Times.Once); - _aggregatesCreator.Verify(x => x.Create(_fileConfiguration, _reRoutes), Times.Once); - _dynamicsCreator.Verify(x => x.Create(_fileConfiguration), Times.Once); - - var mergedReRoutes = _reRoutes - .Union(_aggregates) - .Union(_dynamics) - .ToList(); - - _configCreator.Verify(x => x.Create(_fileConfiguration, It.Is>(y => y.Count == mergedReRoutes.Count)), Times.Once); - } - - private void GivenTheDependenciesAreSetUp() - { - _reRoutes = new List { new ReRouteBuilder().Build() }; - _aggregates = new List { new ReRouteBuilder().Build() }; - _dynamics = new List { new ReRouteBuilder().Build() }; - _internalConfig = new InternalConfiguration(null, "", null, "", null, "", null, null, null); - - _reRoutesCreator.Setup(x => x.Create(It.IsAny())).Returns(_reRoutes); - _aggregatesCreator.Setup(x => x.Create(It.IsAny(), It.IsAny>())).Returns(_aggregates); - _dynamicsCreator.Setup(x => x.Create(It.IsAny())).Returns(_dynamics); - _configCreator.Setup(x => x.Create(It.IsAny(), It.IsAny>())).Returns(_internalConfig); - } - - private void GivenTheValidationSucceeds() - { - var ok = new ConfigurationValidationResult(false); - var response = new OkResponse(ok); - _validator.Setup(x => x.IsValid(It.IsAny())).ReturnsAsync(response); - } - - private void ThenAnErrorIsReturned() - { - _result.IsError.ShouldBeTrue(); - } - - private async Task WhenICreate() - { - _result = await _creator.Create(_fileConfiguration); - } - - private void GivenTheValidationFails() - { - var error = new ConfigurationValidationResult(true, new List { new AnyError() }); - var response = new OkResponse(error); - _validator.Setup(x => x.IsValid(It.IsAny())).ReturnsAsync(response); - } - - private void GivenThe(FileConfiguration fileConfiguration) - { - _fileConfiguration = fileConfiguration; - } - } -} +namespace Ocelot.UnitTests.Configuration +{ + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Ocelot.Configuration.Validator; + using Ocelot.Errors; + using Ocelot.Responses; + using Ocelot.UnitTests.Responder; + using Shouldly; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class FileInternalConfigurationCreatorTests + { + private readonly Mock _validator; + private readonly Mock _routesCreator; + private readonly Mock _aggregatesCreator; + private readonly Mock _dynamicsCreator; + private readonly Mock _configCreator; + private Response _config; + private FileConfiguration _fileConfiguration; + private readonly FileInternalConfigurationCreator _creator; + private Response _result; + private List _routes; + private List _aggregates; + private List _dynamics; + private InternalConfiguration _internalConfig; + + public FileInternalConfigurationCreatorTests() + { + _validator = new Mock(); + _routesCreator = new Mock(); + _aggregatesCreator = new Mock(); + _dynamicsCreator = new Mock(); + _configCreator = new Mock(); + + _creator = new FileInternalConfigurationCreator(_validator.Object, _routesCreator.Object, _aggregatesCreator.Object, _dynamicsCreator.Object, _configCreator.Object); + } + + [Fact] + public void should_return_validation_error() + { + var fileConfiguration = new FileConfiguration(); + + this.Given(_ => GivenThe(fileConfiguration)) + .And(_ => GivenTheValidationFails()) + .When(_ => WhenICreate()) + .Then(_ => ThenAnErrorIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_internal_configuration() + { + var fileConfiguration = new FileConfiguration(); + + this.Given(_ => GivenThe(fileConfiguration)) + .And(_ => GivenTheValidationSucceeds()) + .And(_ => GivenTheDependenciesAreSetUp()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDependenciesAreCalledCorrectly()) + .BDDfy(); + } + + private void ThenTheDependenciesAreCalledCorrectly() + { + _routesCreator.Verify(x => x.Create(_fileConfiguration), Times.Once); + _aggregatesCreator.Verify(x => x.Create(_fileConfiguration, _routes), Times.Once); + _dynamicsCreator.Verify(x => x.Create(_fileConfiguration), Times.Once); + + var mergedRoutes = _routes + .Union(_aggregates) + .Union(_dynamics) + .ToList(); + + _configCreator.Verify(x => x.Create(_fileConfiguration, It.Is>(y => y.Count == mergedRoutes.Count)), Times.Once); + } + + private void GivenTheDependenciesAreSetUp() + { + _routes = new List { new RouteBuilder().Build() }; + _aggregates = new List { new RouteBuilder().Build() }; + _dynamics = new List { new RouteBuilder().Build() }; + _internalConfig = new InternalConfiguration(null, "", null, "", null, "", null, null, null); + + _routesCreator.Setup(x => x.Create(It.IsAny())).Returns(_routes); + _aggregatesCreator.Setup(x => x.Create(It.IsAny(), It.IsAny>())).Returns(_aggregates); + _dynamicsCreator.Setup(x => x.Create(It.IsAny())).Returns(_dynamics); + _configCreator.Setup(x => x.Create(It.IsAny(), It.IsAny>())).Returns(_internalConfig); + } + + private void GivenTheValidationSucceeds() + { + var ok = new ConfigurationValidationResult(false); + var response = new OkResponse(ok); + _validator.Setup(x => x.IsValid(It.IsAny())).ReturnsAsync(response); + } + + private void ThenAnErrorIsReturned() + { + _result.IsError.ShouldBeTrue(); + } + + private async Task WhenICreate() + { + _result = await _creator.Create(_fileConfiguration); + } + + private void GivenTheValidationFails() + { + var error = new ConfigurationValidationResult(true, new List { new AnyError() }); + var response = new OkResponse(error); + _validator.Setup(x => x.IsValid(It.IsAny())).ReturnsAsync(response); + } + + private void GivenThe(FileConfiguration fileConfiguration) + { + _fileConfiguration = fileConfiguration; + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/HeaderFindAndReplaceCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/HeaderFindAndReplaceCreatorTests.cs index 78896bfb9..6221e65f0 100644 --- a/test/Ocelot.UnitTests/Configuration/HeaderFindAndReplaceCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/HeaderFindAndReplaceCreatorTests.cs @@ -16,7 +16,7 @@ namespace Ocelot.UnitTests.Configuration public class HeaderFindAndReplaceCreatorTests { private HeaderFindAndReplaceCreator _creator; - private FileReRoute _reRoute; + private FileRoute _route; private HeaderTransformations _result; private Mock _placeholders; private Mock _factory; @@ -34,13 +34,13 @@ public HeaderFindAndReplaceCreatorTests() [Fact] public void should_create() { - var reRoute = new FileReRoute + var route = new FileRoute { UpstreamHeaderTransform = new Dictionary { {"Test", "Test, Chicken"}, {"Moop", "o, a"} - }, + }, DownstreamHeaderTransform = new Dictionary { {"Pop", "West, East"}, @@ -60,7 +60,7 @@ public void should_create() new HeaderFindAndReplace("Bop", "e", "r", 0) }; - this.Given(x => GivenTheReRoute(reRoute)) + this.Given(x => GivenTheRoute(route)) .When(x => WhenICreate()) .Then(x => ThenTheFollowingUpstreamIsReturned(upstream)) .Then(x => ThenTheFollowingDownstreamIsReturned(downstream)) @@ -71,19 +71,19 @@ public void should_create() public void should_create_with_add_headers_to_request() { const string key = "X-Forwarded-For"; - const string value = "{RemoteIpAddress}"; + const string value = "{RemoteIpAddress}"; - var reRoute = new FileReRoute + var route = new FileRoute { UpstreamHeaderTransform = new Dictionary { {key, value}, } - }; + }; var expected = new AddHeader(key, value); - this.Given(x => GivenTheReRoute(reRoute)) + this.Given(x => GivenTheRoute(route)) .When(x => WhenICreate()) .Then(x => ThenTheFollowingAddHeaderToUpstreamIsReturned(expected)) .BDDfy(); @@ -92,8 +92,8 @@ public void should_create_with_add_headers_to_request() [Fact] public void should_use_base_url_placeholder() { - var reRoute = new FileReRoute - { + var route = new FileRoute + { DownstreamHeaderTransform = new Dictionary { {"Location", "http://www.bbc.co.uk/, {BaseUrl}"}, @@ -105,7 +105,7 @@ public void should_use_base_url_placeholder() new HeaderFindAndReplace("Location", "http://www.bbc.co.uk/", "http://ocelot.com/", 0), }; - this.Given(x => GivenTheReRoute(reRoute)) + this.Given(x => GivenTheRoute(route)) .And(x => GivenTheBaseUrlIs("http://ocelot.com/")) .When(x => WhenICreate()) .Then(x => ThenTheFollowingDownstreamIsReturned(downstream)) @@ -115,7 +115,7 @@ public void should_use_base_url_placeholder() [Fact] public void should_log_errors_and_not_add_headers() { - var reRoute = new FileReRoute + var route = new FileRoute { DownstreamHeaderTransform = new Dictionary { @@ -131,7 +131,7 @@ public void should_log_errors_and_not_add_headers() { }; - this.Given(x => GivenTheReRoute(reRoute)) + this.Given(x => GivenTheRoute(route)) .And(x => GivenTheBaseUrlErrors()) .When(x => WhenICreate()) .Then(x => ThenTheFollowingDownstreamIsReturned(expected)) @@ -149,8 +149,8 @@ private void ThenTheLoggerIsCalledCorrectly(string message) [Fact] public void should_use_base_url_partial_placeholder() { - var reRoute = new FileReRoute - { + var route = new FileRoute + { DownstreamHeaderTransform = new Dictionary { {"Location", "http://www.bbc.co.uk/pay, {BaseUrl}pay"}, @@ -162,7 +162,7 @@ public void should_use_base_url_partial_placeholder() new HeaderFindAndReplace("Location", "http://www.bbc.co.uk/pay", "http://ocelot.com/pay", 0), }; - this.Given(x => GivenTheReRoute(reRoute)) + this.Given(x => GivenTheRoute(route)) .And(x => GivenTheBaseUrlIs("http://ocelot.com/")) .When(x => WhenICreate()) .Then(x => ThenTheFollowingDownstreamIsReturned(downstream)) @@ -172,8 +172,8 @@ public void should_use_base_url_partial_placeholder() [Fact] public void should_add_trace_id_header() { - var reRoute = new FileReRoute - { + var route = new FileRoute + { DownstreamHeaderTransform = new Dictionary { {"Trace-Id", "{TraceId}"}, @@ -182,7 +182,7 @@ public void should_add_trace_id_header() var expected = new AddHeader("Trace-Id", "{TraceId}"); - this.Given(x => GivenTheReRoute(reRoute)) + this.Given(x => GivenTheRoute(route)) .And(x => GivenTheBaseUrlIs("http://ocelot.com/")) .When(x => WhenICreate()) .Then(x => ThenTheFollowingAddHeaderToDownstreamIsReturned(expected)) @@ -192,7 +192,7 @@ public void should_add_trace_id_header() [Fact] public void should_add_downstream_header_as_is_when_no_replacement_is_given() { - var reRoute = new FileReRoute + var route = new FileRoute { DownstreamHeaderTransform = new Dictionary { @@ -202,7 +202,7 @@ public void should_add_downstream_header_as_is_when_no_replacement_is_given() var expected = new AddHeader("X-Custom-Header", "Value"); - this.Given(x => GivenTheReRoute(reRoute)) + this.Given(x => GivenTheRoute(route)) .And(x => WhenICreate()) .Then(x => x.ThenTheFollowingAddHeaderToDownstreamIsReturned(expected)) .BDDfy(); @@ -211,7 +211,7 @@ public void should_add_downstream_header_as_is_when_no_replacement_is_given() [Fact] public void should_add_upstream_header_as_is_when_no_replacement_is_given() { - var reRoute = new FileReRoute + var route = new FileRoute { UpstreamHeaderTransform = new Dictionary { @@ -221,7 +221,7 @@ public void should_add_upstream_header_as_is_when_no_replacement_is_given() var expected = new AddHeader("X-Custom-Header", "Value"); - this.Given(x => GivenTheReRoute(reRoute)) + this.Given(x => GivenTheRoute(route)) .And(x => WhenICreate()) .Then(x => x.ThenTheFollowingAddHeaderToUpstreamIsReturned(expected)) .BDDfy(); @@ -241,8 +241,8 @@ private void ThenTheFollowingAddHeaderToDownstreamIsReturned(AddHeader addHeader { _result.AddHeadersToDownstream[0].Key.ShouldBe(addHeader.Key); _result.AddHeadersToDownstream[0].Value.ShouldBe(addHeader.Value); - } - + } + private void ThenTheFollowingAddHeaderToUpstreamIsReturned(AddHeader addHeader) { _result.AddHeadersToUpstream[0].Key.ShouldBe(addHeader.Key); @@ -251,8 +251,8 @@ private void ThenTheFollowingAddHeaderToUpstreamIsReturned(AddHeader addHeader) private void ThenTheFollowingDownstreamIsReturned(List downstream) { - _result.Downstream.Count.ShouldBe(downstream.Count); - + _result.Downstream.Count.ShouldBe(downstream.Count); + for (int i = 0; i < _result.Downstream.Count; i++) { var result = _result.Downstream[i]; @@ -261,23 +261,23 @@ private void ThenTheFollowingDownstreamIsReturned(List dow result.Index.ShouldBe(expected.Index); result.Key.ShouldBe(expected.Key); result.Replace.ShouldBe(expected.Replace); - } + } } - private void GivenTheReRoute(FileReRoute reRoute) + private void GivenTheRoute(FileRoute route) { - _reRoute = reRoute; + _route = route; } private void WhenICreate() { - _result = _creator.Create(_reRoute); + _result = _creator.Create(_route); } private void ThenTheFollowingUpstreamIsReturned(List expecteds) { - _result.Upstream.Count.ShouldBe(expecteds.Count); - + _result.Upstream.Count.ShouldBe(expecteds.Count); + for (int i = 0; i < _result.Upstream.Count; i++) { var result = _result.Upstream[i]; @@ -289,4 +289,4 @@ private void ThenTheFollowingUpstreamIsReturned(List expec } } } -} +} diff --git a/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs index c4f6eb843..a8c0ee0ec 100644 --- a/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/HttpHandlerOptionsCreatorTests.cs @@ -1,239 +1,239 @@ -using Microsoft.Extensions.DependencyInjection; -using Ocelot.Configuration; -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Shouldly; -using System; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - using Microsoft.AspNetCore.Http; - using Ocelot.Logging; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - - public class HttpHandlerOptionsCreatorTests - { - private IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; - private FileReRoute _fileReRoute; - private HttpHandlerOptions _httpHandlerOptions; - private IServiceProvider _serviceProvider; - private IServiceCollection _serviceCollection; - - public HttpHandlerOptionsCreatorTests() - { - _serviceCollection = new ServiceCollection(); - _serviceProvider = _serviceCollection.BuildServiceProvider(); - _httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(_serviceProvider); - } - - [Fact] - public void should_not_use_tracing_if_fake_tracer_registered() - { - var fileReRoute = new FileReRoute - { - HttpHandlerOptions = new FileHttpHandlerOptions - { - UseTracing = true - } - }; - - 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_use_tracing_if_real_tracer_registered() - { - var fileReRoute = new FileReRoute - { - HttpHandlerOptions = new FileHttpHandlerOptions - { - UseTracing = true - } - }; - - var expectedOptions = new HttpHandlerOptions(false, false, true, true, int.MaxValue); - - this.Given(x => GivenTheFollowing(fileReRoute)) - .And(x => GivenARealTracer()) - .When(x => WhenICreateHttpHandlerOptions()) - .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) - .BDDfy(); - } - - [Fact] - 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, int.MaxValue); - - 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, - UseTracing = false - } - }; - - 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_with_useproxy_true_as_default() - { - var fileReRoute = new FileReRoute - { - HttpHandlerOptions = new FileHttpHandlerOptions() - }; - - 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_with_specified_useproxy() - { - var fileReRoute = new FileReRoute - { - HttpHandlerOptions = new FileHttpHandlerOptions - { - UseProxy = 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()) - .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) - .BDDfy(); - } - - private void GivenTheFollowing(FileReRoute fileReRoute) - { - _fileReRoute = fileReRoute; - } - - private void WhenICreateHttpHandlerOptions() - { - _httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileReRoute.HttpHandlerOptions); - } - - private void ThenTheFollowingOptionsReturned(HttpHandlerOptions expected) - { - _httpHandlerOptions.ShouldNotBeNull(); - _httpHandlerOptions.AllowAutoRedirect.ShouldBe(expected.AllowAutoRedirect); - _httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer); - _httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing); - _httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy); - _httpHandlerOptions.MaxConnectionsPerServer.ShouldBe(expected.MaxConnectionsPerServer); - } - - private void GivenARealTracer() - { - var tracer = new FakeTracer(); - _serviceCollection.AddSingleton(); - _serviceProvider = _serviceCollection.BuildServiceProvider(); - _httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(_serviceProvider); - } - - private class FakeTracer : ITracer - { - public void Event(HttpContext httpContext, string @event) - { - throw new NotImplementedException(); - } - - public Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken, Action addTraceIdToRepo, - Func> baseSendAsync) - { - throw new NotImplementedException(); - } - } - } -} +using Microsoft.Extensions.DependencyInjection; +using Ocelot.Configuration; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Shouldly; +using System; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + using Microsoft.AspNetCore.Http; + using Ocelot.Logging; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + + public class HttpHandlerOptionsCreatorTests + { + private IHttpHandlerOptionsCreator _httpHandlerOptionsCreator; + private FileRoute _fileRoute; + private HttpHandlerOptions _httpHandlerOptions; + private IServiceProvider _serviceProvider; + private IServiceCollection _serviceCollection; + + public HttpHandlerOptionsCreatorTests() + { + _serviceCollection = new ServiceCollection(); + _serviceProvider = _serviceCollection.BuildServiceProvider(); + _httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(_serviceProvider); + } + + [Fact] + public void should_not_use_tracing_if_fake_tracer_registered() + { + var fileRoute = new FileRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + UseTracing = true + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_use_tracing_if_real_tracer_registered() + { + var fileRoute = new FileRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + UseTracing = true + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, true, true, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileRoute)) + .And(x => GivenARealTracer()) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_with_useCookie_false_and_allowAutoRedirect_true_as_default() + { + var fileRoute = new FileRoute(); + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_with_specified_useCookie_and_allowAutoRedirect() + { + var fileRoute = new FileRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + AllowAutoRedirect = false, + UseCookieContainer = false, + UseTracing = false + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_with_useproxy_true_as_default() + { + var fileRoute = new FileRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions() + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_with_specified_useproxy() + { + var fileRoute = new FileRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + UseProxy = false + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, false, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_with_specified_MaxConnectionsPerServer() + { + var fileRoute = new FileRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + MaxConnectionsPerServer = 10 + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, 10); + + this.Given(x => GivenTheFollowing(fileRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_fixing_specified_MaxConnectionsPerServer_range() + { + var fileRoute = new FileRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + MaxConnectionsPerServer = -1 + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_options_fixing_specified_MaxConnectionsPerServer_range_when_zero() + { + var fileRoute = new FileRoute + { + HttpHandlerOptions = new FileHttpHandlerOptions + { + MaxConnectionsPerServer = 0 + } + }; + + var expectedOptions = new HttpHandlerOptions(false, false, false, true, int.MaxValue); + + this.Given(x => GivenTheFollowing(fileRoute)) + .When(x => WhenICreateHttpHandlerOptions()) + .Then(x => ThenTheFollowingOptionsReturned(expectedOptions)) + .BDDfy(); + } + + private void GivenTheFollowing(FileRoute fileRoute) + { + _fileRoute = fileRoute; + } + + private void WhenICreateHttpHandlerOptions() + { + _httpHandlerOptions = _httpHandlerOptionsCreator.Create(_fileRoute.HttpHandlerOptions); + } + + private void ThenTheFollowingOptionsReturned(HttpHandlerOptions expected) + { + _httpHandlerOptions.ShouldNotBeNull(); + _httpHandlerOptions.AllowAutoRedirect.ShouldBe(expected.AllowAutoRedirect); + _httpHandlerOptions.UseCookieContainer.ShouldBe(expected.UseCookieContainer); + _httpHandlerOptions.UseTracing.ShouldBe(expected.UseTracing); + _httpHandlerOptions.UseProxy.ShouldBe(expected.UseProxy); + _httpHandlerOptions.MaxConnectionsPerServer.ShouldBe(expected.MaxConnectionsPerServer); + } + + private void GivenARealTracer() + { + var tracer = new FakeTracer(); + _serviceCollection.AddSingleton(); + _serviceProvider = _serviceCollection.BuildServiceProvider(); + _httpHandlerOptionsCreator = new HttpHandlerOptionsCreator(_serviceProvider); + } + + private class FakeTracer : ITracer + { + public void Event(HttpContext httpContext, string @event) + { + throw new NotImplementedException(); + } + + public Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken, Action addTraceIdToRepo, + Func> baseSendAsync) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs index 4639837e9..949a58538 100644 --- a/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/InMemoryConfigurationRepositoryTests.cs @@ -48,7 +48,7 @@ public void can_get_config() private void ThenTheConfigurationIsReturned() { - _getResult.Data.ReRoutes[0].DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("initial"); + _getResult.Data.Routes[0].DownstreamRoute[0].DownstreamPathTemplate.Value.ShouldBe("initial"); } private void WhenIGetTheConfiguration() @@ -92,19 +92,19 @@ public FakeConfig(string downstreamTemplatePath, string administrationPath) AdministrationPath = administrationPath; } - public List ReRoutes + public List Routes { get { - var downstreamReRoute = new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteBuilder() .WithDownstreamPathTemplate(_downstreamTemplatePath) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - return new List + return new List { - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) .WithUpstreamHttpMethod(new List {"Get"}) .Build() }; diff --git a/test/Ocelot.UnitTests/Configuration/QoSOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/QoSOptionsCreatorTests.cs index 911f30e35..734d68796 100644 --- a/test/Ocelot.UnitTests/Configuration/QoSOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/QoSOptionsCreatorTests.cs @@ -11,22 +11,22 @@ namespace Ocelot.UnitTests.Configuration public class QoSOptionsCreatorTests { private QoSOptionsCreator _creator; - private FileReRoute _fileReRoute; + private FileRoute _fileRoute; private QoSOptions _result; public QoSOptionsCreatorTests() { _creator = new QoSOptionsCreator(); - } - + } + [Fact] public void should_create_qos_options() { - var reRoute = new FileReRoute + var route = new FileRoute { QoSOptions = new FileQoSOptions { - ExceptionsAllowedBeforeBreaking = 1, + ExceptionsAllowedBeforeBreaking = 1, DurationOfBreak = 1, TimeoutValue = 1 } @@ -37,20 +37,20 @@ public void should_create_qos_options() .WithTimeoutValue(1) .Build(); - this.Given(x => x.GivenTheFollowingReRoute(reRoute)) + this.Given(x => x.GivenTheFollowingRoute(route)) .When(x => x.WhenICreate()) .Then(x => x.ThenTheFollowingIsReturned(expected)) .BDDfy(); } - private void GivenTheFollowingReRoute(FileReRoute fileReRoute) + private void GivenTheFollowingRoute(FileRoute fileRoute) { - _fileReRoute = fileReRoute; + _fileRoute = fileRoute; } private void WhenICreate() { - _result = _creator.Create(_fileReRoute.QoSOptions); + _result = _creator.Create(_fileRoute.QoSOptions); } private void ThenTheFollowingIsReturned(QoSOptions expected) @@ -60,4 +60,4 @@ private void ThenTheFollowingIsReturned(QoSOptions expected) _result.TimeoutValue.ShouldBe(expected.TimeoutValue); } } -} +} diff --git a/test/Ocelot.UnitTests/Configuration/RateLimitOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/RateLimitOptionsCreatorTests.cs index a2bc19570..20df81f45 100644 --- a/test/Ocelot.UnitTests/Configuration/RateLimitOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/RateLimitOptionsCreatorTests.cs @@ -1,106 +1,106 @@ -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Shouldly; -using System; -using System.Collections.Generic; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - public class RateLimitOptionsCreatorTests - { - private FileReRoute _fileReRoute; - private FileGlobalConfiguration _fileGlobalConfig; - private bool _enabled; - private RateLimitOptionsCreator _creator; - private RateLimitOptions _result; - - public RateLimitOptionsCreatorTests() - { - _creator = new RateLimitOptionsCreator(); - } - - [Fact] - public void should_create_rate_limit_options() - { - var fileReRoute = new FileReRoute - { - RateLimitOptions = new FileRateLimitRule - { - ClientWhitelist = new List(), - Period = "Period", - Limit = 1, - PeriodTimespan = 1, - EnableRateLimiting = true - } - }; - var fileGlobalConfig = new FileGlobalConfiguration - { - RateLimitOptions = new FileRateLimitOptions - { - ClientIdHeader = "ClientIdHeader", - DisableRateLimitHeaders = true, - QuotaExceededMessage = "QuotaExceededMessage", - RateLimitCounterPrefix = "RateLimitCounterPrefix", - HttpStatusCode = 200 - } - }; - var expected = new RateLimitOptionsBuilder() - .WithClientIdHeader("ClientIdHeader") - .WithClientWhiteList(() => fileReRoute.RateLimitOptions.ClientWhitelist) - .WithDisableRateLimitHeaders(true) - .WithEnableRateLimiting(true) - .WithHttpStatusCode(200) - .WithQuotaExceededMessage("QuotaExceededMessage") - .WithRateLimitCounterPrefix("RateLimitCounterPrefix") - .WithRateLimitRule(new RateLimitRule(fileReRoute.RateLimitOptions.Period, - fileReRoute.RateLimitOptions.PeriodTimespan, - fileReRoute.RateLimitOptions.Limit)) - .Build(); - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .And(x => x.GivenTheFollowingFileGlobalConfig(fileGlobalConfig)) - .And(x => x.GivenRateLimitingIsEnabled()) - .When(x => x.WhenICreate()) - .Then(x => x.ThenTheFollowingIsReturned(expected)) - .BDDfy(); - } - - private void GivenTheFollowingFileReRoute(FileReRoute fileReRoute) - { - _fileReRoute = fileReRoute; - } - - private void GivenTheFollowingFileGlobalConfig(FileGlobalConfiguration fileGlobalConfig) - { - _fileGlobalConfig = fileGlobalConfig; - } - - private void GivenRateLimitingIsEnabled() - { - _enabled = true; - } - - private void WhenICreate() - { - _result = _creator.Create(_fileReRoute.RateLimitOptions, _fileGlobalConfig); - } - - private void ThenTheFollowingIsReturned(RateLimitOptions expected) - { - _result.ClientIdHeader.ShouldBe(expected.ClientIdHeader); - _result.ClientWhitelist.ShouldBe(expected.ClientWhitelist); - _result.DisableRateLimitHeaders.ShouldBe(expected.DisableRateLimitHeaders); - _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); - _result.HttpStatusCode.ShouldBe(expected.HttpStatusCode); - _result.QuotaExceededMessage.ShouldBe(expected.QuotaExceededMessage); - _result.RateLimitCounterPrefix.ShouldBe(expected.RateLimitCounterPrefix); - _result.RateLimitRule.Limit.ShouldBe(expected.RateLimitRule.Limit); - _result.RateLimitRule.Period.ShouldBe(expected.RateLimitRule.Period); - TimeSpan.FromSeconds(_result.RateLimitRule.PeriodTimespan).Ticks.ShouldBe(TimeSpan.FromSeconds(expected.RateLimitRule.PeriodTimespan).Ticks); - } - } -} +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Shouldly; +using System; +using System.Collections.Generic; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + public class RateLimitOptionsCreatorTests + { + private FileRoute _fileRoute; + private FileGlobalConfiguration _fileGlobalConfig; + private bool _enabled; + private RateLimitOptionsCreator _creator; + private RateLimitOptions _result; + + public RateLimitOptionsCreatorTests() + { + _creator = new RateLimitOptionsCreator(); + } + + [Fact] + public void should_create_rate_limit_options() + { + var fileRoute = new FileRoute + { + RateLimitOptions = new FileRateLimitRule + { + ClientWhitelist = new List(), + Period = "Period", + Limit = 1, + PeriodTimespan = 1, + EnableRateLimiting = true + } + }; + var fileGlobalConfig = new FileGlobalConfiguration + { + RateLimitOptions = new FileRateLimitOptions + { + ClientIdHeader = "ClientIdHeader", + DisableRateLimitHeaders = true, + QuotaExceededMessage = "QuotaExceededMessage", + RateLimitCounterPrefix = "RateLimitCounterPrefix", + HttpStatusCode = 200 + } + }; + var expected = new RateLimitOptionsBuilder() + .WithClientIdHeader("ClientIdHeader") + .WithClientWhiteList(() => fileRoute.RateLimitOptions.ClientWhitelist) + .WithDisableRateLimitHeaders(true) + .WithEnableRateLimiting(true) + .WithHttpStatusCode(200) + .WithQuotaExceededMessage("QuotaExceededMessage") + .WithRateLimitCounterPrefix("RateLimitCounterPrefix") + .WithRateLimitRule(new RateLimitRule(fileRoute.RateLimitOptions.Period, + fileRoute.RateLimitOptions.PeriodTimespan, + fileRoute.RateLimitOptions.Limit)) + .Build(); + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .And(x => x.GivenTheFollowingFileGlobalConfig(fileGlobalConfig)) + .And(x => x.GivenRateLimitingIsEnabled()) + .When(x => x.WhenICreate()) + .Then(x => x.ThenTheFollowingIsReturned(expected)) + .BDDfy(); + } + + private void GivenTheFollowingFileRoute(FileRoute fileRoute) + { + _fileRoute = fileRoute; + } + + private void GivenTheFollowingFileGlobalConfig(FileGlobalConfiguration fileGlobalConfig) + { + _fileGlobalConfig = fileGlobalConfig; + } + + private void GivenRateLimitingIsEnabled() + { + _enabled = true; + } + + private void WhenICreate() + { + _result = _creator.Create(_fileRoute.RateLimitOptions, _fileGlobalConfig); + } + + private void ThenTheFollowingIsReturned(RateLimitOptions expected) + { + _result.ClientIdHeader.ShouldBe(expected.ClientIdHeader); + _result.ClientWhitelist.ShouldBe(expected.ClientWhitelist); + _result.DisableRateLimitHeaders.ShouldBe(expected.DisableRateLimitHeaders); + _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); + _result.HttpStatusCode.ShouldBe(expected.HttpStatusCode); + _result.QuotaExceededMessage.ShouldBe(expected.QuotaExceededMessage); + _result.RateLimitCounterPrefix.ShouldBe(expected.RateLimitCounterPrefix); + _result.RateLimitRule.Limit.ShouldBe(expected.RateLimitRule.Limit); + _result.RateLimitRule.Period.ShouldBe(expected.RateLimitRule.Period); + TimeSpan.FromSeconds(_result.RateLimitRule.PeriodTimespan).Ticks.ShouldBe(TimeSpan.FromSeconds(expected.RateLimitRule.PeriodTimespan).Ticks); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/RequestIdKeyCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/RequestIdKeyCreatorTests.cs index bf6ee6dc9..b0dab9aae 100644 --- a/test/Ocelot.UnitTests/Configuration/RequestIdKeyCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/RequestIdKeyCreatorTests.cs @@ -8,7 +8,7 @@ namespace Ocelot.UnitTests.Configuration { public class RequestIdKeyCreatorTests { - private FileReRoute _fileReRoute; + private FileRoute _fileRoute; private FileGlobalConfiguration _fileGlobalConfig; private string _result; private RequestIdKeyCreator _creator; @@ -21,13 +21,13 @@ public RequestIdKeyCreatorTests() [Fact] public void should_use_global_configuration() { - var reRoute = new FileReRoute(); + var route = new FileRoute(); var globalConfig = new FileGlobalConfiguration { RequestIdKey = "cheese" }; - this.Given(x => x.GivenTheFollowingReRoute(reRoute)) + this.Given(x => x.GivenTheFollowingRoute(route)) .And(x => x.GivenTheFollowingGlobalConfig(globalConfig)) .When(x => x.WhenICreate()) .Then(x => x.ThenTheFollowingIsReturned("cheese")) @@ -37,13 +37,13 @@ public void should_use_global_configuration() [Fact] public void should_use_re_route_specific() { - var reRoute = new FileReRoute + var route = new FileRoute { RequestIdKey = "cheese" }; var globalConfig = new FileGlobalConfiguration(); - this.Given(x => x.GivenTheFollowingReRoute(reRoute)) + this.Given(x => x.GivenTheFollowingRoute(route)) .And(x => x.GivenTheFollowingGlobalConfig(globalConfig)) .When(x => x.WhenICreate()) .Then(x => x.ThenTheFollowingIsReturned("cheese")) @@ -53,25 +53,25 @@ public void should_use_re_route_specific() [Fact] public void should_use_re_route_over_global_specific() { - var reRoute = new FileReRoute + var route = new FileRoute { RequestIdKey = "cheese" - }; + }; var globalConfig = new FileGlobalConfiguration { RequestIdKey = "test" }; - this.Given(x => x.GivenTheFollowingReRoute(reRoute)) + this.Given(x => x.GivenTheFollowingRoute(route)) .And(x => x.GivenTheFollowingGlobalConfig(globalConfig)) .When(x => x.WhenICreate()) .Then(x => x.ThenTheFollowingIsReturned("cheese")) .BDDfy(); } - private void GivenTheFollowingReRoute(FileReRoute fileReRoute) + private void GivenTheFollowingRoute(FileRoute fileRoute) { - _fileReRoute = fileReRoute; + _fileRoute = fileRoute; } private void GivenTheFollowingGlobalConfig(FileGlobalConfiguration globalConfig) @@ -81,7 +81,7 @@ private void GivenTheFollowingGlobalConfig(FileGlobalConfiguration globalConfig) private void WhenICreate() { - _result = _creator.Create(_fileReRoute, _fileGlobalConfig); + _result = _creator.Create(_fileRoute, _fileGlobalConfig); } private void ThenTheFollowingIsReturned(string expected) @@ -89,4 +89,4 @@ private void ThenTheFollowingIsReturned(string expected) _result.ShouldBe(expected); } } -} +} diff --git a/test/Ocelot.UnitTests/Configuration/ReRouteKeyCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/RouteKeyCreatorTests.cs similarity index 58% rename from test/Ocelot.UnitTests/Configuration/ReRouteKeyCreatorTests.cs rename to test/Ocelot.UnitTests/Configuration/RouteKeyCreatorTests.cs index 662f5adfb..19ed92370 100644 --- a/test/Ocelot.UnitTests/Configuration/ReRouteKeyCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/RouteKeyCreatorTests.cs @@ -1,84 +1,84 @@ -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Ocelot.LoadBalancer.LoadBalancers; -using Shouldly; -using System.Collections.Generic; -using System.Linq; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - public class ReRouteKeyCreatorTests - { - private ReRouteKeyCreator _creator; - private FileReRoute _reRoute; - private string _result; - - public ReRouteKeyCreatorTests() - { - _creator = new ReRouteKeyCreator(); - } - - [Fact] - public void should_return_sticky_session_key() - { - var reRoute = new FileReRoute - { - LoadBalancerOptions = new FileLoadBalancerOptions - { - Key = "testy", - Type = nameof(CookieStickySessions) - } - }; - - this.Given(_ => GivenThe(reRoute)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheResultIs($"{nameof(CookieStickySessions)}:{reRoute.LoadBalancerOptions.Key}")) - .BDDfy(); - } - - [Fact] - public void should_return_re_route_key() - { - var reRoute = new FileReRoute - { - UpstreamPathTemplate = "/api/product", - UpstreamHttpMethod = new List { "GET", "POST", "PUT" }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 123 - }, - new FileHostAndPort - { - Host = "localhost", - Port = 123 - } - } - }; - - this.Given(_ => GivenThe(reRoute)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheResultIs($"{reRoute.UpstreamPathTemplate}|{string.Join(",", reRoute.UpstreamHttpMethod)}|{string.Join(",", reRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}")) - .BDDfy(); - } - - private void GivenThe(FileReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenICreate() - { - _result = _creator.Create(_reRoute); - } - - private void ThenTheResultIs(string expected) - { - _result.ShouldBe(expected); - } - } -} +namespace Ocelot.UnitTests.Configuration +{ + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Ocelot.LoadBalancer.LoadBalancers; + using Shouldly; + using System.Collections.Generic; + using System.Linq; + using TestStack.BDDfy; + using Xunit; + + public class RouteKeyCreatorTests + { + private RouteKeyCreator _creator; + private FileRoute _route; + private string _result; + + public RouteKeyCreatorTests() + { + _creator = new RouteKeyCreator(); + } + + [Fact] + public void should_return_sticky_session_key() + { + var route = new FileRoute + { + LoadBalancerOptions = new FileLoadBalancerOptions + { + Key = "testy", + Type = nameof(CookieStickySessions) + } + }; + + this.Given(_ => GivenThe(route)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheResultIs($"{nameof(CookieStickySessions)}:{route.LoadBalancerOptions.Key}")) + .BDDfy(); + } + + [Fact] + public void should_return_re_route_key() + { + var route = new FileRoute + { + UpstreamPathTemplate = "/api/product", + UpstreamHttpMethod = new List { "GET", "POST", "PUT" }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 123 + }, + new FileHostAndPort + { + Host = "localhost", + Port = 123 + } + } + }; + + this.Given(_ => GivenThe(route)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheResultIs($"{route.UpstreamPathTemplate}|{string.Join(",", route.UpstreamHttpMethod)}|{string.Join(",", route.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}")) + .BDDfy(); + } + + private void GivenThe(FileRoute route) + { + _route = route; + } + + private void WhenICreate() + { + _result = _creator.Create(_route); + } + + private void ThenTheResultIs(string expected) + { + _result.ShouldBe(expected); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/RouteOptionsCreatorTests.cs similarity index 73% rename from test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs rename to test/Ocelot.UnitTests/Configuration/RouteOptionsCreatorTests.cs index f5c97f713..c5e0d75ef 100644 --- a/test/Ocelot.UnitTests/Configuration/ReRouteOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/RouteOptionsCreatorTests.cs @@ -1,80 +1,80 @@ -namespace Ocelot.UnitTests.Configuration -{ - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.Creator; - using Ocelot.Configuration.File; - using Shouldly; - using System.Collections.Generic; - using TestStack.BDDfy; - using Xunit; - - public class ReRouteOptionsCreatorTests - { - private readonly ReRouteOptionsCreator _creator; - private FileReRoute _reRoute; - private ReRouteOptions _result; - - public ReRouteOptionsCreatorTests() - { - _creator = new ReRouteOptionsCreator(); - } - - [Fact] - public void should_create_re_route_options() - { - var reRoute = new FileReRoute - { - RateLimitOptions = new FileRateLimitRule - { - EnableRateLimiting = true - }, - AuthenticationOptions = new FileAuthenticationOptions() - { - AuthenticationProviderKey = "Test" - }, - RouteClaimsRequirement = new Dictionary() - { - {"",""} - }, - FileCacheOptions = new FileCacheOptions - { - TtlSeconds = 1 - }, - ServiceName = "west" - }; - - var expected = new ReRouteOptionsBuilder() - .WithIsAuthenticated(true) - .WithIsAuthorised(true) - .WithIsCached(true) - .WithRateLimiting(true) - .WithUseServiceDiscovery(true) - .Build(); - - this.Given(x => x.GivenTheFollowing(reRoute)) - .When(x => x.WhenICreate()) - .Then(x => x.ThenTheFollowingIsReturned(expected)) - .BDDfy(); - } - - private void GivenTheFollowing(FileReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenICreate() - { - _result = _creator.Create(_reRoute); - } - - private void ThenTheFollowingIsReturned(ReRouteOptions expected) - { - _result.IsAuthenticated.ShouldBe(expected.IsAuthenticated); - _result.IsAuthorised.ShouldBe(expected.IsAuthorised); - _result.IsCached.ShouldBe(expected.IsCached); - _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); - _result.UseServiceDiscovery.ShouldBe(expected.UseServiceDiscovery); - } - } -} +namespace Ocelot.UnitTests.Configuration +{ + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Shouldly; + using System.Collections.Generic; + using TestStack.BDDfy; + using Xunit; + + public class RouteOptionsCreatorTests + { + private readonly RouteOptionsCreator _creator; + private FileRoute _route; + private RouteOptions _result; + + public RouteOptionsCreatorTests() + { + _creator = new RouteOptionsCreator(); + } + + [Fact] + public void should_create_re_route_options() + { + var route = new FileRoute + { + RateLimitOptions = new FileRateLimitRule + { + EnableRateLimiting = true + }, + AuthenticationOptions = new FileAuthenticationOptions() + { + AuthenticationProviderKey = "Test" + }, + RouteClaimsRequirement = new Dictionary() + { + {"",""} + }, + FileCacheOptions = new FileCacheOptions + { + TtlSeconds = 1 + }, + ServiceName = "west" + }; + + var expected = new RouteOptionsBuilder() + .WithIsAuthenticated(true) + .WithIsAuthorised(true) + .WithIsCached(true) + .WithRateLimiting(true) + .WithUseServiceDiscovery(true) + .Build(); + + this.Given(x => x.GivenTheFollowing(route)) + .When(x => x.WhenICreate()) + .Then(x => x.ThenTheFollowingIsReturned(expected)) + .BDDfy(); + } + + private void GivenTheFollowing(FileRoute route) + { + _route = route; + } + + private void WhenICreate() + { + _result = _creator.Create(_route); + } + + private void ThenTheFollowingIsReturned(RouteOptions expected) + { + _result.IsAuthenticated.ShouldBe(expected.IsAuthenticated); + _result.IsAuthorised.ShouldBe(expected.IsAuthorised); + _result.IsCached.ShouldBe(expected.IsCached); + _result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); + _result.UseServiceDiscovery.ShouldBe(expected.UseServiceDiscovery); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/RoutesCreatorTests.cs similarity index 52% rename from test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs rename to test/Ocelot.UnitTests/Configuration/RoutesCreatorTests.cs index 47bc07c86..81500e51f 100644 --- a/test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/RoutesCreatorTests.cs @@ -1,283 +1,283 @@ -namespace Ocelot.UnitTests.Configuration -{ - using System; - using Moq; - using Ocelot.Cache; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.Creator; - using Ocelot.Configuration.File; - using Ocelot.Values; - using Shouldly; - using System.Collections.Generic; - using System.Linq; - using TestStack.BDDfy; - using Xunit; - - public class ReRoutesCreatorTests - { - private ReRoutesCreator _creator; - private Mock _cthCreator; - private Mock _aoCreator; - private Mock _utpCreator; - private Mock _ridkCreator; - private Mock _qosoCreator; - private Mock _rroCreator; - private Mock _rloCreator; - private Mock _rCreator; - private Mock _hhoCreator; - private Mock _hfarCreator; - private Mock _daCreator; - private Mock _lboCreator; - private Mock _rrkCreator; - private Mock _soCreator; - private Mock _versionCreator; - private FileConfiguration _fileConfig; - private ReRouteOptions _rro; - private string _requestId; - private string _rrk; - private UpstreamPathTemplate _upt; - private AuthenticationOptions _ao; - private List _ctt; - private QoSOptions _qoso; - private RateLimitOptions _rlo; - private string _region; - private HttpHandlerOptions _hho; - private HeaderTransformations _ht; - private List _dhp; - private LoadBalancerOptions _lbo; - private List _result; - private SecurityOptions _securityOptions; - private Version _expectedVersion; - - public ReRoutesCreatorTests() - { - _cthCreator = new Mock(); - _aoCreator = new Mock(); - _utpCreator = new Mock(); - _ridkCreator = new Mock(); - _qosoCreator = new Mock(); - _rroCreator = new Mock(); - _rloCreator = new Mock(); - _rCreator = new Mock(); - _hhoCreator = new Mock(); - _hfarCreator = new Mock(); - _daCreator = new Mock(); - _lboCreator = new Mock(); - _rrkCreator = new Mock(); - _soCreator = new Mock(); - _versionCreator = new Mock(); - - _creator = new ReRoutesCreator( - _cthCreator.Object, - _aoCreator.Object, - _utpCreator.Object, - _ridkCreator.Object, - _qosoCreator.Object, - _rroCreator.Object, - _rloCreator.Object, - _rCreator.Object, - _hhoCreator.Object, - _hfarCreator.Object, - _daCreator.Object, - _lboCreator.Object, - _rrkCreator.Object, - _soCreator.Object, - _versionCreator.Object - ); - } - - [Fact] - public void should_return_nothing() - { - var fileConfig = new FileConfiguration(); - - this.Given(_ => GivenThe(fileConfig)) - .When(_ => WhenICreate()) - .Then(_ => ThenNoReRoutesAreReturned()) - .BDDfy(); - } - - [Fact] - public void should_return_re_routes() - { - var fileConfig = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - ServiceName = "dave", - DangerousAcceptAnyServerCertificateValidator = true, - AddClaimsToRequest = new Dictionary - { - { "a","b" } - }, - AddHeadersToRequest = new Dictionary - { - { "c","d" } - }, - AddQueriesToRequest = new Dictionary - { - { "e","f" } - }, - UpstreamHttpMethod = new List { "GET", "POST" } - }, - new FileReRoute - { - ServiceName = "wave", - DangerousAcceptAnyServerCertificateValidator = false, - AddClaimsToRequest = new Dictionary - { - { "g","h" } - }, - AddHeadersToRequest = new Dictionary - { - { "i","j" } - }, - AddQueriesToRequest = new Dictionary - { - { "k","l" } - }, - UpstreamHttpMethod = new List { "PUT", "DELETE" } - } - } - }; - - this.Given(_ => GivenThe(fileConfig)) - .And(_ => GivenTheDependenciesAreSetUpCorrectly()) - .When(_ => WhenICreate()) - .Then(_ => ThenTheDependenciesAreCalledCorrectly()) - .And(_ => ThenTheReRoutesAreCreated()) - .BDDfy(); - } - - private void ThenTheDependenciesAreCalledCorrectly() - { - ThenTheDepsAreCalledFor(_fileConfig.ReRoutes[0], _fileConfig.GlobalConfiguration); - ThenTheDepsAreCalledFor(_fileConfig.ReRoutes[1], _fileConfig.GlobalConfiguration); - } - - private void GivenTheDependenciesAreSetUpCorrectly() - { - _expectedVersion = new Version("1.1"); - _rro = new ReRouteOptions(false, false, false, false, false); - _requestId = "testy"; - _rrk = "besty"; - _upt = new UpstreamPathTemplateBuilder().Build(); - _ao = new AuthenticationOptionsBuilder().Build(); - _ctt = new List(); - _qoso = new QoSOptionsBuilder().Build(); - _rlo = new RateLimitOptionsBuilder().Build(); - _region = "vesty"; - _hho = new HttpHandlerOptionsBuilder().Build(); - _ht = new HeaderTransformations(new List(), new List(), new List(), new List()); - _dhp = new List(); - _lbo = new LoadBalancerOptionsBuilder().Build(); - - _rroCreator.Setup(x => x.Create(It.IsAny())).Returns(_rro); - _ridkCreator.Setup(x => x.Create(It.IsAny(), It.IsAny())).Returns(_requestId); - _rrkCreator.Setup(x => x.Create(It.IsAny())).Returns(_rrk); - _utpCreator.Setup(x => x.Create(It.IsAny())).Returns(_upt); - _aoCreator.Setup(x => x.Create(It.IsAny())).Returns(_ao); - _cthCreator.Setup(x => x.Create(It.IsAny>())).Returns(_ctt); - _qosoCreator.Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())).Returns(_qoso); - _rloCreator.Setup(x => x.Create(It.IsAny(), It.IsAny())).Returns(_rlo); - _rCreator.Setup(x => x.Create(It.IsAny())).Returns(_region); - _hhoCreator.Setup(x => x.Create(It.IsAny())).Returns(_hho); - _hfarCreator.Setup(x => x.Create(It.IsAny())).Returns(_ht); - _daCreator.Setup(x => x.Create(It.IsAny())).Returns(_dhp); - _lboCreator.Setup(x => x.Create(It.IsAny())).Returns(_lbo); - _versionCreator.Setup(x => x.Create(It.IsAny())).Returns(_expectedVersion); - } - - private void ThenTheReRoutesAreCreated() - { - _result.Count.ShouldBe(2); - - ThenTheReRouteIsSet(_fileConfig.ReRoutes[0], 0); - ThenTheReRouteIsSet(_fileConfig.ReRoutes[1], 1); - } - - private void ThenNoReRoutesAreReturned() - { - _result.ShouldBeEmpty(); - } - - private void GivenThe(FileConfiguration fileConfig) - { - _fileConfig = fileConfig; - } - - private void WhenICreate() - { - _result = _creator.Create(_fileConfig); - } - - private void ThenTheReRouteIsSet(FileReRoute expected, int reRouteIndex) - { - _result[reRouteIndex].DownstreamReRoute[0].DownstreamHttpVersion.ShouldBe(_expectedVersion); - _result[reRouteIndex].DownstreamReRoute[0].IsAuthenticated.ShouldBe(_rro.IsAuthenticated); - _result[reRouteIndex].DownstreamReRoute[0].IsAuthorised.ShouldBe(_rro.IsAuthorised); - _result[reRouteIndex].DownstreamReRoute[0].IsCached.ShouldBe(_rro.IsCached); - _result[reRouteIndex].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBe(_rro.EnableRateLimiting); - _result[reRouteIndex].DownstreamReRoute[0].RequestIdKey.ShouldBe(_requestId); - _result[reRouteIndex].DownstreamReRoute[0].LoadBalancerKey.ShouldBe(_rrk); - _result[reRouteIndex].DownstreamReRoute[0].UpstreamPathTemplate.ShouldBe(_upt); - _result[reRouteIndex].DownstreamReRoute[0].AuthenticationOptions.ShouldBe(_ao); - _result[reRouteIndex].DownstreamReRoute[0].ClaimsToHeaders.ShouldBe(_ctt); - _result[reRouteIndex].DownstreamReRoute[0].ClaimsToQueries.ShouldBe(_ctt); - _result[reRouteIndex].DownstreamReRoute[0].ClaimsToClaims.ShouldBe(_ctt); - _result[reRouteIndex].DownstreamReRoute[0].QosOptions.ShouldBe(_qoso); - _result[reRouteIndex].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo); - _result[reRouteIndex].DownstreamReRoute[0].CacheOptions.Region.ShouldBe(_region); - _result[reRouteIndex].DownstreamReRoute[0].CacheOptions.TtlSeconds.ShouldBe(expected.FileCacheOptions.TtlSeconds); - _result[reRouteIndex].DownstreamReRoute[0].HttpHandlerOptions.ShouldBe(_hho); - _result[reRouteIndex].DownstreamReRoute[0].UpstreamHeadersFindAndReplace.ShouldBe(_ht.Upstream); - _result[reRouteIndex].DownstreamReRoute[0].DownstreamHeadersFindAndReplace.ShouldBe(_ht.Downstream); - _result[reRouteIndex].DownstreamReRoute[0].AddHeadersToUpstream.ShouldBe(_ht.AddHeadersToUpstream); - _result[reRouteIndex].DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(_ht.AddHeadersToDownstream); - _result[reRouteIndex].DownstreamReRoute[0].DownstreamAddresses.ShouldBe(_dhp); - _result[reRouteIndex].DownstreamReRoute[0].LoadBalancerOptions.ShouldBe(_lbo); - _result[reRouteIndex].DownstreamReRoute[0].UseServiceDiscovery.ShouldBe(_rro.UseServiceDiscovery); - _result[reRouteIndex].DownstreamReRoute[0].DangerousAcceptAnyServerCertificateValidator.ShouldBe(expected.DangerousAcceptAnyServerCertificateValidator); - _result[reRouteIndex].DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DelegatingHandlers); - _result[reRouteIndex].DownstreamReRoute[0].ServiceName.ShouldBe(expected.ServiceName); - _result[reRouteIndex].DownstreamReRoute[0].DownstreamScheme.ShouldBe(expected.DownstreamScheme); - _result[reRouteIndex].DownstreamReRoute[0].RouteClaimsRequirement.ShouldBe(expected.RouteClaimsRequirement); - _result[reRouteIndex].DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate); - _result[reRouteIndex].DownstreamReRoute[0].Key.ShouldBe(expected.Key); - _result[reRouteIndex].UpstreamHttpMethod - .Select(x => x.Method) - .ToList() - .ShouldContain(x => x == expected.UpstreamHttpMethod[0]); - _result[reRouteIndex].UpstreamHttpMethod - .Select(x => x.Method) - .ToList() - .ShouldContain(x => x == expected.UpstreamHttpMethod[1]); - _result[reRouteIndex].UpstreamHost.ShouldBe(expected.UpstreamHost); - _result[reRouteIndex].DownstreamReRoute.Count.ShouldBe(1); - _result[reRouteIndex].UpstreamTemplatePattern.ShouldBe(_upt); - } - - private void ThenTheDepsAreCalledFor(FileReRoute fileReRoute, FileGlobalConfiguration globalConfig) - { - _rroCreator.Verify(x => x.Create(fileReRoute), Times.Once); - _ridkCreator.Verify(x => x.Create(fileReRoute, globalConfig), Times.Once); - _rrkCreator.Verify(x => x.Create(fileReRoute), Times.Once); - _utpCreator.Verify(x => x.Create(fileReRoute), Times.Exactly(2)); - _aoCreator.Verify(x => x.Create(fileReRoute), Times.Once); - _cthCreator.Verify(x => x.Create(fileReRoute.AddHeadersToRequest), Times.Once); - _cthCreator.Verify(x => x.Create(fileReRoute.AddClaimsToRequest), Times.Once); - _cthCreator.Verify(x => x.Create(fileReRoute.AddQueriesToRequest), Times.Once); - _qosoCreator.Verify(x => x.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod)); - _rloCreator.Verify(x => x.Create(fileReRoute.RateLimitOptions, globalConfig), Times.Once); - _rCreator.Verify(x => x.Create(fileReRoute), Times.Once); - _hhoCreator.Verify(x => x.Create(fileReRoute.HttpHandlerOptions), Times.Once); - _hfarCreator.Verify(x => x.Create(fileReRoute), Times.Once); - _daCreator.Verify(x => x.Create(fileReRoute), Times.Once); - _lboCreator.Verify(x => x.Create(fileReRoute.LoadBalancerOptions), Times.Once); - _soCreator.Verify(x => x.Create(fileReRoute.SecurityOptions), Times.Once); - } - } -} +namespace Ocelot.UnitTests.Configuration +{ + using System; + using Moq; + using Ocelot.Cache; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.Configuration.File; + using Ocelot.Values; + using Shouldly; + using System.Collections.Generic; + using System.Linq; + using TestStack.BDDfy; + using Xunit; + + public class RoutesCreatorTests + { + private RoutesCreator _creator; + private Mock _cthCreator; + private Mock _aoCreator; + private Mock _utpCreator; + private Mock _ridkCreator; + private Mock _qosoCreator; + private Mock _rroCreator; + private Mock _rloCreator; + private Mock _rCreator; + private Mock _hhoCreator; + private Mock _hfarCreator; + private Mock _daCreator; + private Mock _lboCreator; + private Mock _rrkCreator; + private Mock _soCreator; + private Mock _versionCreator; + private FileConfiguration _fileConfig; + private RouteOptions _rro; + private string _requestId; + private string _rrk; + private UpstreamPathTemplate _upt; + private AuthenticationOptions _ao; + private List _ctt; + private QoSOptions _qoso; + private RateLimitOptions _rlo; + private string _region; + private HttpHandlerOptions _hho; + private HeaderTransformations _ht; + private List _dhp; + private LoadBalancerOptions _lbo; + private List _result; + private SecurityOptions _securityOptions; + private Version _expectedVersion; + + public RoutesCreatorTests() + { + _cthCreator = new Mock(); + _aoCreator = new Mock(); + _utpCreator = new Mock(); + _ridkCreator = new Mock(); + _qosoCreator = new Mock(); + _rroCreator = new Mock(); + _rloCreator = new Mock(); + _rCreator = new Mock(); + _hhoCreator = new Mock(); + _hfarCreator = new Mock(); + _daCreator = new Mock(); + _lboCreator = new Mock(); + _rrkCreator = new Mock(); + _soCreator = new Mock(); + _versionCreator = new Mock(); + + _creator = new RoutesCreator( + _cthCreator.Object, + _aoCreator.Object, + _utpCreator.Object, + _ridkCreator.Object, + _qosoCreator.Object, + _rroCreator.Object, + _rloCreator.Object, + _rCreator.Object, + _hhoCreator.Object, + _hfarCreator.Object, + _daCreator.Object, + _lboCreator.Object, + _rrkCreator.Object, + _soCreator.Object, + _versionCreator.Object + ); + } + + [Fact] + public void should_return_nothing() + { + var fileConfig = new FileConfiguration(); + + this.Given(_ => GivenThe(fileConfig)) + .When(_ => WhenICreate()) + .Then(_ => ThenNoRoutesAreReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_re_routes() + { + var fileConfig = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + ServiceName = "dave", + DangerousAcceptAnyServerCertificateValidator = true, + AddClaimsToRequest = new Dictionary + { + { "a","b" } + }, + AddHeadersToRequest = new Dictionary + { + { "c","d" } + }, + AddQueriesToRequest = new Dictionary + { + { "e","f" } + }, + UpstreamHttpMethod = new List { "GET", "POST" } + }, + new FileRoute + { + ServiceName = "wave", + DangerousAcceptAnyServerCertificateValidator = false, + AddClaimsToRequest = new Dictionary + { + { "g","h" } + }, + AddHeadersToRequest = new Dictionary + { + { "i","j" } + }, + AddQueriesToRequest = new Dictionary + { + { "k","l" } + }, + UpstreamHttpMethod = new List { "PUT", "DELETE" } + } + } + }; + + this.Given(_ => GivenThe(fileConfig)) + .And(_ => GivenTheDependenciesAreSetUpCorrectly()) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDependenciesAreCalledCorrectly()) + .And(_ => ThenTheRoutesAreCreated()) + .BDDfy(); + } + + private void ThenTheDependenciesAreCalledCorrectly() + { + ThenTheDepsAreCalledFor(_fileConfig.Routes[0], _fileConfig.GlobalConfiguration); + ThenTheDepsAreCalledFor(_fileConfig.Routes[1], _fileConfig.GlobalConfiguration); + } + + private void GivenTheDependenciesAreSetUpCorrectly() + { + _expectedVersion = new Version("1.1"); + _rro = new RouteOptions(false, false, false, false, false); + _requestId = "testy"; + _rrk = "besty"; + _upt = new UpstreamPathTemplateBuilder().Build(); + _ao = new AuthenticationOptionsBuilder().Build(); + _ctt = new List(); + _qoso = new QoSOptionsBuilder().Build(); + _rlo = new RateLimitOptionsBuilder().Build(); + _region = "vesty"; + _hho = new HttpHandlerOptionsBuilder().Build(); + _ht = new HeaderTransformations(new List(), new List(), new List(), new List()); + _dhp = new List(); + _lbo = new LoadBalancerOptionsBuilder().Build(); + + _rroCreator.Setup(x => x.Create(It.IsAny())).Returns(_rro); + _ridkCreator.Setup(x => x.Create(It.IsAny(), It.IsAny())).Returns(_requestId); + _rrkCreator.Setup(x => x.Create(It.IsAny())).Returns(_rrk); + _utpCreator.Setup(x => x.Create(It.IsAny())).Returns(_upt); + _aoCreator.Setup(x => x.Create(It.IsAny())).Returns(_ao); + _cthCreator.Setup(x => x.Create(It.IsAny>())).Returns(_ctt); + _qosoCreator.Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())).Returns(_qoso); + _rloCreator.Setup(x => x.Create(It.IsAny(), It.IsAny())).Returns(_rlo); + _rCreator.Setup(x => x.Create(It.IsAny())).Returns(_region); + _hhoCreator.Setup(x => x.Create(It.IsAny())).Returns(_hho); + _hfarCreator.Setup(x => x.Create(It.IsAny())).Returns(_ht); + _daCreator.Setup(x => x.Create(It.IsAny())).Returns(_dhp); + _lboCreator.Setup(x => x.Create(It.IsAny())).Returns(_lbo); + _versionCreator.Setup(x => x.Create(It.IsAny())).Returns(_expectedVersion); + } + + private void ThenTheRoutesAreCreated() + { + _result.Count.ShouldBe(2); + + ThenTheRouteIsSet(_fileConfig.Routes[0], 0); + ThenTheRouteIsSet(_fileConfig.Routes[1], 1); + } + + private void ThenNoRoutesAreReturned() + { + _result.ShouldBeEmpty(); + } + + private void GivenThe(FileConfiguration fileConfig) + { + _fileConfig = fileConfig; + } + + private void WhenICreate() + { + _result = _creator.Create(_fileConfig); + } + + private void ThenTheRouteIsSet(FileRoute expected, int routeIndex) + { + _result[routeIndex].DownstreamRoute[0].DownstreamHttpVersion.ShouldBe(_expectedVersion); + _result[routeIndex].DownstreamRoute[0].IsAuthenticated.ShouldBe(_rro.IsAuthenticated); + _result[routeIndex].DownstreamRoute[0].IsAuthorised.ShouldBe(_rro.IsAuthorised); + _result[routeIndex].DownstreamRoute[0].IsCached.ShouldBe(_rro.IsCached); + _result[routeIndex].DownstreamRoute[0].EnableEndpointEndpointRateLimiting.ShouldBe(_rro.EnableRateLimiting); + _result[routeIndex].DownstreamRoute[0].RequestIdKey.ShouldBe(_requestId); + _result[routeIndex].DownstreamRoute[0].LoadBalancerKey.ShouldBe(_rrk); + _result[routeIndex].DownstreamRoute[0].UpstreamPathTemplate.ShouldBe(_upt); + _result[routeIndex].DownstreamRoute[0].AuthenticationOptions.ShouldBe(_ao); + _result[routeIndex].DownstreamRoute[0].ClaimsToHeaders.ShouldBe(_ctt); + _result[routeIndex].DownstreamRoute[0].ClaimsToQueries.ShouldBe(_ctt); + _result[routeIndex].DownstreamRoute[0].ClaimsToClaims.ShouldBe(_ctt); + _result[routeIndex].DownstreamRoute[0].QosOptions.ShouldBe(_qoso); + _result[routeIndex].DownstreamRoute[0].RateLimitOptions.ShouldBe(_rlo); + _result[routeIndex].DownstreamRoute[0].CacheOptions.Region.ShouldBe(_region); + _result[routeIndex].DownstreamRoute[0].CacheOptions.TtlSeconds.ShouldBe(expected.FileCacheOptions.TtlSeconds); + _result[routeIndex].DownstreamRoute[0].HttpHandlerOptions.ShouldBe(_hho); + _result[routeIndex].DownstreamRoute[0].UpstreamHeadersFindAndReplace.ShouldBe(_ht.Upstream); + _result[routeIndex].DownstreamRoute[0].DownstreamHeadersFindAndReplace.ShouldBe(_ht.Downstream); + _result[routeIndex].DownstreamRoute[0].AddHeadersToUpstream.ShouldBe(_ht.AddHeadersToUpstream); + _result[routeIndex].DownstreamRoute[0].AddHeadersToDownstream.ShouldBe(_ht.AddHeadersToDownstream); + _result[routeIndex].DownstreamRoute[0].DownstreamAddresses.ShouldBe(_dhp); + _result[routeIndex].DownstreamRoute[0].LoadBalancerOptions.ShouldBe(_lbo); + _result[routeIndex].DownstreamRoute[0].UseServiceDiscovery.ShouldBe(_rro.UseServiceDiscovery); + _result[routeIndex].DownstreamRoute[0].DangerousAcceptAnyServerCertificateValidator.ShouldBe(expected.DangerousAcceptAnyServerCertificateValidator); + _result[routeIndex].DownstreamRoute[0].DelegatingHandlers.ShouldBe(expected.DelegatingHandlers); + _result[routeIndex].DownstreamRoute[0].ServiceName.ShouldBe(expected.ServiceName); + _result[routeIndex].DownstreamRoute[0].DownstreamScheme.ShouldBe(expected.DownstreamScheme); + _result[routeIndex].DownstreamRoute[0].RouteClaimsRequirement.ShouldBe(expected.RouteClaimsRequirement); + _result[routeIndex].DownstreamRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate); + _result[routeIndex].DownstreamRoute[0].Key.ShouldBe(expected.Key); + _result[routeIndex].UpstreamHttpMethod + .Select(x => x.Method) + .ToList() + .ShouldContain(x => x == expected.UpstreamHttpMethod[0]); + _result[routeIndex].UpstreamHttpMethod + .Select(x => x.Method) + .ToList() + .ShouldContain(x => x == expected.UpstreamHttpMethod[1]); + _result[routeIndex].UpstreamHost.ShouldBe(expected.UpstreamHost); + _result[routeIndex].DownstreamRoute.Count.ShouldBe(1); + _result[routeIndex].UpstreamTemplatePattern.ShouldBe(_upt); + } + + private void ThenTheDepsAreCalledFor(FileRoute fileRoute, FileGlobalConfiguration globalConfig) + { + _rroCreator.Verify(x => x.Create(fileRoute), Times.Once); + _ridkCreator.Verify(x => x.Create(fileRoute, globalConfig), Times.Once); + _rrkCreator.Verify(x => x.Create(fileRoute), Times.Once); + _utpCreator.Verify(x => x.Create(fileRoute), Times.Exactly(2)); + _aoCreator.Verify(x => x.Create(fileRoute), Times.Once); + _cthCreator.Verify(x => x.Create(fileRoute.AddHeadersToRequest), Times.Once); + _cthCreator.Verify(x => x.Create(fileRoute.AddClaimsToRequest), Times.Once); + _cthCreator.Verify(x => x.Create(fileRoute.AddQueriesToRequest), Times.Once); + _qosoCreator.Verify(x => x.Create(fileRoute.QoSOptions, fileRoute.UpstreamPathTemplate, fileRoute.UpstreamHttpMethod)); + _rloCreator.Verify(x => x.Create(fileRoute.RateLimitOptions, globalConfig), Times.Once); + _rCreator.Verify(x => x.Create(fileRoute), Times.Once); + _hhoCreator.Verify(x => x.Create(fileRoute.HttpHandlerOptions), Times.Once); + _hfarCreator.Verify(x => x.Create(fileRoute), Times.Once); + _daCreator.Verify(x => x.Create(fileRoute), Times.Once); + _lboCreator.Verify(x => x.Create(fileRoute.LoadBalancerOptions), Times.Once); + _soCreator.Verify(x => x.Create(fileRoute.SecurityOptions), Times.Once); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/SecurityOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/SecurityOptionsCreatorTests.cs index e79871eba..f94d0bb22 100644 --- a/test/Ocelot.UnitTests/Configuration/SecurityOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/SecurityOptionsCreatorTests.cs @@ -1,68 +1,68 @@ -using Ocelot.Configuration; -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Shouldly; -using System.Collections.Generic; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - public class SecurityOptionsCreatorTests - { - private FileReRoute _fileReRoute; - private FileGlobalConfiguration _fileGlobalConfig; - private SecurityOptions _result; - private ISecurityOptionsCreator _creator; - - public SecurityOptionsCreatorTests() - { - _creator = new SecurityOptionsCreator(); - } - - [Fact] - public void should_create_security_config() - { - var ipAllowedList = new List() { "127.0.0.1", "192.168.1.1" }; - var ipBlockedList = new List() { "127.0.0.1", "192.168.1.1" }; - var fileReRoute = new FileReRoute - { - SecurityOptions = new FileSecurityOptions() - { - IPAllowedList = ipAllowedList, - IPBlockedList = ipBlockedList - } - }; - - var expected = new SecurityOptions(ipAllowedList, ipBlockedList); - - this.Given(x => x.GivenThe(fileReRoute)) - .When(x => x.WhenICreate()) - .Then(x => x.ThenTheResultIs(expected)) - .BDDfy(); - } - - private void GivenThe(FileReRoute reRoute) - { - _fileReRoute = reRoute; - } - - private void WhenICreate() - { - _result = _creator.Create(_fileReRoute.SecurityOptions); - } - - private void ThenTheResultIs(SecurityOptions expected) - { - for (int i = 0; i < expected.IPAllowedList.Count; i++) - { - _result.IPAllowedList[i].ShouldBe(expected.IPAllowedList[i]); - } - - for (int i = 0; i < expected.IPBlockedList.Count; i++) - { - _result.IPBlockedList[i].ShouldBe(expected.IPBlockedList[i]); - } - } - } -} +using Ocelot.Configuration; +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Shouldly; +using System.Collections.Generic; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + public class SecurityOptionsCreatorTests + { + private FileRoute _fileRoute; + private FileGlobalConfiguration _fileGlobalConfig; + private SecurityOptions _result; + private ISecurityOptionsCreator _creator; + + public SecurityOptionsCreatorTests() + { + _creator = new SecurityOptionsCreator(); + } + + [Fact] + public void should_create_security_config() + { + var ipAllowedList = new List() { "127.0.0.1", "192.168.1.1" }; + var ipBlockedList = new List() { "127.0.0.1", "192.168.1.1" }; + var fileRoute = new FileRoute + { + SecurityOptions = new FileSecurityOptions() + { + IPAllowedList = ipAllowedList, + IPBlockedList = ipBlockedList + } + }; + + var expected = new SecurityOptions(ipAllowedList, ipBlockedList); + + this.Given(x => x.GivenThe(fileRoute)) + .When(x => x.WhenICreate()) + .Then(x => x.ThenTheResultIs(expected)) + .BDDfy(); + } + + private void GivenThe(FileRoute route) + { + _fileRoute = route; + } + + private void WhenICreate() + { + _result = _creator.Create(_fileRoute.SecurityOptions); + } + + private void ThenTheResultIs(SecurityOptions expected) + { + for (int i = 0; i < expected.IPAllowedList.Count; i++) + { + _result.IPAllowedList[i].ShouldBe(expected.IPAllowedList[i]); + } + + for (int i = 0; i < expected.IPBlockedList.Count; i++) + { + _result.IPBlockedList[i].ShouldBe(expected.IPBlockedList[i]); + } + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs index 5d0684966..98f136710 100644 --- a/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/UpstreamTemplatePatternCreatorTests.cs @@ -1,260 +1,260 @@ -using Ocelot.Configuration.Creator; -using Ocelot.Configuration.File; -using Ocelot.Values; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration -{ - public class UpstreamTemplatePatternCreatorTests - { - private FileReRoute _fileReRoute; - private readonly UpstreamTemplatePatternCreator _creator; - private UpstreamPathTemplate _result; - - public UpstreamTemplatePatternCreatorTests() - { - _creator = new UpstreamTemplatePatternCreator(); - } - - [Fact] - public void should_match_up_to_next_slash() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/api/v{apiVersion}/cards", - Priority = 0 - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^(?i)/api/v[^/]+/cards$")) - .And(x => ThenThePriorityIs(0)) - .BDDfy(); - } - - [Fact] - public void should_use_re_route_priority() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/orders/{catchAll}", - Priority = 0 - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^(?i)/orders/.+$")) - .And(x => ThenThePriorityIs(0)) - .BDDfy(); - } - - [Fact] - public void should_use_zero_priority() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/{catchAll}", - Priority = 1 - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^/.*")) - .And(x => ThenThePriorityIs(0)) - .BDDfy(); - } - - [Fact] - public void should_set_upstream_template_pattern_to_ignore_case_sensitivity() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/PRODUCTS/{productId}", - ReRouteIsCaseSensitive = false - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/.+$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_match_forward_slash_or_no_forward_slash_if_template_end_with_forward_slash() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/PRODUCTS/", - ReRouteIsCaseSensitive = false - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS(/|)$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_set_upstream_template_pattern_to_respect_case_sensitivity() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/PRODUCTS/{productId}", - ReRouteIsCaseSensitive = true - }; - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.+$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_create_template_pattern_that_matches_anything_to_end_of_string() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - ReRouteIsCaseSensitive = true - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^/api/products/.+$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_create_template_pattern_that_matches_more_than_one_placeholder() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}", - ReRouteIsCaseSensitive = true - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^/api/products/[^/]+/variants/.+$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}/", - ReRouteIsCaseSensitive = true - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^/api/products/[^/]+/variants/[^/]+(/|)$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_create_template_pattern_that_matches_to_end_of_string() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/" - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^/$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_create_template_pattern_that_matches_to_end_of_string_when_slash_and_placeholder() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/{url}" - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^/.*")) - .And(x => ThenThePriorityIs(0)) - .BDDfy(); - } - - [Fact] - public void should_create_template_pattern_that_starts_with_placeholder_then_has_another_later() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/{productId}/products/variants/{variantId}/", - ReRouteIsCaseSensitive = true - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^/[^/]+/products/variants/[^/]+(/|)$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_create_template_pattern_that_matches_query_string() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}" - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^(?i)/api/subscriptions/[^/]+/updates\\?unitId=.+$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - [Fact] - public void should_create_template_pattern_that_matches_query_string_with_multiple_params() - { - var fileReRoute = new FileReRoute - { - UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}&productId={productId}" - }; - - this.Given(x => x.GivenTheFollowingFileReRoute(fileReRoute)) - .When(x => x.WhenICreateTheTemplatePattern()) - .Then(x => x.ThenTheFollowingIsReturned("^(?i)/api/subscriptions/[^/]+/updates\\?unitId=.+&productId=.+$")) - .And(x => ThenThePriorityIs(1)) - .BDDfy(); - } - - private void GivenTheFollowingFileReRoute(FileReRoute fileReRoute) - { - _fileReRoute = fileReRoute; - } - - private void WhenICreateTheTemplatePattern() - { - _result = _creator.Create(_fileReRoute); - } - - private void ThenTheFollowingIsReturned(string expected) - { - _result.Template.ShouldBe(expected); - } - - private void ThenThePriorityIs(int v) - { - _result.Priority.ShouldBe(v); - } - } -} +using Ocelot.Configuration.Creator; +using Ocelot.Configuration.File; +using Ocelot.Values; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration +{ + public class UpstreamTemplatePatternCreatorTests + { + private FileRoute _fileRoute; + private readonly UpstreamTemplatePatternCreator _creator; + private UpstreamPathTemplate _result; + + public UpstreamTemplatePatternCreatorTests() + { + _creator = new UpstreamTemplatePatternCreator(); + } + + [Fact] + public void should_match_up_to_next_slash() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/api/v{apiVersion}/cards", + Priority = 0 + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^(?i)/api/v[^/]+/cards$")) + .And(x => ThenThePriorityIs(0)) + .BDDfy(); + } + + [Fact] + public void should_use_re_route_priority() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/orders/{catchAll}", + Priority = 0 + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^(?i)/orders/.+$")) + .And(x => ThenThePriorityIs(0)) + .BDDfy(); + } + + [Fact] + public void should_use_zero_priority() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/{catchAll}", + Priority = 1 + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/.*")) + .And(x => ThenThePriorityIs(0)) + .BDDfy(); + } + + [Fact] + public void should_set_upstream_template_pattern_to_ignore_case_sensitivity() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/PRODUCTS/{productId}", + RouteIsCaseSensitive = false + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS/.+$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_match_forward_slash_or_no_forward_slash_if_template_end_with_forward_slash() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/PRODUCTS/", + RouteIsCaseSensitive = false + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^(?i)/PRODUCTS(/|)$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_set_upstream_template_pattern_to_respect_case_sensitivity() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/PRODUCTS/{productId}", + RouteIsCaseSensitive = true + }; + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/PRODUCTS/.+$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_create_template_pattern_that_matches_anything_to_end_of_string() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/api/products/{productId}", + RouteIsCaseSensitive = true + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/api/products/.+$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_create_template_pattern_that_matches_more_than_one_placeholder() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}", + RouteIsCaseSensitive = true + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/api/products/[^/]+/variants/.+$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_create_template_pattern_that_matches_more_than_one_placeholder_with_trailing_slash() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/api/products/{productId}/variants/{variantId}/", + RouteIsCaseSensitive = true + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/api/products/[^/]+/variants/[^/]+(/|)$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_create_template_pattern_that_matches_to_end_of_string() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/" + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_create_template_pattern_that_matches_to_end_of_string_when_slash_and_placeholder() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/{url}" + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/.*")) + .And(x => ThenThePriorityIs(0)) + .BDDfy(); + } + + [Fact] + public void should_create_template_pattern_that_starts_with_placeholder_then_has_another_later() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/{productId}/products/variants/{variantId}/", + RouteIsCaseSensitive = true + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^/[^/]+/products/variants/[^/]+(/|)$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_create_template_pattern_that_matches_query_string() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}" + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^(?i)/api/subscriptions/[^/]+/updates\\?unitId=.+$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + [Fact] + public void should_create_template_pattern_that_matches_query_string_with_multiple_params() + { + var fileRoute = new FileRoute + { + UpstreamPathTemplate = "/api/subscriptions/{subscriptionId}/updates?unitId={unitId}&productId={productId}" + }; + + this.Given(x => x.GivenTheFollowingFileRoute(fileRoute)) + .When(x => x.WhenICreateTheTemplatePattern()) + .Then(x => x.ThenTheFollowingIsReturned("^(?i)/api/subscriptions/[^/]+/updates\\?unitId=.+&productId=.+$")) + .And(x => ThenThePriorityIs(1)) + .BDDfy(); + } + + private void GivenTheFollowingFileRoute(FileRoute fileRoute) + { + _fileRoute = fileRoute; + } + + private void WhenICreateTheTemplatePattern() + { + _result = _creator.Create(_fileRoute); + } + + private void ThenTheFollowingIsReturned(string expected) + { + _result.Template.ShouldBe(expected); + } + + private void ThenThePriorityIs(int v) + { + _result.Priority.ShouldBe(v); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs b/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs index 1879b90d3..619925679 100644 --- a/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/Validation/FileConfigurationFluentValidatorTests.cs @@ -1,1584 +1,1584 @@ -namespace Ocelot.UnitTests.Configuration.Validation -{ - using Microsoft.AspNetCore.Authentication; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Options; - using Moq; - using Ocelot.Configuration.File; - using Ocelot.Configuration.Validator; - using Ocelot.Requester; - using Ocelot.Responses; - using Ocelot.ServiceDiscovery; - using Ocelot.ServiceDiscovery.Providers; - using Ocelot.Values; - using Requester; - using Shouldly; - using System.Collections.Generic; - using System.Security.Claims; - using System.Text.Encodings.Web; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class FileConfigurationFluentValidatorTests - { - private IConfigurationValidator _configurationValidator; - private FileConfiguration _fileConfiguration; - private Response _result; - private readonly Mock _authProvider; - - public FileConfigurationFluentValidatorTests() - { - _authProvider = new Mock(); - var provider = new ServiceCollection() - .BuildServiceProvider(); - // Todo - replace with mocks - _configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider))); - } - - [Fact] - public void configuration_is_valid_if_service_discovery_options_specified_and_has_service_fabric_as_option() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "test" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "localhost", - Type = "ServiceFabric", - Port = 8500 - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_if_service_discovery_options_specified_and_has_service_discovery_handler() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "test" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "localhost", - Type = "FakeServiceDiscoveryProvider", - Port = 8500 - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .And(x => x.GivenAServiceDiscoveryHandler()) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_if_service_discovery_options_specified_dynamically_and_has_service_discovery_handler() - { - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "localhost", - Type = "FakeServiceDiscoveryProvider", - Port = 8500 - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .And(x => x.GivenAServiceDiscoveryHandler()) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_service_discovery_options_specified_but_no_service_discovery_handler() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "test" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "localhost", - Type = "FakeServiceDiscoveryProvider", - Port = 8500 - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorIs()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "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()?")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_service_discovery_options_specified_dynamically_but_service_discovery_handler() - { - var configuration = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "localhost", - Type = "FakeServiceDiscoveryProvider", - Port = 8500 - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorIs()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "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()?")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_service_discovery_options_specified_but_no_service_discovery_handler_with_matching_name() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - ServiceName = "test" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "localhost", - Type = "consul", - Port = 8500 - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .And(x => x.GivenAServiceDiscoveryHandler()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorIs()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "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()?")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_if_qos_options_specified_and_has_qos_handler() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura", - QoSOptions = new FileQoSOptions - { - TimeoutValue = 1, - ExceptionsAllowedBeforeBreaking = 1 - } - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .And(x => x.GivenAQoSHandler()) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_if_qos_options_specified_globally_and_has_qos_handler() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura", - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - QoSOptions = new FileQoSOptions - { - TimeoutValue = 1, - ExceptionsAllowedBeforeBreaking = 1 - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .And(x => x.GivenAQoSHandler()) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_qos_options_specified_but_no_qos_handler() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura", - QoSOptions = new FileQoSOptions - { - TimeoutValue = 1, - ExceptionsAllowedBeforeBreaking = 1 - } - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorIs()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_qos_options_specified_globally_but_no_qos_handler() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura", - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - QoSOptions = new FileQoSOptions - { - TimeoutValue = 1, - ExceptionsAllowedBeforeBreaking = 1 - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorIs()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_if_aggregates_are_valid() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51880, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_aggregates_are_duplicate_of_re_routes() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51880, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom", - UpstreamHost = "localhost" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/tom", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - }, - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "reRoute /tom has duplicate aggregate")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_if_aggregates_are_not_duplicate_of_re_routes() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51880, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Post" }, - Key = "Tom", - UpstreamHost = "localhost" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/tom", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - }, - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_aggregates_are_duplicate_of_aggregates() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51880, - } - }, - UpstreamPathTemplate = "/lol", - UpstreamHttpMethod = new List { "Get" }, - Key = "Tom" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/tom", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - }, - new FileAggregateReRoute - { - UpstreamPathTemplate = "/tom", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "aggregate /tom has duplicate aggregate")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_re_routes_dont_exist_for_aggregate() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "ReRoutes for aggregateReRoute / either do not exist or do not have correct ServiceName property")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_aggregate_has_re_routes_with_specific_request_id_keys() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51878, - } - }, - UpstreamPathTemplate = "/laura", - UpstreamHttpMethod = new List { "Get" }, - Key = "Laura" - }, - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamScheme = "http", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 51880, - } - }, - UpstreamPathTemplate = "/tom", - UpstreamHttpMethod = new List { "Get" }, - RequestIdKey = "should_fail", - Key = "Tom" - } - }, - Aggregates = new List - { - new FileAggregateReRoute - { - UpstreamPathTemplate = "/", - UpstreamHost = "localhost", - ReRouteKeys = new List - { - "Tom", - "Laura" - } - } - } - }; - - this.Given(x => x.GivenAConfiguration(configuration)) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "aggregateReRoute / contains ReRoute with specific RequestIdKey, this is not possible with Aggregates")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_scheme_in_downstream_or_upstream_template() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "http://www.bbc.co.uk/api/products/{productId}", - UpstreamPathTemplate = "http://asdf.com" - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .Then(x => x.ThenTheErrorIs()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} doesnt start with forward slash")) - .And(x => x.ThenTheErrorMessageAtPositionIs(1, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) - .And(x => x.ThenTheErrorMessageAtPositionIs(2, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains scheme")) - - .And(x => x.ThenTheErrorMessageAtPositionIs(3, "Upstream Path Template http://asdf.com contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) - .And(x => x.ThenTheErrorMessageAtPositionIs(4, "Upstream Path Template http://asdf.com doesnt start with forward slash")) - .And(x => x.ThenTheErrorMessageAtPositionIs(5, "Upstream Path Template http://asdf.com contains scheme")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_with_one_reroute() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk" - } - }, - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_without_slash_prefix_downstream_path_template() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "api/products/", - UpstreamPathTemplate = "/asdf/" - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Downstream Path Template api/products/ doesnt start with forward slash")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_without_slash_prefix_upstream_path_template() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "api/prod/", - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Upstream Path Template api/prod/ doesnt start with forward slash")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_upstream_url_contains_forward_slash_then_another_forward_slash() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "//api/prod/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - Port = 80 - } - }, - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Upstream Path Template //api/prod/ contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_if_downstream_url_contains_forward_slash_then_another_forward_slash() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "//api/products/", - UpstreamPathTemplate = "/api/prod/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - Port = 80 - } - }, - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Downstream Path Template //api/products/ contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_with_valid_authentication_provider() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - AuthenticationOptions = new FileAuthenticationOptions() - { - AuthenticationProviderKey = "Test" - } - } - } - })) - .And(x => x.GivenTheAuthSchemeExists("Test")) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_with_invalid_authentication_provider() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - AuthenticationOptions = new FileAuthenticationOptions() - { - AuthenticationProviderKey = "Test" - } - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Authentication Options AuthenticationProviderKey:Test,AllowedScopes:[] is unsupported authentication provider")) - .BDDfy(); - } - - [Fact] - public void configuration_is_not_valid_with_duplicate_reroutes_all_verbs() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bb.co.uk" - } - }, - }, - new FileReRoute - { - DownstreamPathTemplate = "/www/test/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bb.co.uk" - } - }, - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "reRoute /asdf/ has duplicate")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_with_duplicate_reroutes_all_verbs_but_different_hosts() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bb.co.uk" - } - }, - UpstreamHost = "host1" - }, - new FileReRoute - { - DownstreamPathTemplate = "/www/test/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bb.co.uk" - } - }, - UpstreamHost = "host2" - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_not_valid_with_duplicate_reroutes_specific_verbs() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - UpstreamHttpMethod = new List {"Get"} - }, - new FileReRoute - { - DownstreamPathTemplate = "/www/test/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - UpstreamHttpMethod = new List {"Get"} - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "reRoute /asdf/ has duplicate")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_with_duplicate_reroutes_different_verbs() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - }, - new FileReRoute - { - DownstreamPathTemplate = "/www/test/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Post"}, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_not_valid_with_duplicate_reroutes_with_duplicated_upstreamhosts() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - UpstreamHttpMethod = new List(), - UpstreamHost = "upstreamhost" - }, - new FileReRoute - { - DownstreamPathTemplate = "/www/test/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - UpstreamHttpMethod = new List(), - UpstreamHost = "upstreamhost" - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "reRoute /asdf/ has duplicate")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_with_duplicate_reroutes_but_different_upstreamhosts() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - UpstreamHttpMethod = new List(), - UpstreamHost = "upstreamhost111" - }, - new FileReRoute - { - DownstreamPathTemplate = "/www/test/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - UpstreamHttpMethod = new List(), - UpstreamHost = "upstreamhost222" - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_with_duplicate_reroutes_but_one_upstreamhost_is_not_set() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - UpstreamHttpMethod = new List(), - UpstreamHost = "upstreamhost" - }, - new FileReRoute - { - DownstreamPathTemplate = "/www/test/", - UpstreamPathTemplate = "/asdf/", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - UpstreamHttpMethod = new List() - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_invalid_with_invalid_rate_limit_configuration() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - RateLimitOptions = new FileRateLimitRule - { - Period = "1x", - EnableRateLimiting = true - } - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_with_valid_rate_limit_configuration() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk", - } - }, - RateLimitOptions = new FileRateLimitRule - { - Period = "1d", - EnableRateLimiting = true - } - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_with_using_service_discovery_and_service_name() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - ServiceName = "Test" - } - }, - GlobalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Type = "servicefabric", - Host = "localhost", - Port = 1234 - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - public void configuration_is_invalid_when_not_using_service_discovery_and_host(string downstreamHost) - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = downstreamHost, - } - }, - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!")) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_when_not_using_service_discovery_and_host_is_set() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "bbc.co.uk" - } - }, - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_valid_when_no_downstream_but_has_host_and_port() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "test" - } - } - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void configuration_is_not_valid_when_no_host_and_port() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - DownstreamHostAndPorts = new List - { - } - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!")) - .BDDfy(); - } - - [Fact] - public void configuration_is_not_valid_when_host_and_port_is_empty() - { - this.Given(x => x.GivenAConfiguration(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/api/products/", - UpstreamPathTemplate = "/asdf/", - UpstreamHttpMethod = new List {"Get"}, - DownstreamHostAndPorts = new List - { - new FileHostAndPort() - } - } - } - })) - .When(x => x.WhenIValidateTheConfiguration()) - .Then(x => x.ThenTheResultIsNotValid()) - .And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!")) - .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; - } - - private void WhenIValidateTheConfiguration() - { - _result = _configurationValidator.IsValid(_fileConfiguration).Result; - } - - private void ThenTheResultIsValid() - { - _result.Data.IsError.ShouldBeFalse(); - } - - private void ThenTheResultIsNotValid() - { - _result.Data.IsError.ShouldBeTrue(); - } - - private void ThenTheErrorIs() - { - _result.Data.Errors[0].ShouldBeOfType(); - } - - private void ThenTheErrorMessageAtPositionIs(int index, string expected) - { - _result.Data.Errors[index].Message.ShouldBe(expected); - } - - private void GivenTheAuthSchemeExists(string name) - { - _authProvider.Setup(x => x.GetAllSchemesAsync()).ReturnsAsync(new List - { - new AuthenticationScheme(name, name, typeof(TestHandler)) - }); - } - - private void GivenAQoSHandler() - { - var collection = new ServiceCollection(); - QosDelegatingHandlerDelegate del = (a, b) => new FakeDelegatingHandler(); - collection.AddSingleton(del); - var provider = collection.BuildServiceProvider(); - _configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider))); - } - - private void GivenAServiceDiscoveryHandler() - { - var collection = new ServiceCollection(); - ServiceDiscoveryFinderDelegate del = (a, b, c) => new FakeServiceDiscoveryProvider(); - collection.AddSingleton(del); - var provider = collection.BuildServiceProvider(); - _configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider))); - } - - private class FakeServiceDiscoveryProvider : IServiceDiscoveryProvider - { - public Task> Get() - { - throw new System.NotImplementedException(); - } - } - - private class TestOptions : AuthenticationSchemeOptions - { - } - - private class TestHandler : AuthenticationHandler - { - public TestHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) - { - } - - protected override Task HandleAuthenticateAsync() - { - var principal = new ClaimsPrincipal(); - return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); - } - } - } -} +namespace Ocelot.UnitTests.Configuration.Validation +{ + using Microsoft.AspNetCore.Authentication; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; + using Moq; + using Ocelot.Configuration.File; + using Ocelot.Configuration.Validator; + using Ocelot.Requester; + using Ocelot.Responses; + using Ocelot.ServiceDiscovery; + using Ocelot.ServiceDiscovery.Providers; + using Ocelot.Values; + using Requester; + using Shouldly; + using System.Collections.Generic; + using System.Security.Claims; + using System.Text.Encodings.Web; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class FileConfigurationFluentValidatorTests + { + private IConfigurationValidator _configurationValidator; + private FileConfiguration _fileConfiguration; + private Response _result; + private readonly Mock _authProvider; + + public FileConfigurationFluentValidatorTests() + { + _authProvider = new Mock(); + var provider = new ServiceCollection() + .BuildServiceProvider(); + // Todo - replace with mocks + _configurationValidator = new FileConfigurationFluentValidator(provider, new RouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider))); + } + + [Fact] + public void configuration_is_valid_if_service_discovery_options_specified_and_has_service_fabric_as_option() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "test" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "localhost", + Type = "ServiceFabric", + Port = 8500 + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_if_service_discovery_options_specified_and_has_service_discovery_handler() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "test" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "localhost", + Type = "FakeServiceDiscoveryProvider", + Port = 8500 + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .And(x => x.GivenAServiceDiscoveryHandler()) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_if_service_discovery_options_specified_dynamically_and_has_service_discovery_handler() + { + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "localhost", + Type = "FakeServiceDiscoveryProvider", + Port = 8500 + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .And(x => x.GivenAServiceDiscoveryHandler()) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_service_discovery_options_specified_but_no_service_discovery_handler() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "test" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "localhost", + Type = "FakeServiceDiscoveryProvider", + Port = 8500 + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorIs()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route 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()?")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_service_discovery_options_specified_dynamically_but_service_discovery_handler() + { + var configuration = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "localhost", + Type = "FakeServiceDiscoveryProvider", + Port = 8500 + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorIs()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route 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()?")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_service_discovery_options_specified_but_no_service_discovery_handler_with_matching_name() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + ServiceName = "test" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "localhost", + Type = "consul", + Port = 8500 + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .And(x => x.GivenAServiceDiscoveryHandler()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorIs()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Unable to start Ocelot, errors are: Unable to start Ocelot because either a Route 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()?")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_if_qos_options_specified_and_has_qos_handler() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura", + QoSOptions = new FileQoSOptions + { + TimeoutValue = 1, + ExceptionsAllowedBeforeBreaking = 1 + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .And(x => x.GivenAQoSHandler()) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_if_qos_options_specified_globally_and_has_qos_handler() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura", + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + QoSOptions = new FileQoSOptions + { + TimeoutValue = 1, + ExceptionsAllowedBeforeBreaking = 1 + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .And(x => x.GivenAQoSHandler()) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_qos_options_specified_but_no_qos_handler() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura", + QoSOptions = new FileQoSOptions + { + TimeoutValue = 1, + ExceptionsAllowedBeforeBreaking = 1 + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorIs()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Unable to start Ocelot because either a Route or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_qos_options_specified_globally_but_no_qos_handler() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura", + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + QoSOptions = new FileQoSOptions + { + TimeoutValue = 1, + ExceptionsAllowedBeforeBreaking = 1 + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorIs()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Unable to start Ocelot because either a Route or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_if_aggregates_are_valid() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + RouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_aggregates_are_duplicate_of_re_routes() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom", + UpstreamHost = "localhost" + } + }, + Aggregates = new List + { + new FileAggregateRoute + { + UpstreamPathTemplate = "/tom", + UpstreamHost = "localhost", + RouteKeys = new List + { + "Tom", + "Laura" + }, + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "route /tom has duplicate aggregate")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_if_aggregates_are_not_duplicate_of_re_routes() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Post" }, + Key = "Tom", + UpstreamHost = "localhost" + } + }, + Aggregates = new List + { + new FileAggregateRoute + { + UpstreamPathTemplate = "/tom", + UpstreamHost = "localhost", + RouteKeys = new List + { + "Tom", + "Laura" + }, + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_aggregates_are_duplicate_of_aggregates() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/lol", + UpstreamHttpMethod = new List { "Get" }, + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateRoute + { + UpstreamPathTemplate = "/tom", + UpstreamHost = "localhost", + RouteKeys = new List + { + "Tom", + "Laura" + } + }, + new FileAggregateRoute + { + UpstreamPathTemplate = "/tom", + UpstreamHost = "localhost", + RouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "aggregate /tom has duplicate aggregate")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_re_routes_dont_exist_for_aggregate() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + } + }, + Aggregates = new List + { + new FileAggregateRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + RouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Routes for aggregateRoute / either do not exist or do not have correct ServiceName property")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_aggregate_has_re_routes_with_specific_request_id_keys() + { + var configuration = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51878, + } + }, + UpstreamPathTemplate = "/laura", + UpstreamHttpMethod = new List { "Get" }, + Key = "Laura" + }, + new FileRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51880, + } + }, + UpstreamPathTemplate = "/tom", + UpstreamHttpMethod = new List { "Get" }, + RequestIdKey = "should_fail", + Key = "Tom" + } + }, + Aggregates = new List + { + new FileAggregateRoute + { + UpstreamPathTemplate = "/", + UpstreamHost = "localhost", + RouteKeys = new List + { + "Tom", + "Laura" + } + } + } + }; + + this.Given(x => x.GivenAConfiguration(configuration)) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "aggregateRoute / contains Route with specific RequestIdKey, this is not possible with Aggregates")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_scheme_in_downstream_or_upstream_template() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "http://www.bbc.co.uk/api/products/{productId}", + UpstreamPathTemplate = "http://asdf.com" + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .Then(x => x.ThenTheErrorIs()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} doesnt start with forward slash")) + .And(x => x.ThenTheErrorMessageAtPositionIs(1, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) + .And(x => x.ThenTheErrorMessageAtPositionIs(2, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains scheme")) + + .And(x => x.ThenTheErrorMessageAtPositionIs(3, "Upstream Path Template http://asdf.com contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) + .And(x => x.ThenTheErrorMessageAtPositionIs(4, "Upstream Path Template http://asdf.com doesnt start with forward slash")) + .And(x => x.ThenTheErrorMessageAtPositionIs(5, "Upstream Path Template http://asdf.com contains scheme")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_with_one_route() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk" + } + }, + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_without_slash_prefix_downstream_path_template() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "api/products/", + UpstreamPathTemplate = "/asdf/" + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Downstream Path Template api/products/ doesnt start with forward slash")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_without_slash_prefix_upstream_path_template() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "api/prod/", + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Upstream Path Template api/prod/ doesnt start with forward slash")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_upstream_url_contains_forward_slash_then_another_forward_slash() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "//api/prod/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + Port = 80 + } + }, + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Upstream Path Template //api/prod/ contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_if_downstream_url_contains_forward_slash_then_another_forward_slash() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "//api/products/", + UpstreamPathTemplate = "/api/prod/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + Port = 80 + } + }, + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Downstream Path Template //api/products/ contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_with_valid_authentication_provider() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + AuthenticationOptions = new FileAuthenticationOptions() + { + AuthenticationProviderKey = "Test" + } + } + } + })) + .And(x => x.GivenTheAuthSchemeExists("Test")) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_with_invalid_authentication_provider() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + AuthenticationOptions = new FileAuthenticationOptions() + { + AuthenticationProviderKey = "Test" + } + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Authentication Options AuthenticationProviderKey:Test,AllowedScopes:[] is unsupported authentication provider")) + .BDDfy(); + } + + [Fact] + public void configuration_is_not_valid_with_duplicate_routes_all_verbs() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bb.co.uk" + } + }, + }, + new FileRoute + { + DownstreamPathTemplate = "/www/test/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bb.co.uk" + } + }, + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "route /asdf/ has duplicate")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_with_duplicate_routes_all_verbs_but_different_hosts() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bb.co.uk" + } + }, + UpstreamHost = "host1" + }, + new FileRoute + { + DownstreamPathTemplate = "/www/test/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bb.co.uk" + } + }, + UpstreamHost = "host2" + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_not_valid_with_duplicate_routes_specific_verbs() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + UpstreamHttpMethod = new List {"Get"} + }, + new FileRoute + { + DownstreamPathTemplate = "/www/test/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + UpstreamHttpMethod = new List {"Get"} + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "route /asdf/ has duplicate")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_with_duplicate_routes_different_verbs() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + }, + new FileRoute + { + DownstreamPathTemplate = "/www/test/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Post"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_not_valid_with_duplicate_routes_with_duplicated_upstreamhosts() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + UpstreamHttpMethod = new List(), + UpstreamHost = "upstreamhost" + }, + new FileRoute + { + DownstreamPathTemplate = "/www/test/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + UpstreamHttpMethod = new List(), + UpstreamHost = "upstreamhost" + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "route /asdf/ has duplicate")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_with_duplicate_routes_but_different_upstreamhosts() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + UpstreamHttpMethod = new List(), + UpstreamHost = "upstreamhost111" + }, + new FileRoute + { + DownstreamPathTemplate = "/www/test/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + UpstreamHttpMethod = new List(), + UpstreamHost = "upstreamhost222" + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_with_duplicate_routes_but_one_upstreamhost_is_not_set() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + UpstreamHttpMethod = new List(), + UpstreamHost = "upstreamhost" + }, + new FileRoute + { + DownstreamPathTemplate = "/www/test/", + UpstreamPathTemplate = "/asdf/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + UpstreamHttpMethod = new List() + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_with_invalid_rate_limit_configuration() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + RateLimitOptions = new FileRateLimitRule + { + Period = "1x", + EnableRateLimiting = true + } + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_with_valid_rate_limit_configuration() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk", + } + }, + RateLimitOptions = new FileRateLimitRule + { + Period = "1d", + EnableRateLimiting = true + } + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_with_using_service_discovery_and_service_name() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + ServiceName = "Test" + } + }, + GlobalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Type = "servicefabric", + Host = "localhost", + Port = 1234 + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void configuration_is_invalid_when_not_using_service_discovery_and_host(string downstreamHost) + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = downstreamHost, + } + }, + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using Route.Host or Ocelot cannot find your service!")) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_when_not_using_service_discovery_and_host_is_set() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "bbc.co.uk" + } + }, + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_valid_when_no_downstream_but_has_host_and_port() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "test" + } + } + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void configuration_is_not_valid_when_no_host_and_port() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + DownstreamHostAndPorts = new List + { + } + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!")) + .BDDfy(); + } + + [Fact] + public void configuration_is_not_valid_when_host_and_port_is_empty() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamPathTemplate = "/api/products/", + UpstreamPathTemplate = "/asdf/", + UpstreamHttpMethod = new List {"Get"}, + DownstreamHostAndPorts = new List + { + new FileHostAndPort() + } + } + } + })) + .When(x => x.WhenIValidateTheConfiguration()) + .Then(x => x.ThenTheResultIsNotValid()) + .And(x => x.ThenTheErrorMessageAtPositionIs(0, "When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using Route.Host or Ocelot cannot find your service!")) + .BDDfy(); + } + + [Fact] + public void configuration_is_invalid_when_placeholder_is_used_twice_in_upstream_path_template() + { + this.Given(x => x.GivenAConfiguration(new FileConfiguration + { + Routes = new List + { + new FileRoute + { + 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, "route /foo/bar/{everything}/{everything} has duplicated placeholder")) + .BDDfy(); + } + + private void GivenAConfiguration(FileConfiguration fileConfiguration) + { + _fileConfiguration = fileConfiguration; + } + + private void WhenIValidateTheConfiguration() + { + _result = _configurationValidator.IsValid(_fileConfiguration).Result; + } + + private void ThenTheResultIsValid() + { + _result.Data.IsError.ShouldBeFalse(); + } + + private void ThenTheResultIsNotValid() + { + _result.Data.IsError.ShouldBeTrue(); + } + + private void ThenTheErrorIs() + { + _result.Data.Errors[0].ShouldBeOfType(); + } + + private void ThenTheErrorMessageAtPositionIs(int index, string expected) + { + _result.Data.Errors[index].Message.ShouldBe(expected); + } + + private void GivenTheAuthSchemeExists(string name) + { + _authProvider.Setup(x => x.GetAllSchemesAsync()).ReturnsAsync(new List + { + new AuthenticationScheme(name, name, typeof(TestHandler)) + }); + } + + private void GivenAQoSHandler() + { + var collection = new ServiceCollection(); + QosDelegatingHandlerDelegate del = (a, b) => new FakeDelegatingHandler(); + collection.AddSingleton(del); + var provider = collection.BuildServiceProvider(); + _configurationValidator = new FileConfigurationFluentValidator(provider, new RouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider))); + } + + private void GivenAServiceDiscoveryHandler() + { + var collection = new ServiceCollection(); + ServiceDiscoveryFinderDelegate del = (a, b, c) => new FakeServiceDiscoveryProvider(); + collection.AddSingleton(del); + var provider = collection.BuildServiceProvider(); + _configurationValidator = new FileConfigurationFluentValidator(provider, new RouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider))); + } + + private class FakeServiceDiscoveryProvider : IServiceDiscoveryProvider + { + public Task> Get() + { + throw new System.NotImplementedException(); + } + } + + private class TestOptions : AuthenticationSchemeOptions + { + } + + private class TestHandler : AuthenticationHandler + { + public TestHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { + } + + protected override Task HandleAuthenticateAsync() + { + var principal = new ClaimsPrincipal(); + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); + } + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/Validation/FileQoSOptionsFluentValidatorTests.cs b/test/Ocelot.UnitTests/Configuration/Validation/FileQoSOptionsFluentValidatorTests.cs index 64178767f..0b414f3aa 100644 --- a/test/Ocelot.UnitTests/Configuration/Validation/FileQoSOptionsFluentValidatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/Validation/FileQoSOptionsFluentValidatorTests.cs @@ -1,103 +1,103 @@ -using FluentValidation.Results; -using Microsoft.Extensions.DependencyInjection; -using Ocelot.Configuration.File; -using Ocelot.Configuration.Validator; -using Ocelot.Requester; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration.Validation -{ - public class FileQoSOptionsFluentValidatorTests - { - private FileQoSOptionsFluentValidator _validator; - private ServiceCollection _services; - private ValidationResult _result; - private FileQoSOptions _qosOptions; - - public FileQoSOptionsFluentValidatorTests() - { - _services = new ServiceCollection(); - var provider = _services.BuildServiceProvider(); - _validator = new FileQoSOptionsFluentValidator(provider); - } - - [Fact] - public void should_be_valid_as_nothing_set() - { - this.Given(_ => GivenThe(new FileQoSOptions())) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void should_be_valid_as_qos_delegate_set() - { - var qosOptions = new FileQoSOptions - { - TimeoutValue = 1, - ExceptionsAllowedBeforeBreaking = 1 - }; - - this.Given(_ => GivenThe(qosOptions)) - .And(_ => GivenAQosDelegate()) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void should_be_invalid_as_no_qos_delegate() - { - var qosOptions = new FileQoSOptions - { - TimeoutValue = 1, - ExceptionsAllowedBeforeBreaking = 1 - }; - - this.Given(_ => GivenThe(qosOptions)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInValid()) - .And(_ => ThenTheErrorIs()) - .BDDfy(); - } - - private void ThenTheErrorIs() - { - _result.Errors[0].ErrorMessage.ShouldBe("Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?"); - } - - private void ThenTheResultIsInValid() - { - _result.IsValid.ShouldBeFalse(); - } - - private void GivenAQosDelegate() - { - QosDelegatingHandlerDelegate fake = (a, b) => - { - return null; - }; - _services.AddSingleton(fake); - var provider = _services.BuildServiceProvider(); - _validator = new FileQoSOptionsFluentValidator(provider); - } - - private void GivenThe(FileQoSOptions qosOptions) - { - _qosOptions = qosOptions; - } - - private void WhenIValidate() - { - _result = _validator.Validate(_qosOptions); - } - - private void ThenTheResultIsValid() - { - _result.IsValid.ShouldBeTrue(); - } - } -} +using FluentValidation.Results; +using Microsoft.Extensions.DependencyInjection; +using Ocelot.Configuration.File; +using Ocelot.Configuration.Validator; +using Ocelot.Requester; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration.Validation +{ + public class FileQoSOptionsFluentValidatorTests + { + private FileQoSOptionsFluentValidator _validator; + private ServiceCollection _services; + private ValidationResult _result; + private FileQoSOptions _qosOptions; + + public FileQoSOptionsFluentValidatorTests() + { + _services = new ServiceCollection(); + var provider = _services.BuildServiceProvider(); + _validator = new FileQoSOptionsFluentValidator(provider); + } + + [Fact] + public void should_be_valid_as_nothing_set() + { + this.Given(_ => GivenThe(new FileQoSOptions())) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void should_be_valid_as_qos_delegate_set() + { + var qosOptions = new FileQoSOptions + { + TimeoutValue = 1, + ExceptionsAllowedBeforeBreaking = 1 + }; + + this.Given(_ => GivenThe(qosOptions)) + .And(_ => GivenAQosDelegate()) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void should_be_invalid_as_no_qos_delegate() + { + var qosOptions = new FileQoSOptions + { + TimeoutValue = 1, + ExceptionsAllowedBeforeBreaking = 1 + }; + + this.Given(_ => GivenThe(qosOptions)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInValid()) + .And(_ => ThenTheErrorIs()) + .BDDfy(); + } + + private void ThenTheErrorIs() + { + _result.Errors[0].ErrorMessage.ShouldBe("Unable to start Ocelot because either a Route or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?"); + } + + private void ThenTheResultIsInValid() + { + _result.IsValid.ShouldBeFalse(); + } + + private void GivenAQosDelegate() + { + QosDelegatingHandlerDelegate fake = (a, b) => + { + return null; + }; + _services.AddSingleton(fake); + var provider = _services.BuildServiceProvider(); + _validator = new FileQoSOptionsFluentValidator(provider); + } + + private void GivenThe(FileQoSOptions qosOptions) + { + _qosOptions = qosOptions; + } + + private void WhenIValidate() + { + _result = _validator.Validate(_qosOptions); + } + + private void ThenTheResultIsValid() + { + _result.IsValid.ShouldBeTrue(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/Validation/HostAndPortValidatorTests.cs b/test/Ocelot.UnitTests/Configuration/Validation/HostAndPortValidatorTests.cs index 6052c24fb..5dcadb809 100644 --- a/test/Ocelot.UnitTests/Configuration/Validation/HostAndPortValidatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/Validation/HostAndPortValidatorTests.cs @@ -1,77 +1,77 @@ -using FluentValidation.Results; -using Ocelot.Configuration.File; -using Ocelot.Configuration.Validator; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Configuration.Validation -{ - public class HostAndPortValidatorTests - { - private HostAndPortValidator _validator; - private ValidationResult _result; - private FileHostAndPort _hostAndPort; - - public HostAndPortValidatorTests() - { - _validator = new HostAndPortValidator(); - } - - [Theory] - [InlineData(null)] - [InlineData("")] - public void should_be_invalid_because_host_empty(string host) - { - var fileHostAndPort = new FileHostAndPort - { - Host = host - }; - - this.Given(_ => GivenThe(fileHostAndPort)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInValid()) - .And(_ => ThenTheErorrIs()) - .BDDfy(); - } - - [Fact] - public void should_be_valid_because_host_set() - { - var fileHostAndPort = new FileHostAndPort - { - Host = "test" - }; - - this.Given(_ => GivenThe(fileHostAndPort)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsValid()) - .BDDfy(); - } - - private void GivenThe(FileHostAndPort hostAndPort) - { - _hostAndPort = hostAndPort; - } - - private void WhenIValidate() - { - _result = _validator.Validate(_hostAndPort); - } - - private void ThenTheResultIsValid() - { - _result.IsValid.ShouldBeTrue(); - } - - private void ThenTheErorrIs() - { - _result.Errors[0].ErrorMessage.ShouldBe("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!"); - } - - private void ThenTheResultIsInValid() - { - _result.IsValid.ShouldBeFalse(); - } - } -} +using FluentValidation.Results; +using Ocelot.Configuration.File; +using Ocelot.Configuration.Validator; +using Shouldly; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Configuration.Validation +{ + public class HostAndPortValidatorTests + { + private HostAndPortValidator _validator; + private ValidationResult _result; + private FileHostAndPort _hostAndPort; + + public HostAndPortValidatorTests() + { + _validator = new HostAndPortValidator(); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void should_be_invalid_because_host_empty(string host) + { + var fileHostAndPort = new FileHostAndPort + { + Host = host + }; + + this.Given(_ => GivenThe(fileHostAndPort)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInValid()) + .And(_ => ThenTheErorrIs()) + .BDDfy(); + } + + [Fact] + public void should_be_valid_because_host_set() + { + var fileHostAndPort = new FileHostAndPort + { + Host = "test" + }; + + this.Given(_ => GivenThe(fileHostAndPort)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsValid()) + .BDDfy(); + } + + private void GivenThe(FileHostAndPort hostAndPort) + { + _hostAndPort = hostAndPort; + } + + private void WhenIValidate() + { + _result = _validator.Validate(_hostAndPort); + } + + private void ThenTheResultIsValid() + { + _result.IsValid.ShouldBeTrue(); + } + + private void ThenTheErorrIs() + { + _result.Errors[0].ErrorMessage.ShouldBe("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using Route.Host or Ocelot cannot find your service!"); + } + + private void ThenTheResultIsInValid() + { + _result.IsValid.ShouldBeFalse(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/Validation/ReRouteFluentValidatorTests.cs b/test/Ocelot.UnitTests/Configuration/Validation/RouteFluentValidatorTests.cs similarity index 84% rename from test/Ocelot.UnitTests/Configuration/Validation/ReRouteFluentValidatorTests.cs rename to test/Ocelot.UnitTests/Configuration/Validation/RouteFluentValidatorTests.cs index 82b3e646e..70ccdd426 100644 --- a/test/Ocelot.UnitTests/Configuration/Validation/ReRouteFluentValidatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/Validation/RouteFluentValidatorTests.cs @@ -1,433 +1,433 @@ -namespace Ocelot.UnitTests.Configuration.Validation -{ - using FluentValidation.Results; - using Microsoft.AspNetCore.Authentication; - using Microsoft.AspNetCore.Http; - using Moq; - using Ocelot.Configuration.File; - using Ocelot.Configuration.Validator; - using Ocelot.Requester; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class ReRouteFluentValidatorTests - { - private readonly ReRouteFluentValidator _validator; - private readonly Mock _authProvider; - private QosDelegatingHandlerDelegate _qosDelegatingHandler; - private Mock _serviceProvider; - private FileReRoute _reRoute; - private ValidationResult _result; - - public ReRouteFluentValidatorTests() - { - _authProvider = new Mock(); - _serviceProvider = new Mock(); - // Todo - replace with mocks - _validator = new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(_serviceProvider.Object)); - } - - [Fact] - public void downstream_path_template_should_not_be_empty() - { - var fileReRoute = new FileReRoute(); - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("Downstream Path Template cannot be empty")) - .BDDfy(); - } - - [Fact] - public void upstream_path_template_should_not_be_empty() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "test" - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("Upstream Path Template cannot be empty")) - .BDDfy(); - } - - [Fact] - public void downstream_path_template_should_start_with_forward_slash() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "test" - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("Downstream Path Template test doesnt start with forward slash")) - .BDDfy(); - } - - [Fact] - public void downstream_path_template_should_not_contain_double_forward_slash() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "//test" - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("Downstream Path Template //test contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) - .BDDfy(); - } - - [Theory] - [InlineData("https://test")] - [InlineData("http://test")] - [InlineData("/test/http://")] - [InlineData("/test/https://")] - public void downstream_path_template_should_not_contain_scheme(string downstreamPathTemplate) - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = downstreamPathTemplate - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains($"Downstream Path Template {downstreamPathTemplate} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) - .BDDfy(); - } - - [Fact] - public void upstream_path_template_should_start_with_forward_slash() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "test" - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("Upstream Path Template test doesnt start with forward slash")) - .BDDfy(); - } - - [Fact] - public void upstream_path_template_should_not_contain_double_forward_slash() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "//test" - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("Upstream Path Template //test contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) - .BDDfy(); - } - - [Theory] - [InlineData("https://test")] - [InlineData("http://test")] - [InlineData("/test/http://")] - [InlineData("/test/https://")] - public void upstream_path_template_should_not_contain_scheme(string upstreamPathTemplate) - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = upstreamPathTemplate - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains($"Upstream Path Template {upstreamPathTemplate} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) - .BDDfy(); - } - - [Fact] - public void should_not_be_valid_if_enable_rate_limiting_true_and_period_is_empty() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - RateLimitOptions = new FileRateLimitRule - { - EnableRateLimiting = true - } - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("RateLimitOptions.Period is empty")) - .BDDfy(); - } - - [Fact] - public void should_not_be_valid_if_enable_rate_limiting_true_and_period_has_value() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - RateLimitOptions = new FileRateLimitRule - { - EnableRateLimiting = true, - Period = "test" - } - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period")) - .BDDfy(); - } - - [Fact] - public void should_not_be_valid_if_specified_authentication_provider_isnt_registered() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = "JwtLads" - } - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains($"Authentication Options AuthenticationProviderKey:JwtLads,AllowedScopes:[] is unsupported authentication provider")) - .BDDfy(); - } - - [Fact] - public void should_not_be_valid_if_not_using_service_discovery_and_no_host_and_ports() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!")) - .BDDfy(); - } - - [Fact] - public void should_be_valid_if_using_service_discovery_and_no_host_and_ports() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - ServiceName = "Lads" - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void should_be_valid_re_route_using_host_and_port_and_paths() - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 5000 - } - } - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsValid()) - .BDDfy(); - } - - [Fact] - public void should_be_valid_if_specified_authentication_provider_is_registered() - { - const string key = "JwtLads"; - - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - AuthenticationOptions = new FileAuthenticationOptions - { - AuthenticationProviderKey = key - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 5000 - } - } - }; - - this.Given(_ => GivenThe(fileReRoute)) - .And(_ => GivenAnAuthProvider(key)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsValid()) - .BDDfy(); - } - - [Theory] - [InlineData("1.0")] - [InlineData("1.1")] - [InlineData("2.0")] - [InlineData("1,0")] - [InlineData("1,1")] - [InlineData("2,0")] - [InlineData("1")] - [InlineData("2")] - [InlineData("")] - [InlineData(null)] - public void should_be_valid_re_route_using_downstream_http_version(string version) - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 5000, - }, - }, - DownstreamHttpVersion = version, - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsValid()) - .BDDfy(); - } - - [Theory] - [InlineData("retg1.1")] - [InlineData("re2.0")] - [InlineData("1,0a")] - [InlineData("a1,1")] - [InlineData("12,0")] - [InlineData("asdf")] - public void should_be_invalid_re_route_using_downstream_http_version(string version) - { - var fileReRoute = new FileReRoute - { - DownstreamPathTemplate = "/test", - UpstreamPathTemplate = "/test", - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "localhost", - Port = 5000, - }, - }, - DownstreamHttpVersion = version, - }; - - this.Given(_ => GivenThe(fileReRoute)) - .When(_ => WhenIValidate()) - .Then(_ => ThenTheResultIsInvalid()) - .And(_ => ThenTheErrorsContains("'Downstream Http Version' is not in the correct format.")) - .BDDfy(); - } - - private void GivenAnAuthProvider(string key) - { - var schemes = new List - { - new AuthenticationScheme(key, key, typeof(FakeAutheHandler)) - }; - - _authProvider - .Setup(x => x.GetAllSchemesAsync()) - .ReturnsAsync(schemes); - } - - private void ThenTheResultIsValid() - { - _result.IsValid.ShouldBeTrue(); - } - - private void GivenThe(FileReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenIValidate() - { - _result = _validator.Validate(_reRoute); - } - - private void ThenTheResultIsInvalid() - { - _result.IsValid.ShouldBeFalse(); - } - - private void ThenTheErrorsContains(string expected) - { - _result.Errors.ShouldContain(x => x.ErrorMessage == expected); - } - - private class FakeAutheHandler : IAuthenticationHandler - { - public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) - { - throw new System.NotImplementedException(); - } - - public Task AuthenticateAsync() - { - throw new System.NotImplementedException(); - } - - public Task ChallengeAsync(AuthenticationProperties properties) - { - throw new System.NotImplementedException(); - } - - public Task ForbidAsync(AuthenticationProperties properties) - { - throw new System.NotImplementedException(); - } - } - } -} +namespace Ocelot.UnitTests.Configuration.Validation +{ + using FluentValidation.Results; + using Microsoft.AspNetCore.Authentication; + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Configuration.File; + using Ocelot.Configuration.Validator; + using Ocelot.Requester; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class RouteFluentValidatorTests + { + private readonly RouteFluentValidator _validator; + private readonly Mock _authProvider; + private QosDelegatingHandlerDelegate _qosDelegatingHandler; + private Mock _serviceProvider; + private FileRoute _route; + private ValidationResult _result; + + public RouteFluentValidatorTests() + { + _authProvider = new Mock(); + _serviceProvider = new Mock(); + // Todo - replace with mocks + _validator = new RouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(_serviceProvider.Object)); + } + + [Fact] + public void downstream_path_template_should_not_be_empty() + { + var fileRoute = new FileRoute(); + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("Downstream Path Template cannot be empty")) + .BDDfy(); + } + + [Fact] + public void upstream_path_template_should_not_be_empty() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "test" + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("Upstream Path Template cannot be empty")) + .BDDfy(); + } + + [Fact] + public void downstream_path_template_should_start_with_forward_slash() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "test" + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("Downstream Path Template test doesnt start with forward slash")) + .BDDfy(); + } + + [Fact] + public void downstream_path_template_should_not_contain_double_forward_slash() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "//test" + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("Downstream Path Template //test contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) + .BDDfy(); + } + + [Theory] + [InlineData("https://test")] + [InlineData("http://test")] + [InlineData("/test/http://")] + [InlineData("/test/https://")] + public void downstream_path_template_should_not_contain_scheme(string downstreamPathTemplate) + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = downstreamPathTemplate + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains($"Downstream Path Template {downstreamPathTemplate} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) + .BDDfy(); + } + + [Fact] + public void upstream_path_template_should_start_with_forward_slash() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "test" + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("Upstream Path Template test doesnt start with forward slash")) + .BDDfy(); + } + + [Fact] + public void upstream_path_template_should_not_contain_double_forward_slash() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "//test" + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("Upstream Path Template //test contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) + .BDDfy(); + } + + [Theory] + [InlineData("https://test")] + [InlineData("http://test")] + [InlineData("/test/http://")] + [InlineData("/test/https://")] + public void upstream_path_template_should_not_contain_scheme(string upstreamPathTemplate) + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = upstreamPathTemplate + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains($"Upstream Path Template {upstreamPathTemplate} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.")) + .BDDfy(); + } + + [Fact] + public void should_not_be_valid_if_enable_rate_limiting_true_and_period_is_empty() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + RateLimitOptions = new FileRateLimitRule + { + EnableRateLimiting = true + } + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("RateLimitOptions.Period is empty")) + .BDDfy(); + } + + [Fact] + public void should_not_be_valid_if_enable_rate_limiting_true_and_period_has_value() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + RateLimitOptions = new FileRateLimitRule + { + EnableRateLimiting = true, + Period = "test" + } + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period")) + .BDDfy(); + } + + [Fact] + public void should_not_be_valid_if_specified_authentication_provider_isnt_registered() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "JwtLads" + } + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains($"Authentication Options AuthenticationProviderKey:JwtLads,AllowedScopes:[] is unsupported authentication provider")) + .BDDfy(); + } + + [Fact] + public void should_not_be_valid_if_not_using_service_discovery_and_no_host_and_ports() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!")) + .BDDfy(); + } + + [Fact] + public void should_be_valid_if_using_service_discovery_and_no_host_and_ports() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + ServiceName = "Lads" + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void should_be_valid_re_route_using_host_and_port_and_paths() + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 5000 + } + } + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsValid()) + .BDDfy(); + } + + [Fact] + public void should_be_valid_if_specified_authentication_provider_is_registered() + { + const string key = "JwtLads"; + + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = key + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 5000 + } + } + }; + + this.Given(_ => GivenThe(fileRoute)) + .And(_ => GivenAnAuthProvider(key)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsValid()) + .BDDfy(); + } + + [Theory] + [InlineData("1.0")] + [InlineData("1.1")] + [InlineData("2.0")] + [InlineData("1,0")] + [InlineData("1,1")] + [InlineData("2,0")] + [InlineData("1")] + [InlineData("2")] + [InlineData("")] + [InlineData(null)] + public void should_be_valid_re_route_using_downstream_http_version(string version) + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 5000, + }, + }, + DownstreamHttpVersion = version, + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsValid()) + .BDDfy(); + } + + [Theory] + [InlineData("retg1.1")] + [InlineData("re2.0")] + [InlineData("1,0a")] + [InlineData("a1,1")] + [InlineData("12,0")] + [InlineData("asdf")] + public void should_be_invalid_re_route_using_downstream_http_version(string version) + { + var fileRoute = new FileRoute + { + DownstreamPathTemplate = "/test", + UpstreamPathTemplate = "/test", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 5000, + }, + }, + DownstreamHttpVersion = version, + }; + + this.Given(_ => GivenThe(fileRoute)) + .When(_ => WhenIValidate()) + .Then(_ => ThenTheResultIsInvalid()) + .And(_ => ThenTheErrorsContains("'Downstream Http Version' is not in the correct format.")) + .BDDfy(); + } + + private void GivenAnAuthProvider(string key) + { + var schemes = new List + { + new AuthenticationScheme(key, key, typeof(FakeAutheHandler)) + }; + + _authProvider + .Setup(x => x.GetAllSchemesAsync()) + .ReturnsAsync(schemes); + } + + private void ThenTheResultIsValid() + { + _result.IsValid.ShouldBeTrue(); + } + + private void GivenThe(FileRoute route) + { + _route = route; + } + + private void WhenIValidate() + { + _result = _validator.Validate(_route); + } + + private void ThenTheResultIsInvalid() + { + _result.IsValid.ShouldBeFalse(); + } + + private void ThenTheErrorsContains(string expected) + { + _result.Errors.ShouldContain(x => x.ErrorMessage == expected); + } + + private class FakeAutheHandler : IAuthenticationHandler + { + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) + { + throw new System.NotImplementedException(); + } + + public Task AuthenticateAsync() + { + throw new System.NotImplementedException(); + } + + public Task ChallengeAsync(AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + + public Task ForbidAsync(AuthenticationProperties properties) + { + throw new System.NotImplementedException(); + } + } + } +} diff --git a/test/Ocelot.UnitTests/Consul/ConsulFileConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Consul/ConsulFileConfigurationRepositoryTests.cs index cc12042f6..c9fc9ccdb 100644 --- a/test/Ocelot.UnitTests/Consul/ConsulFileConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Consul/ConsulFileConfigurationRepositoryTests.cs @@ -1,260 +1,260 @@ -namespace Ocelot.UnitTests.Consul -{ - using global::Consul; - using Microsoft.Extensions.Options; - using Moq; - using Newtonsoft.Json; - using Ocelot.Cache; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.File; - using Ocelot.Configuration.Repository; - using Ocelot.Logging; - using Provider.Consul; - using Responses; - using Shouldly; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Xunit; - - public class ConsulFileConfigurationRepositoryTests - { - private ConsulFileConfigurationRepository _repo; - private Mock> _options; - private Mock> _cache; - private Mock _factory; - private Mock _loggerFactory; - private Mock _client; - private Mock _kvEndpoint; - private FileConfiguration _fileConfiguration; - private Response _setResult; - private Response _getResult; - - public ConsulFileConfigurationRepositoryTests() - { - _cache = new Mock>(); - _loggerFactory = new Mock(); - - _options = new Mock>(); - _factory = new Mock(); - _client = new Mock(); - _kvEndpoint = new Mock(); - - _client - .Setup(x => x.KV) - .Returns(_kvEndpoint.Object); - - _factory - .Setup(x => x.Get(It.IsAny())) - .Returns(_client.Object); - - _options - .SetupGet(x => x.Value) - .Returns(() => _fileConfiguration); - } - - [Fact] - public void should_set_config() - { - var config = FakeFileConfiguration(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .And(_ => GivenWritingToConsulSucceeds()) - .When(_ => WhenISetTheConfiguration()) - .Then(_ => ThenTheConfigurationIsStoredAs(config)) - .BDDfy(); - } - - [Fact] - public void should_get_config() - { - var config = FakeFileConfiguration(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .And(_ => GivenFetchFromConsulSucceeds()) - .When(_ => WhenIGetTheConfiguration()) - .Then(_ => ThenTheConfigurationIs(config)) - .BDDfy(); - } - - [Fact] - public void should_get_null_config() - { - var config = FakeFileConfiguration(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .Given(_ => GivenFetchFromConsulReturnsNull()) - .When(_ => WhenIGetTheConfiguration()) - .Then(_ => ThenTheConfigurationIsNull()) - .BDDfy(); - } - - [Fact] - public void should_get_config_from_cache() - { - var config = FakeFileConfiguration(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .And(_ => GivenFetchFromCacheSucceeds()) - .When(_ => WhenIGetTheConfiguration()) - .Then(_ => ThenTheConfigurationIs(config)) - .BDDfy(); - } - - [Fact] - public void should_set_config_key() - { - var config = FakeFileConfiguration(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .And(_ => GivenTheConfigKeyComesFromFileConfig("Tom")) - .And(_ => GivenFetchFromConsulSucceeds()) - .When(_ => WhenIGetTheConfiguration()) - .And(_ => ThenTheConfigKeyIs("Tom")) - .BDDfy(); - } - - [Fact] - public void should_set_default_config_key() - { - var config = FakeFileConfiguration(); - - this.Given(_ => GivenIHaveAConfiguration(config)) - .And(_ => GivenFetchFromConsulSucceeds()) - .When(_ => WhenIGetTheConfiguration()) - .And(_ => ThenTheConfigKeyIs("InternalConfiguration")) - .BDDfy(); - } - - private void ThenTheConfigKeyIs(string expected) - { - _kvEndpoint - .Verify(x => x.Get(expected, It.IsAny()), Times.Once); - } - - private void GivenTheConfigKeyComesFromFileConfig(string key) - { - _fileConfiguration.GlobalConfiguration.ServiceDiscoveryProvider.ConfigurationKey = key; - _repo = new ConsulFileConfigurationRepository(_options.Object, _cache.Object, _factory.Object, _loggerFactory.Object); - } - - private void ThenTheConfigurationIsNull() - { - _getResult.Data.ShouldBeNull(); - } - - private void ThenTheConfigurationIs(FileConfiguration config) - { - var expected = JsonConvert.SerializeObject(config, Formatting.Indented); - var result = JsonConvert.SerializeObject(_getResult.Data, Formatting.Indented); - result.ShouldBe(expected); - } - - private async Task WhenIGetTheConfiguration() - { - _getResult = await _repo.Get(); - } - - private void GivenWritingToConsulSucceeds() - { - var response = new WriteResult(); - response.Response = true; - - _kvEndpoint - .Setup(x => x.Put(It.IsAny(), It.IsAny())).ReturnsAsync(response); - } - - private void GivenFetchFromCacheSucceeds() - { - _cache.Setup(x => x.Get(It.IsAny(), It.IsAny())).Returns(_fileConfiguration); - } - - private void GivenFetchFromConsulReturnsNull() - { - QueryResult result = new QueryResult(); - - _kvEndpoint - .Setup(x => x.Get(It.IsAny(), It.IsAny())) - .ReturnsAsync(result); - } - - private void GivenFetchFromConsulSucceeds() - { - var json = JsonConvert.SerializeObject(_fileConfiguration, Formatting.Indented); - - var bytes = Encoding.UTF8.GetBytes(json); - - var kvp = new KVPair("OcelotConfiguration"); - kvp.Value = bytes; - - var query = new QueryResult(); - query.Response = kvp; - - _kvEndpoint - .Setup(x => x.Get(It.IsAny(), It.IsAny())) - .ReturnsAsync(query); - } - - private void ThenTheConfigurationIsStoredAs(FileConfiguration config) - { - var json = JsonConvert.SerializeObject(config, Formatting.Indented); - - var bytes = Encoding.UTF8.GetBytes(json); - - _kvEndpoint - .Verify(x => x.Put(It.Is(k => k.Value.SequenceEqual(bytes)), It.IsAny()), Times.Once); - } - - private async Task WhenISetTheConfiguration() - { - _setResult = await _repo.Set(_fileConfiguration); - } - - private void GivenIHaveAConfiguration(FileConfiguration config) - { - _fileConfiguration = config; - - _repo = new ConsulFileConfigurationRepository(_options.Object, _cache.Object, _factory.Object, _loggerFactory.Object); - } - - private FileConfiguration FakeFileConfiguration() - { - var reRoutes = new List - { - new FileReRoute - { - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "123.12.12.12", - Port = 80, - } - }, - DownstreamScheme = "https", - DownstreamPathTemplate = "/asdfs/test/{test}" - } - }; - - var globalConfiguration = new FileGlobalConfiguration - { - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Port = 198, - Host = "blah" - } - }; - - return new FileConfiguration - { - GlobalConfiguration = globalConfiguration, - ReRoutes = reRoutes - }; - } - } -} +namespace Ocelot.UnitTests.Consul +{ + using global::Consul; + using Microsoft.Extensions.Options; + using Moq; + using Newtonsoft.Json; + using Ocelot.Cache; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.File; + using Ocelot.Configuration.Repository; + using Ocelot.Logging; + using Provider.Consul; + using Responses; + using Shouldly; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Xunit; + + public class ConsulFileConfigurationRepositoryTests + { + private ConsulFileConfigurationRepository _repo; + private Mock> _options; + private Mock> _cache; + private Mock _factory; + private Mock _loggerFactory; + private Mock _client; + private Mock _kvEndpoint; + private FileConfiguration _fileConfiguration; + private Response _setResult; + private Response _getResult; + + public ConsulFileConfigurationRepositoryTests() + { + _cache = new Mock>(); + _loggerFactory = new Mock(); + + _options = new Mock>(); + _factory = new Mock(); + _client = new Mock(); + _kvEndpoint = new Mock(); + + _client + .Setup(x => x.KV) + .Returns(_kvEndpoint.Object); + + _factory + .Setup(x => x.Get(It.IsAny())) + .Returns(_client.Object); + + _options + .SetupGet(x => x.Value) + .Returns(() => _fileConfiguration); + } + + [Fact] + public void should_set_config() + { + var config = FakeFileConfiguration(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .And(_ => GivenWritingToConsulSucceeds()) + .When(_ => WhenISetTheConfiguration()) + .Then(_ => ThenTheConfigurationIsStoredAs(config)) + .BDDfy(); + } + + [Fact] + public void should_get_config() + { + var config = FakeFileConfiguration(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .And(_ => GivenFetchFromConsulSucceeds()) + .When(_ => WhenIGetTheConfiguration()) + .Then(_ => ThenTheConfigurationIs(config)) + .BDDfy(); + } + + [Fact] + public void should_get_null_config() + { + var config = FakeFileConfiguration(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .Given(_ => GivenFetchFromConsulReturnsNull()) + .When(_ => WhenIGetTheConfiguration()) + .Then(_ => ThenTheConfigurationIsNull()) + .BDDfy(); + } + + [Fact] + public void should_get_config_from_cache() + { + var config = FakeFileConfiguration(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .And(_ => GivenFetchFromCacheSucceeds()) + .When(_ => WhenIGetTheConfiguration()) + .Then(_ => ThenTheConfigurationIs(config)) + .BDDfy(); + } + + [Fact] + public void should_set_config_key() + { + var config = FakeFileConfiguration(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .And(_ => GivenTheConfigKeyComesFromFileConfig("Tom")) + .And(_ => GivenFetchFromConsulSucceeds()) + .When(_ => WhenIGetTheConfiguration()) + .And(_ => ThenTheConfigKeyIs("Tom")) + .BDDfy(); + } + + [Fact] + public void should_set_default_config_key() + { + var config = FakeFileConfiguration(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .And(_ => GivenFetchFromConsulSucceeds()) + .When(_ => WhenIGetTheConfiguration()) + .And(_ => ThenTheConfigKeyIs("InternalConfiguration")) + .BDDfy(); + } + + private void ThenTheConfigKeyIs(string expected) + { + _kvEndpoint + .Verify(x => x.Get(expected, It.IsAny()), Times.Once); + } + + private void GivenTheConfigKeyComesFromFileConfig(string key) + { + _fileConfiguration.GlobalConfiguration.ServiceDiscoveryProvider.ConfigurationKey = key; + _repo = new ConsulFileConfigurationRepository(_options.Object, _cache.Object, _factory.Object, _loggerFactory.Object); + } + + private void ThenTheConfigurationIsNull() + { + _getResult.Data.ShouldBeNull(); + } + + private void ThenTheConfigurationIs(FileConfiguration config) + { + var expected = JsonConvert.SerializeObject(config, Formatting.Indented); + var result = JsonConvert.SerializeObject(_getResult.Data, Formatting.Indented); + result.ShouldBe(expected); + } + + private async Task WhenIGetTheConfiguration() + { + _getResult = await _repo.Get(); + } + + private void GivenWritingToConsulSucceeds() + { + var response = new WriteResult(); + response.Response = true; + + _kvEndpoint + .Setup(x => x.Put(It.IsAny(), It.IsAny())).ReturnsAsync(response); + } + + private void GivenFetchFromCacheSucceeds() + { + _cache.Setup(x => x.Get(It.IsAny(), It.IsAny())).Returns(_fileConfiguration); + } + + private void GivenFetchFromConsulReturnsNull() + { + QueryResult result = new QueryResult(); + + _kvEndpoint + .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(result); + } + + private void GivenFetchFromConsulSucceeds() + { + var json = JsonConvert.SerializeObject(_fileConfiguration, Formatting.Indented); + + var bytes = Encoding.UTF8.GetBytes(json); + + var kvp = new KVPair("OcelotConfiguration"); + kvp.Value = bytes; + + var query = new QueryResult(); + query.Response = kvp; + + _kvEndpoint + .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(query); + } + + private void ThenTheConfigurationIsStoredAs(FileConfiguration config) + { + var json = JsonConvert.SerializeObject(config, Formatting.Indented); + + var bytes = Encoding.UTF8.GetBytes(json); + + _kvEndpoint + .Verify(x => x.Put(It.Is(k => k.Value.SequenceEqual(bytes)), It.IsAny()), Times.Once); + } + + private async Task WhenISetTheConfiguration() + { + _setResult = await _repo.Set(_fileConfiguration); + } + + private void GivenIHaveAConfiguration(FileConfiguration config) + { + _fileConfiguration = config; + + _repo = new ConsulFileConfigurationRepository(_options.Object, _cache.Object, _factory.Object, _loggerFactory.Object); + } + + private FileConfiguration FakeFileConfiguration() + { + var routes = new List + { + new FileRoute + { + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "123.12.12.12", + Port = 80, + } + }, + DownstreamScheme = "https", + DownstreamPathTemplate = "/asdfs/test/{test}" + } + }; + + var globalConfiguration = new FileGlobalConfiguration + { + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Port = 198, + Host = "blah" + } + }; + + return new FileConfiguration + { + GlobalConfiguration = globalConfiguration, + Routes = routes + }; + } + } +} diff --git a/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs b/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs index 96048b40b..b5b12c304 100644 --- a/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs @@ -1,57 +1,57 @@ -using Ocelot.Configuration.Builder; - -namespace Ocelot.UnitTests.Consul -{ - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration; - using Ocelot.Logging; - using Provider.Consul; - using Shouldly; - using System; - using Xunit; - - public class ProviderFactoryTests - { - private readonly IServiceProvider _provider; - - public ProviderFactoryTests() - { - var services = new ServiceCollection(); - var loggerFactory = new Mock(); - var logger = new Mock(); - loggerFactory.Setup(x => x.CreateLogger()).Returns(logger.Object); - loggerFactory.Setup(x => x.CreateLogger()).Returns(logger.Object); - var consulFactory = new Mock(); - services.AddSingleton(consulFactory.Object); - services.AddSingleton(loggerFactory.Object); - _provider = services.BuildServiceProvider(); - } - - [Fact] - public void should_return_ConsulServiceDiscoveryProvider() - { - var reRoute = new DownstreamReRouteBuilder() - .WithServiceName("") - .Build(); - - var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("", "", "", 1, "", "", 1), reRoute); - provider.ShouldBeOfType(); - } - - [Fact] - public void should_return_PollingConsulServiceDiscoveryProvider() - { - var stopsPollerFromPolling = 10000; - - var reRoute = new DownstreamReRouteBuilder() - .WithServiceName("") - .Build(); - - var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("pollconsul", "http", "", 1, "", "", stopsPollerFromPolling), reRoute); - var pollProvider = provider as PollConsul; - pollProvider.ShouldNotBeNull(); - pollProvider.Dispose(); - } - } -} +using Ocelot.Configuration.Builder; + +namespace Ocelot.UnitTests.Consul +{ + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration; + using Ocelot.Logging; + using Provider.Consul; + using Shouldly; + using System; + using Xunit; + + public class ProviderFactoryTests + { + private readonly IServiceProvider _provider; + + public ProviderFactoryTests() + { + var services = new ServiceCollection(); + var loggerFactory = new Mock(); + var logger = new Mock(); + loggerFactory.Setup(x => x.CreateLogger()).Returns(logger.Object); + loggerFactory.Setup(x => x.CreateLogger()).Returns(logger.Object); + var consulFactory = new Mock(); + services.AddSingleton(consulFactory.Object); + services.AddSingleton(loggerFactory.Object); + _provider = services.BuildServiceProvider(); + } + + [Fact] + public void should_return_ConsulServiceDiscoveryProvider() + { + var route = new DownstreamRouteBuilder() + .WithServiceName("") + .Build(); + + var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("", "", "", 1, "", "", 1), route); + provider.ShouldBeOfType(); + } + + [Fact] + public void should_return_PollingConsulServiceDiscoveryProvider() + { + var stopsPollerFromPolling = 10000; + + var route = new DownstreamRouteBuilder() + .WithServiceName("") + .Build(); + + var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("pollconsul", "http", "", 1, "", "", stopsPollerFromPolling), route); + var pollProvider = provider as PollConsul; + pollProvider.ShouldNotBeNull(); + pollProvider.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs index fcdc41fc7..f1d54b3aa 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs @@ -1,334 +1,334 @@ -namespace Ocelot.UnitTests.DependencyInjection -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Moq; - using Newtonsoft.Json; - using Ocelot.Configuration.File; - using Ocelot.DependencyInjection; - using Shouldly; - using System.Collections.Generic; - using System.IO; - using TestStack.BDDfy; - using Xunit; - - public class ConfigurationBuilderExtensionsTests - { - private IConfigurationRoot _configuration; - private string _result; - private IConfigurationRoot _configRoot; - private FileConfiguration _globalConfig; - private FileConfiguration _reRouteA; - private FileConfiguration _reRouteB; - private FileConfiguration _aggregate; - private FileConfiguration _envSpecific; - private Mock _hostingEnvironment; - - public ConfigurationBuilderExtensionsTests() - { - _hostingEnvironment = new Mock(); - // Clean up config files before each test - var subConfigFiles = new DirectoryInfo(".").GetFiles("ocelot.*.json"); - - foreach (var config in subConfigFiles) - { - config.Delete(); - } - } - - [Fact] - public void should_add_base_url_to_config() - { - this.Given(_ => GivenTheBaseUrl("test")) - .When(_ => WhenIGet("BaseUrl")) - .Then(_ => ThenTheResultIs("test")) - .BDDfy(); - } - - [Fact] - public void should_merge_files() - { - this.Given(_ => GivenMultipleConfigurationFiles("", false)) - .And(_ => GivenTheEnvironmentIs(null)) - .When(_ => WhenIAddOcelotConfiguration()) - .Then(_ => ThenTheConfigsAreMerged()) - .BDDfy(); - } - - [Fact] - public void should_merge_files_except_env() - { - this.Given(_ => GivenMultipleConfigurationFiles("", true)) - .And(_ => GivenTheEnvironmentIs("Env")) - .When(_ => WhenIAddOcelotConfiguration()) - .Then(_ => ThenTheConfigsAreMerged()) - .And(_ => NotContainsEnvSpecificConfig()) - .BDDfy(); - } - - [Fact] - public void should_merge_files_in_specific_folder() - { - string configFolder = "ConfigFiles"; - this.Given(_ => GivenMultipleConfigurationFiles(configFolder, false)) - .When(_ => WhenIAddOcelotConfigurationWithSpecificFolder(configFolder)) - .Then(_ => ThenTheConfigsAreMerged()) - .BDDfy(); - } - - private void GivenMultipleConfigurationFiles(string folder, bool addEnvSpecificConfig) - { - if (!string.IsNullOrEmpty(folder)) - { - Directory.CreateDirectory(folder); - } - - _globalConfig = new FileConfiguration - { - GlobalConfiguration = new FileGlobalConfiguration - { - BaseUrl = "BaseUrl", - RateLimitOptions = new FileRateLimitOptions - { - HttpStatusCode = 500, - ClientIdHeader = "ClientIdHeader", - DisableRateLimitHeaders = true, - QuotaExceededMessage = "QuotaExceededMessage", - RateLimitCounterPrefix = "RateLimitCounterPrefix" - }, - ServiceDiscoveryProvider = new FileServiceDiscoveryProvider - { - Scheme = "https", - Host = "Host", - Port = 80, - Type = "Type" - }, - RequestIdKey = "RequestIdKey" - } - }; - - _reRouteA = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamScheme = "DownstreamScheme", - DownstreamPathTemplate = "DownstreamPathTemplate", - Key = "Key", - UpstreamHost = "UpstreamHost", - UpstreamHttpMethod = new List - { - "UpstreamHttpMethod" - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "Host", - Port = 80 - } - } - } - } - }; - - _reRouteB = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamScheme = "DownstreamSchemeB", - DownstreamPathTemplate = "DownstreamPathTemplateB", - Key = "KeyB", - UpstreamHost = "UpstreamHostB", - UpstreamHttpMethod = new List - { - "UpstreamHttpMethodB" - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "HostB", - Port = 80 - } - } - }, - new FileReRoute - { - DownstreamScheme = "DownstreamSchemeBB", - DownstreamPathTemplate = "DownstreamPathTemplateBB", - Key = "KeyBB", - UpstreamHost = "UpstreamHostBB", - UpstreamHttpMethod = new List - { - "UpstreamHttpMethodBB" - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "HostBB", - Port = 80 - } - } - } - } - }; - - _aggregate = new FileConfiguration - { - Aggregates = new List - { - new FileAggregateReRoute - { - ReRouteKeys = new List - { - "KeyB", - "KeyBB" - }, - UpstreamPathTemplate = "UpstreamPathTemplate", - }, - new FileAggregateReRoute - { - ReRouteKeys = new List - { - "KeyB", - "KeyBB" - }, - UpstreamPathTemplate = "UpstreamPathTemplate", - } - } - }; - - _envSpecific = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamScheme = "DownstreamSchemeSpec", - DownstreamPathTemplate = "DownstreamPathTemplateSpec", - Key = "KeySpec", - UpstreamHost = "UpstreamHostSpec", - UpstreamHttpMethod = new List - { - "UpstreamHttpMethodSpec" - }, - DownstreamHostAndPorts = new List - { - new FileHostAndPort - { - Host = "HostSpec", - Port = 80 - } - } - } - } - }; - - string globalFilename = Path.Combine(folder, "ocelot.global.json"); - string reroutesAFilename = Path.Combine(folder, "ocelot.reRoutesA.json"); - string reroutesBFilename = Path.Combine(folder, "ocelot.reRoutesB.json"); - string aggregatesFilename = Path.Combine(folder, "ocelot.aggregates.json"); - - File.WriteAllText(globalFilename, JsonConvert.SerializeObject(_globalConfig)); - File.WriteAllText(reroutesAFilename, JsonConvert.SerializeObject(_reRouteA)); - File.WriteAllText(reroutesBFilename, JsonConvert.SerializeObject(_reRouteB)); - File.WriteAllText(aggregatesFilename, JsonConvert.SerializeObject(_aggregate)); - - if (addEnvSpecificConfig) - { - string envSpecificFilename = Path.Combine(folder, "ocelot.Env.json"); - File.WriteAllText(envSpecificFilename, JsonConvert.SerializeObject(_envSpecific)); - } - } - - private void GivenTheEnvironmentIs(string env) - { - _hostingEnvironment.SetupGet(x => x.EnvironmentName).Returns(env); - } - - private void WhenIAddOcelotConfiguration() - { - IConfigurationBuilder builder = new ConfigurationBuilder(); - - builder.AddOcelot(_hostingEnvironment.Object); - - _configRoot = builder.Build(); - } - - private void WhenIAddOcelotConfigurationWithSpecificFolder(string folder) - { - IConfigurationBuilder builder = new ConfigurationBuilder(); - builder.AddOcelot(folder, _hostingEnvironment.Object); - _configRoot = builder.Build(); - } - - private void ThenTheConfigsAreMerged() - { - var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration)); - - fc.GlobalConfiguration.BaseUrl.ShouldBe(_globalConfig.GlobalConfiguration.BaseUrl); - fc.GlobalConfiguration.RateLimitOptions.ClientIdHeader.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.ClientIdHeader); - fc.GlobalConfiguration.RateLimitOptions.DisableRateLimitHeaders.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.DisableRateLimitHeaders); - fc.GlobalConfiguration.RateLimitOptions.HttpStatusCode.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.HttpStatusCode); - fc.GlobalConfiguration.RateLimitOptions.QuotaExceededMessage.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.QuotaExceededMessage); - fc.GlobalConfiguration.RateLimitOptions.RateLimitCounterPrefix.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.RateLimitCounterPrefix); - fc.GlobalConfiguration.RequestIdKey.ShouldBe(_globalConfig.GlobalConfiguration.RequestIdKey); - fc.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); - fc.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Host); - fc.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Port); - fc.GlobalConfiguration.ServiceDiscoveryProvider.Type.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Type); - - fc.ReRoutes.Count.ShouldBe(_reRouteA.ReRoutes.Count + _reRouteB.ReRoutes.Count); - - fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteA.ReRoutes[0].DownstreamPathTemplate); - fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteB.ReRoutes[0].DownstreamPathTemplate); - fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteB.ReRoutes[1].DownstreamPathTemplate); - - fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteA.ReRoutes[0].DownstreamScheme); - fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteB.ReRoutes[0].DownstreamScheme); - fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteB.ReRoutes[1].DownstreamScheme); - - fc.ReRoutes.ShouldContain(x => x.Key == _reRouteA.ReRoutes[0].Key); - fc.ReRoutes.ShouldContain(x => x.Key == _reRouteB.ReRoutes[0].Key); - fc.ReRoutes.ShouldContain(x => x.Key == _reRouteB.ReRoutes[1].Key); - - fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteA.ReRoutes[0].UpstreamHost); - fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteB.ReRoutes[0].UpstreamHost); - fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteB.ReRoutes[1].UpstreamHost); - - fc.Aggregates.Count.ShouldBe(_aggregate.Aggregates.Count); - } - - private void NotContainsEnvSpecificConfig() - { - var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration)); - fc.ReRoutes.ShouldNotContain(x => x.DownstreamScheme == _envSpecific.ReRoutes[0].DownstreamScheme); - fc.ReRoutes.ShouldNotContain(x => x.DownstreamPathTemplate == _envSpecific.ReRoutes[0].DownstreamPathTemplate); - fc.ReRoutes.ShouldNotContain(x => x.Key == _envSpecific.ReRoutes[0].Key); - } - - private void GivenTheBaseUrl(string baseUrl) - { -#pragma warning disable CS0618 - var builder = new ConfigurationBuilder() - .AddOcelotBaseUrl(baseUrl); -#pragma warning restore CS0618 - _configuration = builder.Build(); - } - - private void WhenIGet(string key) - { - _result = _configuration.GetValue(key, ""); - } - - private void ThenTheResultIs(string expected) - { - _result.ShouldBe(expected); - } - } -} +namespace Ocelot.UnitTests.DependencyInjection +{ + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Moq; + using Newtonsoft.Json; + using Ocelot.Configuration.File; + using Ocelot.DependencyInjection; + using Shouldly; + using System.Collections.Generic; + using System.IO; + using TestStack.BDDfy; + using Xunit; + + public class ConfigurationBuilderExtensionsTests + { + private IConfigurationRoot _configuration; + private string _result; + private IConfigurationRoot _configRoot; + private FileConfiguration _globalConfig; + private FileConfiguration _routeA; + private FileConfiguration _routeB; + private FileConfiguration _aggregate; + private FileConfiguration _envSpecific; + private Mock _hostingEnvironment; + + public ConfigurationBuilderExtensionsTests() + { + _hostingEnvironment = new Mock(); + // Clean up config files before each test + var subConfigFiles = new DirectoryInfo(".").GetFiles("ocelot.*.json"); + + foreach (var config in subConfigFiles) + { + config.Delete(); + } + } + + [Fact] + public void should_add_base_url_to_config() + { + this.Given(_ => GivenTheBaseUrl("test")) + .When(_ => WhenIGet("BaseUrl")) + .Then(_ => ThenTheResultIs("test")) + .BDDfy(); + } + + [Fact] + public void should_merge_files() + { + this.Given(_ => GivenMultipleConfigurationFiles("", false)) + .And(_ => GivenTheEnvironmentIs(null)) + .When(_ => WhenIAddOcelotConfiguration()) + .Then(_ => ThenTheConfigsAreMerged()) + .BDDfy(); + } + + [Fact] + public void should_merge_files_except_env() + { + this.Given(_ => GivenMultipleConfigurationFiles("", true)) + .And(_ => GivenTheEnvironmentIs("Env")) + .When(_ => WhenIAddOcelotConfiguration()) + .Then(_ => ThenTheConfigsAreMerged()) + .And(_ => NotContainsEnvSpecificConfig()) + .BDDfy(); + } + + [Fact] + public void should_merge_files_in_specific_folder() + { + string configFolder = "ConfigFiles"; + this.Given(_ => GivenMultipleConfigurationFiles(configFolder, false)) + .When(_ => WhenIAddOcelotConfigurationWithSpecificFolder(configFolder)) + .Then(_ => ThenTheConfigsAreMerged()) + .BDDfy(); + } + + private void GivenMultipleConfigurationFiles(string folder, bool addEnvSpecificConfig) + { + if (!string.IsNullOrEmpty(folder)) + { + Directory.CreateDirectory(folder); + } + + _globalConfig = new FileConfiguration + { + GlobalConfiguration = new FileGlobalConfiguration + { + BaseUrl = "BaseUrl", + RateLimitOptions = new FileRateLimitOptions + { + HttpStatusCode = 500, + ClientIdHeader = "ClientIdHeader", + DisableRateLimitHeaders = true, + QuotaExceededMessage = "QuotaExceededMessage", + RateLimitCounterPrefix = "RateLimitCounterPrefix" + }, + ServiceDiscoveryProvider = new FileServiceDiscoveryProvider + { + Scheme = "https", + Host = "Host", + Port = 80, + Type = "Type" + }, + RequestIdKey = "RequestIdKey" + } + }; + + _routeA = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamScheme = "DownstreamScheme", + DownstreamPathTemplate = "DownstreamPathTemplate", + Key = "Key", + UpstreamHost = "UpstreamHost", + UpstreamHttpMethod = new List + { + "UpstreamHttpMethod" + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "Host", + Port = 80 + } + } + } + } + }; + + _routeB = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamScheme = "DownstreamSchemeB", + DownstreamPathTemplate = "DownstreamPathTemplateB", + Key = "KeyB", + UpstreamHost = "UpstreamHostB", + UpstreamHttpMethod = new List + { + "UpstreamHttpMethodB" + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "HostB", + Port = 80 + } + } + }, + new FileRoute + { + DownstreamScheme = "DownstreamSchemeBB", + DownstreamPathTemplate = "DownstreamPathTemplateBB", + Key = "KeyBB", + UpstreamHost = "UpstreamHostBB", + UpstreamHttpMethod = new List + { + "UpstreamHttpMethodBB" + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "HostBB", + Port = 80 + } + } + } + } + }; + + _aggregate = new FileConfiguration + { + Aggregates = new List + { + new FileAggregateRoute + { + RouteKeys = new List + { + "KeyB", + "KeyBB" + }, + UpstreamPathTemplate = "UpstreamPathTemplate", + }, + new FileAggregateRoute + { + RouteKeys = new List + { + "KeyB", + "KeyBB" + }, + UpstreamPathTemplate = "UpstreamPathTemplate", + } + } + }; + + _envSpecific = new FileConfiguration + { + Routes = new List + { + new FileRoute + { + DownstreamScheme = "DownstreamSchemeSpec", + DownstreamPathTemplate = "DownstreamPathTemplateSpec", + Key = "KeySpec", + UpstreamHost = "UpstreamHostSpec", + UpstreamHttpMethod = new List + { + "UpstreamHttpMethodSpec" + }, + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "HostSpec", + Port = 80 + } + } + } + } + }; + + string globalFilename = Path.Combine(folder, "ocelot.global.json"); + string routesAFilename = Path.Combine(folder, "ocelot.routesA.json"); + string routesBFilename = Path.Combine(folder, "ocelot.routesB.json"); + string aggregatesFilename = Path.Combine(folder, "ocelot.aggregates.json"); + + File.WriteAllText(globalFilename, JsonConvert.SerializeObject(_globalConfig)); + File.WriteAllText(routesAFilename, JsonConvert.SerializeObject(_routeA)); + File.WriteAllText(routesBFilename, JsonConvert.SerializeObject(_routeB)); + File.WriteAllText(aggregatesFilename, JsonConvert.SerializeObject(_aggregate)); + + if (addEnvSpecificConfig) + { + string envSpecificFilename = Path.Combine(folder, "ocelot.Env.json"); + File.WriteAllText(envSpecificFilename, JsonConvert.SerializeObject(_envSpecific)); + } + } + + private void GivenTheEnvironmentIs(string env) + { + _hostingEnvironment.SetupGet(x => x.EnvironmentName).Returns(env); + } + + private void WhenIAddOcelotConfiguration() + { + IConfigurationBuilder builder = new ConfigurationBuilder(); + + builder.AddOcelot(_hostingEnvironment.Object); + + _configRoot = builder.Build(); + } + + private void WhenIAddOcelotConfigurationWithSpecificFolder(string folder) + { + IConfigurationBuilder builder = new ConfigurationBuilder(); + builder.AddOcelot(folder, _hostingEnvironment.Object); + _configRoot = builder.Build(); + } + + private void ThenTheConfigsAreMerged() + { + var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration)); + + fc.GlobalConfiguration.BaseUrl.ShouldBe(_globalConfig.GlobalConfiguration.BaseUrl); + fc.GlobalConfiguration.RateLimitOptions.ClientIdHeader.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.ClientIdHeader); + fc.GlobalConfiguration.RateLimitOptions.DisableRateLimitHeaders.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.DisableRateLimitHeaders); + fc.GlobalConfiguration.RateLimitOptions.HttpStatusCode.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.HttpStatusCode); + fc.GlobalConfiguration.RateLimitOptions.QuotaExceededMessage.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.QuotaExceededMessage); + fc.GlobalConfiguration.RateLimitOptions.RateLimitCounterPrefix.ShouldBe(_globalConfig.GlobalConfiguration.RateLimitOptions.RateLimitCounterPrefix); + fc.GlobalConfiguration.RequestIdKey.ShouldBe(_globalConfig.GlobalConfiguration.RequestIdKey); + fc.GlobalConfiguration.ServiceDiscoveryProvider.Scheme.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Scheme); + fc.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Host); + fc.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Port); + fc.GlobalConfiguration.ServiceDiscoveryProvider.Type.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Type); + + fc.Routes.Count.ShouldBe(_routeA.Routes.Count + _routeB.Routes.Count); + + fc.Routes.ShouldContain(x => x.DownstreamPathTemplate == _routeA.Routes[0].DownstreamPathTemplate); + fc.Routes.ShouldContain(x => x.DownstreamPathTemplate == _routeB.Routes[0].DownstreamPathTemplate); + fc.Routes.ShouldContain(x => x.DownstreamPathTemplate == _routeB.Routes[1].DownstreamPathTemplate); + + fc.Routes.ShouldContain(x => x.DownstreamScheme == _routeA.Routes[0].DownstreamScheme); + fc.Routes.ShouldContain(x => x.DownstreamScheme == _routeB.Routes[0].DownstreamScheme); + fc.Routes.ShouldContain(x => x.DownstreamScheme == _routeB.Routes[1].DownstreamScheme); + + fc.Routes.ShouldContain(x => x.Key == _routeA.Routes[0].Key); + fc.Routes.ShouldContain(x => x.Key == _routeB.Routes[0].Key); + fc.Routes.ShouldContain(x => x.Key == _routeB.Routes[1].Key); + + fc.Routes.ShouldContain(x => x.UpstreamHost == _routeA.Routes[0].UpstreamHost); + fc.Routes.ShouldContain(x => x.UpstreamHost == _routeB.Routes[0].UpstreamHost); + fc.Routes.ShouldContain(x => x.UpstreamHost == _routeB.Routes[1].UpstreamHost); + + fc.Aggregates.Count.ShouldBe(_aggregate.Aggregates.Count); + } + + private void NotContainsEnvSpecificConfig() + { + var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration)); + fc.Routes.ShouldNotContain(x => x.DownstreamScheme == _envSpecific.Routes[0].DownstreamScheme); + fc.Routes.ShouldNotContain(x => x.DownstreamPathTemplate == _envSpecific.Routes[0].DownstreamPathTemplate); + fc.Routes.ShouldNotContain(x => x.Key == _envSpecific.Routes[0].Key); + } + + private void GivenTheBaseUrl(string baseUrl) + { +#pragma warning disable CS0618 + var builder = new ConfigurationBuilder() + .AddOcelotBaseUrl(baseUrl); +#pragma warning restore CS0618 + _configuration = builder.Build(); + } + + private void WhenIGet(string key) + { + _result = _configuration.GetValue(key, ""); + } + + private void ThenTheResultIs(string expected) + { + _result.ShouldBe(expected); + } + } +} diff --git a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs index 121634e58..a68fee7ce 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs @@ -1,403 +1,403 @@ -namespace Ocelot.UnitTests.DependencyInjection -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration.Setter; - using Ocelot.DependencyInjection; - using Ocelot.Infrastructure; - using Ocelot.Multiplexer; - using Ocelot.Requester; - using Ocelot.UnitTests.Requester; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Net.Http; - using System.Reflection; - using Microsoft.AspNetCore.Http; - using TestStack.BDDfy; - using Xunit; +namespace Ocelot.UnitTests.DependencyInjection +{ + using Microsoft.AspNetCore.Hosting; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration.Setter; + using Ocelot.DependencyInjection; + using Ocelot.Infrastructure; + using Ocelot.Multiplexer; + using Ocelot.Requester; + using Ocelot.UnitTests.Requester; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Reflection; + using Microsoft.AspNetCore.Http; + using TestStack.BDDfy; + using Xunit; using System.Threading.Tasks; using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.Responses; using Ocelot.Values; using static Ocelot.UnitTests.Multiplexing.UserDefinedResponseAggregatorTests; - public class OcelotBuilderTests - { - private readonly IServiceCollection _services; - private IServiceProvider _serviceProvider; - private readonly IConfiguration _configRoot; - private IOcelotBuilder _ocelotBuilder; - private readonly int _maxRetries; - private Exception _ex; - - public OcelotBuilderTests() - { - _configRoot = new ConfigurationRoot(new List()); - _services = new ServiceCollection(); - _services.AddSingleton(GetHostingEnvironment()); - _services.AddSingleton(_configRoot); - _maxRetries = 100; - } - - private IWebHostEnvironment GetHostingEnvironment() - { - var environment = new Mock(); - environment - .Setup(e => e.ApplicationName) - .Returns(typeof(OcelotBuilderTests).GetTypeInfo().Assembly.GetName().Name); - - return environment.Object; - } - - [Fact] - public void should_add_specific_delegating_handlers_transient() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddSpecificTransientDelegatingHandler()) - .And(x => AddSpecificTransientDelegatingHandler()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers()) - .And(x => ThenTheSpecificHandlersAreTransient()) - .BDDfy(); - } - - [Fact] - public void should_add_type_specific_delegating_handlers_transient() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddTypeSpecificTransientDelegatingHandler(typeof(FakeDelegatingHandler))) - .And(x => AddTypeSpecificTransientDelegatingHandler(typeof(FakeDelegatingHandlerTwo))) - .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers()) - .And(x => ThenTheSpecificHandlersAreTransient()) - .BDDfy(); - } - - [Fact] - public void should_add_global_delegating_handlers_transient() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddTransientGlobalDelegatingHandler()) - .And(x => AddTransientGlobalDelegatingHandler()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) - .And(x => ThenTheGlobalHandlersAreTransient()) - .BDDfy(); - } - - [Fact] - public void should_add_global_type_delegating_handlers_transient() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddTransientGlobalDelegatingHandler()) - .And(x => AddTransientGlobalDelegatingHandler()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) - .And(x => ThenTheGlobalHandlersAreTransient()) - .BDDfy(); - } - - [Fact] - public void should_set_up_services() - { - this.When(x => WhenISetUpOcelotServices()) - .Then(x => ThenAnExceptionIsntThrown()) - .BDDfy(); - } - - [Fact] - public void should_return_ocelot_builder() - { - this.When(x => WhenISetUpOcelotServices()) - .Then(x => ThenAnOcelotBuilderIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_use_logger_factory() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => WhenIValidateScopes()) - .When(x => WhenIAccessLoggerFactory()) - .Then(x => ThenAnExceptionIsntThrown()) - .BDDfy(); - } - - [Fact] - public void should_set_up_without_passing_in_config() - { - this.When(x => WhenISetUpOcelotServicesWithoutConfig()) - .Then(x => ThenAnExceptionIsntThrown()) - .BDDfy(); - } - - [Fact] - public void should_add_singleton_defined_aggregators() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddSingletonDefinedAggregator()) - .When(x => AddSingletonDefinedAggregator()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators()) - .And(x => ThenTheAggregatorsAreSingleton()) - .BDDfy(); - } - - [Fact] - public void should_add_transient_defined_aggregators() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => AddTransientDefinedAggregator()) - .When(x => AddTransientDefinedAggregator()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators()) - .And(x => ThenTheAggregatorsAreTransient()) - .BDDfy(); - } - - [Fact] - public void should_add_custom_load_balancer_creators_by_default_ctor() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => _ocelotBuilder.AddCustomLoadBalancer()) - .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) - .BDDfy(); - } - - [Fact] - public void should_add_custom_load_balancer_creators_by_factory_method() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => _ocelotBuilder.AddCustomLoadBalancer(() => new FakeCustomLoadBalancer())) - .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) - .BDDfy(); - } - - [Fact] - public void should_add_custom_load_balancer_creators_by_di_factory_method() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => _ocelotBuilder.AddCustomLoadBalancer(provider => new FakeCustomLoadBalancer())) - .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) - .BDDfy(); - } - - [Fact] - public void should_add_custom_load_balancer_creators_by_factory_method_with_arguments() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => _ocelotBuilder.AddCustomLoadBalancer((reroute, discoveryProvider) => new FakeCustomLoadBalancer())) - .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) - .BDDfy(); - } - - [Fact] - public void should_replace_iplaceholder() - { - this.Given(x => x.WhenISetUpOcelotServices()) - .When(x => AddConfigPlaceholders()) - .Then(x => ThenAnExceptionIsntThrown()) - .And(x => ThenTheIPlaceholderInstanceIsReplaced()) - .BDDfy(); - } - - [Fact] - public void should_add_custom_load_balancer_creators() - { - this.Given(x => WhenISetUpOcelotServices()) - .When(x => _ocelotBuilder.AddCustomLoadBalancer((provider, reroute, discoveryProvider) => new FakeCustomLoadBalancer())) - .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) - .BDDfy(); - } - - private void AddSingletonDefinedAggregator() - where T : class, IDefinedAggregator - { - _ocelotBuilder.AddSingletonDefinedAggregator(); - } - - private void AddTransientDefinedAggregator() - where T : class, IDefinedAggregator - { - _ocelotBuilder.AddTransientDefinedAggregator(); - } - - private void AddConfigPlaceholders() - { - _ocelotBuilder.AddConfigPlaceholders(); - } - - private void ThenTheSpecificHandlersAreTransient() - { - var handlers = _serviceProvider.GetServices().ToList(); - var first = handlers[0]; - handlers = _serviceProvider.GetServices().ToList(); - var second = handlers[0]; - first.ShouldNotBe(second); - } - - private void ThenTheGlobalHandlersAreTransient() - { - var handlers = _serviceProvider.GetServices().ToList(); - var first = handlers[0].DelegatingHandler; - handlers = _serviceProvider.GetServices().ToList(); - var second = handlers[0].DelegatingHandler; - first.ShouldNotBe(second); - } - - private void AddTransientGlobalDelegatingHandler() - where T : DelegatingHandler - { - _ocelotBuilder.AddDelegatingHandler(true); - } - - private void AddSpecificTransientDelegatingHandler() - where T : DelegatingHandler - { - _ocelotBuilder.AddDelegatingHandler(); - } - - private void AddTypeTransientGlobalDelegatingHandler(Type type) - { - _ocelotBuilder.AddDelegatingHandler(type, true); - } - - private void AddTypeSpecificTransientDelegatingHandler(Type type) - { - _ocelotBuilder.AddDelegatingHandler(type); - } - - private void ThenTheProviderIsRegisteredAndReturnsHandlers() - { - _serviceProvider = _services.BuildServiceProvider(); - var handlers = _serviceProvider.GetServices().ToList(); - handlers[0].DelegatingHandler.ShouldBeOfType(); - handlers[1].DelegatingHandler.ShouldBeOfType(); - } - - private void ThenTheProviderIsRegisteredAndReturnsSpecificHandlers() - { - _serviceProvider = _services.BuildServiceProvider(); - var handlers = _serviceProvider.GetServices().ToList(); - handlers[0].ShouldBeOfType(); - handlers[1].ShouldBeOfType(); - } - - private void ThenTheProviderIsRegisteredAndReturnsSpecificAggregators() - { - _serviceProvider = _services.BuildServiceProvider(); - var handlers = _serviceProvider.GetServices().ToList(); - handlers[0].ShouldBeOfType(); - handlers[1].ShouldBeOfType(); - } - - private void ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators() - { - _serviceProvider = _services.BuildServiceProvider(); - var creators = _serviceProvider.GetServices().ToList(); - creators.Count(c => c.GetType() == typeof(NoLoadBalancerCreator)).ShouldBe(1); - creators.Count(c => c.GetType() == typeof(RoundRobinCreator)).ShouldBe(1); - creators.Count(c => c.GetType() == typeof(CookieStickySessionsCreator)).ShouldBe(1); - creators.Count(c => c.GetType() == typeof(LeastConnectionCreator)).ShouldBe(1); - creators.Count(c => c.GetType() == typeof(DelegateInvokingLoadBalancerCreator)).ShouldBe(1); - } - - private void ThenTheAggregatorsAreTransient() - { - var aggregators = _serviceProvider.GetServices().ToList(); - var first = aggregators[0]; - aggregators = _serviceProvider.GetServices().ToList(); - var second = aggregators[0]; - first.ShouldNotBe(second); - } - - private void ThenTheAggregatorsAreSingleton() - { - var aggregators = _serviceProvider.GetServices().ToList(); - var first = aggregators[0]; - aggregators = _serviceProvider.GetServices().ToList(); - var second = aggregators[0]; - first.ShouldBe(second); - } - - private void ThenAnOcelotBuilderIsReturned() - { - _ocelotBuilder.ShouldBeOfType(); - } - - private void ThenTheIPlaceholderInstanceIsReplaced() - { - _serviceProvider = _services.BuildServiceProvider(); - var placeholders = _serviceProvider.GetService(); - placeholders.ShouldBeOfType(); - } - - private void WhenISetUpOcelotServices() - { - try - { - _ocelotBuilder = _services.AddOcelot(_configRoot); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenISetUpOcelotServicesWithoutConfig() - { - try - { - _ocelotBuilder = _services.AddOcelot(); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenIAccessLoggerFactory() - { - try - { - _serviceProvider = _services.BuildServiceProvider(); - var logger = _serviceProvider.GetService(); - logger.ShouldNotBeNull(); - } - catch (Exception e) - { - _ex = e; - } - } - - private void WhenIValidateScopes() - { - try - { - _serviceProvider = _services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true }); - } - catch (Exception e) - { - _ex = e; - } - } - - private void ThenAnExceptionIsntThrown() - { - _ex.ShouldBeNull(); - } - - private class FakeCustomLoadBalancer : ILoadBalancer - { - public Task> Lease(HttpContext httpContext) - { - // Not relevant for these tests - throw new NotImplementedException(); - } - - public void Release(ServiceHostAndPort hostAndPort) - { - // Not relevant for these tests - throw new NotImplementedException(); - } - } - } -} + public class OcelotBuilderTests + { + private readonly IServiceCollection _services; + private IServiceProvider _serviceProvider; + private readonly IConfiguration _configRoot; + private IOcelotBuilder _ocelotBuilder; + private readonly int _maxRetries; + private Exception _ex; + + public OcelotBuilderTests() + { + _configRoot = new ConfigurationRoot(new List()); + _services = new ServiceCollection(); + _services.AddSingleton(GetHostingEnvironment()); + _services.AddSingleton(_configRoot); + _maxRetries = 100; + } + + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotBuilderTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } + + [Fact] + public void should_add_specific_delegating_handlers_transient() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddSpecificTransientDelegatingHandler()) + .And(x => AddSpecificTransientDelegatingHandler()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers()) + .And(x => ThenTheSpecificHandlersAreTransient()) + .BDDfy(); + } + + [Fact] + public void should_add_type_specific_delegating_handlers_transient() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddTypeSpecificTransientDelegatingHandler(typeof(FakeDelegatingHandler))) + .And(x => AddTypeSpecificTransientDelegatingHandler(typeof(FakeDelegatingHandlerTwo))) + .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers()) + .And(x => ThenTheSpecificHandlersAreTransient()) + .BDDfy(); + } + + [Fact] + public void should_add_global_delegating_handlers_transient() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddTransientGlobalDelegatingHandler()) + .And(x => AddTransientGlobalDelegatingHandler()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) + .And(x => ThenTheGlobalHandlersAreTransient()) + .BDDfy(); + } + + [Fact] + public void should_add_global_type_delegating_handlers_transient() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddTransientGlobalDelegatingHandler()) + .And(x => AddTransientGlobalDelegatingHandler()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) + .And(x => ThenTheGlobalHandlersAreTransient()) + .BDDfy(); + } + + [Fact] + public void should_set_up_services() + { + this.When(x => WhenISetUpOcelotServices()) + .Then(x => ThenAnExceptionIsntThrown()) + .BDDfy(); + } + + [Fact] + public void should_return_ocelot_builder() + { + this.When(x => WhenISetUpOcelotServices()) + .Then(x => ThenAnOcelotBuilderIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_use_logger_factory() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => WhenIValidateScopes()) + .When(x => WhenIAccessLoggerFactory()) + .Then(x => ThenAnExceptionIsntThrown()) + .BDDfy(); + } + + [Fact] + public void should_set_up_without_passing_in_config() + { + this.When(x => WhenISetUpOcelotServicesWithoutConfig()) + .Then(x => ThenAnExceptionIsntThrown()) + .BDDfy(); + } + + [Fact] + public void should_add_singleton_defined_aggregators() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddSingletonDefinedAggregator()) + .When(x => AddSingletonDefinedAggregator()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators()) + .And(x => ThenTheAggregatorsAreSingleton()) + .BDDfy(); + } + + [Fact] + public void should_add_transient_defined_aggregators() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddTransientDefinedAggregator()) + .When(x => AddTransientDefinedAggregator()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificAggregators()) + .And(x => ThenTheAggregatorsAreTransient()) + .BDDfy(); + } + + [Fact] + public void should_add_custom_load_balancer_creators_by_default_ctor() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => _ocelotBuilder.AddCustomLoadBalancer()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) + .BDDfy(); + } + + [Fact] + public void should_add_custom_load_balancer_creators_by_factory_method() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => _ocelotBuilder.AddCustomLoadBalancer(() => new FakeCustomLoadBalancer())) + .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) + .BDDfy(); + } + + [Fact] + public void should_add_custom_load_balancer_creators_by_di_factory_method() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => _ocelotBuilder.AddCustomLoadBalancer(provider => new FakeCustomLoadBalancer())) + .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) + .BDDfy(); + } + + [Fact] + public void should_add_custom_load_balancer_creators_by_factory_method_with_arguments() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => _ocelotBuilder.AddCustomLoadBalancer((route, discoveryProvider) => new FakeCustomLoadBalancer())) + .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) + .BDDfy(); + } + + [Fact] + public void should_replace_iplaceholder() + { + this.Given(x => x.WhenISetUpOcelotServices()) + .When(x => AddConfigPlaceholders()) + .Then(x => ThenAnExceptionIsntThrown()) + .And(x => ThenTheIPlaceholderInstanceIsReplaced()) + .BDDfy(); + } + + [Fact] + public void should_add_custom_load_balancer_creators() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => _ocelotBuilder.AddCustomLoadBalancer((provider, route, discoveryProvider) => new FakeCustomLoadBalancer())) + .Then(x => ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators()) + .BDDfy(); + } + + private void AddSingletonDefinedAggregator() + where T : class, IDefinedAggregator + { + _ocelotBuilder.AddSingletonDefinedAggregator(); + } + + private void AddTransientDefinedAggregator() + where T : class, IDefinedAggregator + { + _ocelotBuilder.AddTransientDefinedAggregator(); + } + + private void AddConfigPlaceholders() + { + _ocelotBuilder.AddConfigPlaceholders(); + } + + private void ThenTheSpecificHandlersAreTransient() + { + var handlers = _serviceProvider.GetServices().ToList(); + var first = handlers[0]; + handlers = _serviceProvider.GetServices().ToList(); + var second = handlers[0]; + first.ShouldNotBe(second); + } + + private void ThenTheGlobalHandlersAreTransient() + { + var handlers = _serviceProvider.GetServices().ToList(); + var first = handlers[0].DelegatingHandler; + handlers = _serviceProvider.GetServices().ToList(); + var second = handlers[0].DelegatingHandler; + first.ShouldNotBe(second); + } + + private void AddTransientGlobalDelegatingHandler() + where T : DelegatingHandler + { + _ocelotBuilder.AddDelegatingHandler(true); + } + + private void AddSpecificTransientDelegatingHandler() + where T : DelegatingHandler + { + _ocelotBuilder.AddDelegatingHandler(); + } + + private void AddTypeTransientGlobalDelegatingHandler(Type type) + { + _ocelotBuilder.AddDelegatingHandler(type, true); + } + + private void AddTypeSpecificTransientDelegatingHandler(Type type) + { + _ocelotBuilder.AddDelegatingHandler(type); + } + + private void ThenTheProviderIsRegisteredAndReturnsHandlers() + { + _serviceProvider = _services.BuildServiceProvider(); + var handlers = _serviceProvider.GetServices().ToList(); + handlers[0].DelegatingHandler.ShouldBeOfType(); + handlers[1].DelegatingHandler.ShouldBeOfType(); + } + + private void ThenTheProviderIsRegisteredAndReturnsSpecificHandlers() + { + _serviceProvider = _services.BuildServiceProvider(); + var handlers = _serviceProvider.GetServices().ToList(); + handlers[0].ShouldBeOfType(); + handlers[1].ShouldBeOfType(); + } + + private void ThenTheProviderIsRegisteredAndReturnsSpecificAggregators() + { + _serviceProvider = _services.BuildServiceProvider(); + var handlers = _serviceProvider.GetServices().ToList(); + handlers[0].ShouldBeOfType(); + handlers[1].ShouldBeOfType(); + } + + private void ThenTheProviderIsRegisteredAndReturnsBothBuiltInAndCustomLoadBalancerCreators() + { + _serviceProvider = _services.BuildServiceProvider(); + var creators = _serviceProvider.GetServices().ToList(); + creators.Count(c => c.GetType() == typeof(NoLoadBalancerCreator)).ShouldBe(1); + creators.Count(c => c.GetType() == typeof(RoundRobinCreator)).ShouldBe(1); + creators.Count(c => c.GetType() == typeof(CookieStickySessionsCreator)).ShouldBe(1); + creators.Count(c => c.GetType() == typeof(LeastConnectionCreator)).ShouldBe(1); + creators.Count(c => c.GetType() == typeof(DelegateInvokingLoadBalancerCreator)).ShouldBe(1); + } + + private void ThenTheAggregatorsAreTransient() + { + var aggregators = _serviceProvider.GetServices().ToList(); + var first = aggregators[0]; + aggregators = _serviceProvider.GetServices().ToList(); + var second = aggregators[0]; + first.ShouldNotBe(second); + } + + private void ThenTheAggregatorsAreSingleton() + { + var aggregators = _serviceProvider.GetServices().ToList(); + var first = aggregators[0]; + aggregators = _serviceProvider.GetServices().ToList(); + var second = aggregators[0]; + first.ShouldBe(second); + } + + private void ThenAnOcelotBuilderIsReturned() + { + _ocelotBuilder.ShouldBeOfType(); + } + + private void ThenTheIPlaceholderInstanceIsReplaced() + { + _serviceProvider = _services.BuildServiceProvider(); + var placeholders = _serviceProvider.GetService(); + placeholders.ShouldBeOfType(); + } + + private void WhenISetUpOcelotServices() + { + try + { + _ocelotBuilder = _services.AddOcelot(_configRoot); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenISetUpOcelotServicesWithoutConfig() + { + try + { + _ocelotBuilder = _services.AddOcelot(); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenIAccessLoggerFactory() + { + try + { + _serviceProvider = _services.BuildServiceProvider(); + var logger = _serviceProvider.GetService(); + logger.ShouldNotBeNull(); + } + catch (Exception e) + { + _ex = e; + } + } + + private void WhenIValidateScopes() + { + try + { + _serviceProvider = _services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = true }); + } + catch (Exception e) + { + _ex = e; + } + } + + private void ThenAnExceptionIsntThrown() + { + _ex.ShouldBeNull(); + } + + private class FakeCustomLoadBalancer : ILoadBalancer + { + public Task> Lease(HttpContext httpContext) + { + // Not relevant for these tests + throw new NotImplementedException(); + } + + public void Release(ServiceHostAndPort hostAndPort) + { + // Not relevant for these tests + throw new NotImplementedException(); + } + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamPathManipulation/ClaimsToDownstreamPathMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamPathManipulation/ClaimsToDownstreamPathMiddlewareTests.cs index f82cc565b..e3c5fa5f5 100644 --- a/test/Ocelot.UnitTests/DownstreamPathManipulation/ClaimsToDownstreamPathMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamPathManipulation/ClaimsToDownstreamPathMiddlewareTests.cs @@ -1,104 +1,104 @@ -using Microsoft.AspNetCore.Http; -namespace Ocelot.UnitTests.DownstreamPathManipulation -{ - using Ocelot.DownstreamPathManipulation.Middleware; - using Ocelot.Infrastructure.RequestData; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.Logging; - using Ocelot.Middleware; - using Ocelot.PathManipulation; - using Ocelot.Request.Middleware; - using Ocelot.Responses; - using Ocelot.Values; - using System.Collections.Generic; - using System.Net.Http; - using System.Security.Claims; - using System.Threading.Tasks; - using TestStack.BDDfy; +using Microsoft.AspNetCore.Http; +namespace Ocelot.UnitTests.DownstreamPathManipulation +{ + using Ocelot.DownstreamPathManipulation.Middleware; + using Ocelot.Infrastructure.RequestData; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.UrlMatcher; + using Ocelot.Logging; + using Ocelot.Middleware; + using Ocelot.PathManipulation; + using Ocelot.Request.Middleware; + using Ocelot.Responses; + using Ocelot.Values; + using System.Collections.Generic; + using System.Net.Http; + using System.Security.Claims; + using System.Threading.Tasks; + using TestStack.BDDfy; using Xunit; using Ocelot.DownstreamRouteFinder.Middleware; - public class ClaimsToDownstreamPathMiddlewareTests - { - private readonly Mock _changePath; - private Mock _loggerFactory; - private Mock _logger; - private ClaimsToDownstreamPathMiddleware _middleware; - private RequestDelegate _next; - private HttpContext _httpContext; - - public ClaimsToDownstreamPathMiddlewareTests() - { - _httpContext = new DefaultHttpContext(); - _loggerFactory = new Mock(); - _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = context => Task.CompletedTask; + public class ClaimsToDownstreamPathMiddlewareTests + { + private readonly Mock _changePath; + private Mock _loggerFactory; + private Mock _logger; + private ClaimsToDownstreamPathMiddleware _middleware; + private RequestDelegate _next; + private HttpContext _httpContext; + + public ClaimsToDownstreamPathMiddlewareTests() + { + _httpContext = new DefaultHttpContext(); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = context => Task.CompletedTask; _changePath = new Mock(); - _httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"))); - _middleware = new ClaimsToDownstreamPathMiddleware(_next, _loggerFactory.Object, _changePath.Object); - } - - [Fact] - public void should_call_add_queries_correctly() - { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithClaimsToDownstreamPath(new List - { - new ClaimToThing("UserId", "Subject", "", 0), - }) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); - - this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheChangeDownstreamPathReturnsOk()) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenChangeDownstreamPathIsCalledCorrectly()) - .BDDfy(); - - } - - private void WhenICallTheMiddleware() - { - _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); - } - - private void GivenTheChangeDownstreamPathReturnsOk() - { - _changePath - .Setup(x => x.ChangeDownstreamPath( - It.IsAny>(), - It.IsAny>(), - It.IsAny(), - It.IsAny>())) - .Returns(new OkResponse()); - } - - private void ThenChangeDownstreamPathIsCalledCorrectly() - { - _changePath - .Verify(x => x.ChangeDownstreamPath( - It.IsAny>(), - It.IsAny>(), - _httpContext.Items.DownstreamReRoute().DownstreamPathTemplate, - _httpContext.Items.TemplatePlaceholderNameAndValues()), Times.Once); - } - - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) - { + _httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"))); + _middleware = new ClaimsToDownstreamPathMiddleware(_next, _loggerFactory.Object, _changePath.Object); + } + + [Fact] + public void should_call_add_queries_correctly() + { + var downstreamRoute = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithClaimsToDownstreamPath(new List + { + new ClaimToThing("UserId", "Subject", "", 0), + }) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) + .And(x => x.GivenTheChangeDownstreamPathReturnsOk()) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenChangeDownstreamPathIsCalledCorrectly()) + .BDDfy(); + + } + + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); + } + + private void GivenTheChangeDownstreamPathReturnsOk() + { + _changePath + .Setup(x => x.ChangeDownstreamPath( + It.IsAny>(), + It.IsAny>(), + It.IsAny(), + It.IsAny>())) + .Returns(new OkResponse()); + } + + private void ThenChangeDownstreamPathIsCalledCorrectly() + { + _changePath + .Verify(x => x.ChangeDownstreamPath( + It.IsAny>(), + It.IsAny>(), + _httpContext.Items.DownstreamRoute().DownstreamPathTemplate, + _httpContext.Items.TemplatePlaceholderNameAndValues()), Times.Once); + } + + private void GivenTheDownStreamRouteIs(Ocelot.DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) + { _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); - } - - } -} + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); + } + + } +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs index fd8190ead..cd7db20ac 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteCreatorTests.cs @@ -1,306 +1,306 @@ -namespace Ocelot.UnitTests.DownstreamRouteFinder -{ - using System; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.Creator; - using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.Finder; - using Ocelot.LoadBalancer.LoadBalancers; - using Responses; - using Shouldly; - using System.Collections.Generic; - using System.Net.Http; - using TestStack.BDDfy; - using Xunit; - - public class DownstreamRouteCreatorTests - { - private readonly DownstreamRouteCreator _creator; - private readonly QoSOptions _qoSOptions; - private readonly HttpHandlerOptions _handlerOptions; - private readonly LoadBalancerOptions _loadBalancerOptions; - private Response _result; - private string _upstreamHost; - private string _upstreamUrlPath; - private string _upstreamHttpMethod; - private IInternalConfiguration _configuration; - private Mock _qosOptionsCreator; - private Response _resultTwo; - private string _upstreamQuery; - - public DownstreamRouteCreatorTests() - { - _qosOptionsCreator = new Mock(); - _qoSOptions = new QoSOptionsBuilder().Build(); - _handlerOptions = new HttpHandlerOptionsBuilder().Build(); - _loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(NoLoadBalancer)).Build(); - _qosOptionsCreator - .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(_qoSOptions); - _creator = new DownstreamRouteCreator(_qosOptionsCreator.Object); - } - - [Fact] - public void should_create_downstream_route() - { - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheDownstreamRouteIsCreated()) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_route_with_rate_limit_options() - { - var rateLimitOptions = new RateLimitOptionsBuilder() - .WithEnableRateLimiting(true) - .WithClientIdHeader("test") - .Build(); - - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithServiceName("auth") - .WithRateLimitOptions(rateLimitOptions) - .Build(); - - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .Build(); - - var reRoutes = new List { reRoute }; - - var configuration = new InternalConfiguration(reRoutes, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheDownstreamRouteIsCreated()) - .And(_ => WithRateLimitOptions(rateLimitOptions)) - .BDDfy(); - } - - [Fact] - public void should_cache_downstream_route() - { - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) - .When(_ => WhenICreate()) - .And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) - .When(_ => WhenICreateAgain()) - .Then(_ => ThenTheDownstreamRoutesAreTheSameReference()) - .BDDfy(); - } - - [Fact] - public void should_not_cache_downstream_route() - { - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration, "/geoffistheworst/")) - .When(_ => WhenICreate()) - .And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) - .When(_ => WhenICreateAgain()) - .Then(_ => ThenTheDownstreamRoutesAreTheNotSameReference()) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_route_with_no_path() - { - var upstreamUrlPath = "/auth/"; - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheDownstreamPathIsForwardSlash()) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_route_with_only_first_segment_no_traling_slash() - { - var upstreamUrlPath = "/auth"; - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheDownstreamPathIsForwardSlash()) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_route_with_segments_no_traling_slash() - { - var upstreamUrlPath = "/auth/test"; - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) - .When(_ => WhenICreate()) - .Then(_ => ThenThePathDoesNotHaveTrailingSlash()) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_route_and_remove_query_string() - { - var upstreamUrlPath = "/auth/test?test=1&best=2"; - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheQueryStringIsRemoved()) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_route_for_sticky_sessions() - { - var loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(CookieStickySessions)).WithKey("boom").WithExpiryInMs(1).Build(); - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheStickySessionLoadBalancerIsUsed(loadBalancerOptions)) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_route_with_qos() - { - var qoSOptions = new QoSOptionsBuilder() - .WithExceptionsAllowedBeforeBreaking(1) - .WithTimeoutValue(1) - .Build(); - - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration)) - .And(_ => GivenTheQosCreatorReturns(qoSOptions)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheQosOptionsAreSet(qoSOptions)) - .BDDfy(); - } - - [Fact] - public void should_create_downstream_route_with_handler_options() - { - var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); - - this.Given(_ => GivenTheConfiguration(configuration)) - .When(_ => WhenICreate()) - .Then(_ => ThenTheHandlerOptionsAreSet()) - .BDDfy(); - } - - private void GivenTheQosCreatorReturns(QoSOptions options) - { - _qosOptionsCreator - .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())) - .Returns(options); - } - - private void WithRateLimitOptions(RateLimitOptions expected) - { - _result.Data.ReRoute.DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeTrue(); - _result.Data.ReRoute.DownstreamReRoute[0].RateLimitOptions.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); - _result.Data.ReRoute.DownstreamReRoute[0].RateLimitOptions.ClientIdHeader.ShouldBe(expected.ClientIdHeader); - } - - private void ThenTheDownstreamRouteIsCreated() - { - _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); - _result.Data.ReRoute.UpstreamHttpMethod[0].ShouldBe(HttpMethod.Get); - _result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth"); - _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); - _result.Data.ReRoute.DownstreamReRoute[0].UseServiceDiscovery.ShouldBeTrue(); - _result.Data.ReRoute.DownstreamReRoute[0].HttpHandlerOptions.ShouldNotBeNull(); - _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.ShouldNotBeNull(); - _result.Data.ReRoute.DownstreamReRoute[0].DownstreamScheme.ShouldBe("http"); - _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerOptions.Type.ShouldBe(nameof(NoLoadBalancer)); - _result.Data.ReRoute.DownstreamReRoute[0].HttpHandlerOptions.ShouldBe(_handlerOptions); - _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.ShouldBe(_qoSOptions); - _result.Data.ReRoute.UpstreamTemplatePattern.ShouldNotBeNull(); - _result.Data.ReRoute.DownstreamReRoute[0].UpstreamPathTemplate.ShouldNotBeNull(); - } - - private void ThenTheDownstreamPathIsForwardSlash() - { - _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/"); - _result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth"); - _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET"); - } - - private void ThenThePathDoesNotHaveTrailingSlash() - { - _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); - _result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth"); - _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); - } - - private void ThenTheQueryStringIsRemoved() - { - _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); - _result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth"); - _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); - } - - private void ThenTheStickySessionLoadBalancerIsUsed(LoadBalancerOptions expected) - { - _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe($"{nameof(CookieStickySessions)}:boom"); - _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerOptions.Type.ShouldBe(nameof(CookieStickySessions)); - _result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerOptions.ShouldBe(expected); - } - - private void ThenTheQosOptionsAreSet(QoSOptions expected) - { - _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.ShouldBe(expected); - _result.Data.ReRoute.DownstreamReRoute[0].QosOptions.UseQos.ShouldBeTrue(); - _qosOptionsCreator - .Verify(x => x.Create(expected, _upstreamUrlPath, It.IsAny>()), Times.Once); - } - - private void GivenTheConfiguration(IInternalConfiguration config) - { - _upstreamHost = "doesnt matter"; - _upstreamUrlPath = "/auth/test"; - _upstreamHttpMethod = "GET"; - _configuration = config; - } - - private void GivenTheConfiguration(IInternalConfiguration config, string upstreamUrlPath) - { - _upstreamHost = "doesnt matter"; - _upstreamUrlPath = upstreamUrlPath; - _upstreamHttpMethod = "GET"; - _configuration = config; - } - - private void ThenTheHandlerOptionsAreSet() - { - _result.Data.ReRoute.DownstreamReRoute[0].HttpHandlerOptions.ShouldBe(_handlerOptions); - } - - private void WhenICreate() - { - _result = _creator.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _configuration, _upstreamHost); - } - - private void WhenICreateAgain() - { - _resultTwo = _creator.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _configuration, _upstreamHost); - } - - private void ThenTheDownstreamRoutesAreTheSameReference() - { - _result.ShouldBe(_resultTwo); - } - - private void ThenTheDownstreamRoutesAreTheNotSameReference() - { - _result.ShouldNotBe(_resultTwo); - } - } -} +namespace Ocelot.UnitTests.DownstreamRouteFinder +{ + using System; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.Finder; + using Ocelot.LoadBalancer.LoadBalancers; + using Responses; + using Shouldly; + using System.Collections.Generic; + using System.Net.Http; + using TestStack.BDDfy; + using Xunit; + + public class DownstreamRouteCreatorTests + { + private readonly DownstreamRouteCreator _creator; + private readonly QoSOptions _qoSOptions; + private readonly HttpHandlerOptions _handlerOptions; + private readonly LoadBalancerOptions _loadBalancerOptions; + private Response _result; + private string _upstreamHost; + private string _upstreamUrlPath; + private string _upstreamHttpMethod; + private IInternalConfiguration _configuration; + private Mock _qosOptionsCreator; + private Response _resultTwo; + private string _upstreamQuery; + + public DownstreamRouteCreatorTests() + { + _qosOptionsCreator = new Mock(); + _qoSOptions = new QoSOptionsBuilder().Build(); + _handlerOptions = new HttpHandlerOptionsBuilder().Build(); + _loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(NoLoadBalancer)).Build(); + _qosOptionsCreator + .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(_qoSOptions); + _creator = new DownstreamRouteCreator(_qosOptionsCreator.Object); + } + + [Fact] + public void should_create_downstream_route() + { + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDownstreamRouteIsCreated()) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_rate_limit_options() + { + var rateLimitOptions = new RateLimitOptionsBuilder() + .WithEnableRateLimiting(true) + .WithClientIdHeader("test") + .Build(); + + var downstreamRoute = new DownstreamRouteBuilder() + .WithServiceName("auth") + .WithRateLimitOptions(rateLimitOptions) + .Build(); + + var route = new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .Build(); + + var routes = new List { route }; + + var configuration = new InternalConfiguration(routes, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDownstreamRouteIsCreated()) + .And(_ => WithRateLimitOptions(rateLimitOptions)) + .BDDfy(); + } + + [Fact] + public void should_cache_downstream_route() + { + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) + .When(_ => WhenICreate()) + .And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) + .When(_ => WhenICreateAgain()) + .Then(_ => ThenTheDownstreamRoutesAreTheSameReference()) + .BDDfy(); + } + + [Fact] + public void should_not_cache_downstream_route() + { + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration, "/geoffistheworst/")) + .When(_ => WhenICreate()) + .And(_ => GivenTheConfiguration(configuration, "/geoffisthebest/")) + .When(_ => WhenICreateAgain()) + .Then(_ => ThenTheDownstreamRoutesAreTheNotSameReference()) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_no_path() + { + var upstreamUrlPath = "/auth/"; + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDownstreamPathIsForwardSlash()) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_only_first_segment_no_traling_slash() + { + var upstreamUrlPath = "/auth"; + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheDownstreamPathIsForwardSlash()) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_segments_no_traling_slash() + { + var upstreamUrlPath = "/auth/test"; + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) + .When(_ => WhenICreate()) + .Then(_ => ThenThePathDoesNotHaveTrailingSlash()) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_and_remove_query_string() + { + var upstreamUrlPath = "/auth/test?test=1&best=2"; + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration, upstreamUrlPath)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheQueryStringIsRemoved()) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_for_sticky_sessions() + { + var loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(CookieStickySessions)).WithKey("boom").WithExpiryInMs(1).Build(); + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheStickySessionLoadBalancerIsUsed(loadBalancerOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_qos() + { + var qoSOptions = new QoSOptionsBuilder() + .WithExceptionsAllowedBeforeBreaking(1) + .WithTimeoutValue(1) + .Build(); + + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration)) + .And(_ => GivenTheQosCreatorReturns(qoSOptions)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheQosOptionsAreSet(qoSOptions)) + .BDDfy(); + } + + [Fact] + public void should_create_downstream_route_with_handler_options() + { + var configuration = new InternalConfiguration(null, "doesnt matter", null, "doesnt matter", _loadBalancerOptions, "http", _qoSOptions, _handlerOptions, new Version("1.1")); + + this.Given(_ => GivenTheConfiguration(configuration)) + .When(_ => WhenICreate()) + .Then(_ => ThenTheHandlerOptionsAreSet()) + .BDDfy(); + } + + private void GivenTheQosCreatorReturns(QoSOptions options) + { + _qosOptionsCreator + .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny>())) + .Returns(options); + } + + private void WithRateLimitOptions(RateLimitOptions expected) + { + _result.Data.Route.DownstreamRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeTrue(); + _result.Data.Route.DownstreamRoute[0].RateLimitOptions.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting); + _result.Data.Route.DownstreamRoute[0].RateLimitOptions.ClientIdHeader.ShouldBe(expected.ClientIdHeader); + } + + private void ThenTheDownstreamRouteIsCreated() + { + _result.Data.Route.DownstreamRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); + _result.Data.Route.UpstreamHttpMethod[0].ShouldBe(HttpMethod.Get); + _result.Data.Route.DownstreamRoute[0].ServiceName.ShouldBe("auth"); + _result.Data.Route.DownstreamRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); + _result.Data.Route.DownstreamRoute[0].UseServiceDiscovery.ShouldBeTrue(); + _result.Data.Route.DownstreamRoute[0].HttpHandlerOptions.ShouldNotBeNull(); + _result.Data.Route.DownstreamRoute[0].QosOptions.ShouldNotBeNull(); + _result.Data.Route.DownstreamRoute[0].DownstreamScheme.ShouldBe("http"); + _result.Data.Route.DownstreamRoute[0].LoadBalancerOptions.Type.ShouldBe(nameof(Ocelot.LoadBalancer.LoadBalancers.NoLoadBalancer)); + _result.Data.Route.DownstreamRoute[0].HttpHandlerOptions.ShouldBe(_handlerOptions); + _result.Data.Route.DownstreamRoute[0].QosOptions.ShouldBe(_qoSOptions); + _result.Data.Route.UpstreamTemplatePattern.ShouldNotBeNull(); + _result.Data.Route.DownstreamRoute[0].UpstreamPathTemplate.ShouldNotBeNull(); + } + + private void ThenTheDownstreamPathIsForwardSlash() + { + _result.Data.Route.DownstreamRoute[0].DownstreamPathTemplate.Value.ShouldBe("/"); + _result.Data.Route.DownstreamRoute[0].ServiceName.ShouldBe("auth"); + _result.Data.Route.DownstreamRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET"); + } + + private void ThenThePathDoesNotHaveTrailingSlash() + { + _result.Data.Route.DownstreamRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); + _result.Data.Route.DownstreamRoute[0].ServiceName.ShouldBe("auth"); + _result.Data.Route.DownstreamRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); + } + + private void ThenTheQueryStringIsRemoved() + { + _result.Data.Route.DownstreamRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test"); + _result.Data.Route.DownstreamRoute[0].ServiceName.ShouldBe("auth"); + _result.Data.Route.DownstreamRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET"); + } + + private void ThenTheStickySessionLoadBalancerIsUsed(LoadBalancerOptions expected) + { + _result.Data.Route.DownstreamRoute[0].LoadBalancerKey.ShouldBe($"{nameof(Ocelot.LoadBalancer.LoadBalancers.CookieStickySessions)}:boom"); + _result.Data.Route.DownstreamRoute[0].LoadBalancerOptions.Type.ShouldBe(nameof(Ocelot.LoadBalancer.LoadBalancers.CookieStickySessions)); + _result.Data.Route.DownstreamRoute[0].LoadBalancerOptions.ShouldBe(expected); + } + + private void ThenTheQosOptionsAreSet(QoSOptions expected) + { + _result.Data.Route.DownstreamRoute[0].QosOptions.ShouldBe(expected); + _result.Data.Route.DownstreamRoute[0].QosOptions.UseQos.ShouldBeTrue(); + _qosOptionsCreator + .Verify(x => x.Create(expected, _upstreamUrlPath, It.IsAny>()), Times.Once); + } + + private void GivenTheConfiguration(IInternalConfiguration config) + { + _upstreamHost = "doesnt matter"; + _upstreamUrlPath = "/auth/test"; + _upstreamHttpMethod = "GET"; + _configuration = config; + } + + private void GivenTheConfiguration(IInternalConfiguration config, string upstreamUrlPath) + { + _upstreamHost = "doesnt matter"; + _upstreamUrlPath = upstreamUrlPath; + _upstreamHttpMethod = "GET"; + _configuration = config; + } + + private void ThenTheHandlerOptionsAreSet() + { + _result.Data.Route.DownstreamRoute[0].HttpHandlerOptions.ShouldBe(_handlerOptions); + } + + private void WhenICreate() + { + _result = _creator.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _configuration, _upstreamHost); + } + + private void WhenICreateAgain() + { + _resultTwo = _creator.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _configuration, _upstreamHost); + } + + private void ThenTheDownstreamRoutesAreTheSameReference() + { + _result.ShouldBe(_resultTwo); + } + + private void ThenTheDownstreamRoutesAreTheNotSameReference() + { + _result.ShouldNotBe(_resultTwo); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs index d27a65a51..081e008db 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderMiddlewareTests.cs @@ -24,7 +24,7 @@ public class DownstreamRouteFinderMiddlewareTests { private readonly Mock _finder; private readonly Mock _factory; - private Response _downstreamRoute; + private Response _downstreamRoute; private IInternalConfiguration _config; private Mock _loggerFactory; private Mock _logger; @@ -50,16 +50,16 @@ public void should_call_scoped_data_repository_correctly() { var config = new InternalConfiguration(null, null, new ServiceProviderConfigurationBuilder().Build(), "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); - var downstreamReRoute = new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithUpstreamHttpMethod(new List { "Get" }) .Build(); this.Given(x => x.GivenTheDownStreamRouteFinderReturns( - new DownstreamRoute( + new DownstreamRouteHolder( new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) .WithUpstreamHttpMethod(new List { "Get" }) .Build()))) .And(x => GivenTheFollowingConfig(config)) @@ -79,9 +79,9 @@ private void GivenTheFollowingConfig(IInternalConfiguration config) _httpContext.Items.SetIInternalConfiguration(config); } - private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute) + private void GivenTheDownStreamRouteFinderReturns(Ocelot.DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) { - _downstreamRoute = new OkResponse(downstreamRoute); + _downstreamRoute = new OkResponse(downstreamRoute); _finder .Setup(x => x.Get(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(_downstreamRoute); diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs index 5f9e1a503..13f7ff1cf 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteFinderTests.cs @@ -1,771 +1,771 @@ -using Moq; -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; -using Ocelot.DownstreamRouteFinder; -using Ocelot.DownstreamRouteFinder.Finder; -using Ocelot.DownstreamRouteFinder.UrlMatcher; -using Ocelot.Responses; -using Ocelot.Values; -using Shouldly; -using System.Collections.Generic; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.DownstreamRouteFinder -{ - using System; - - public class DownstreamRouteFinderTests - { - private readonly IDownstreamRouteProvider _downstreamRouteFinder; - private readonly Mock _mockMatcher; - private readonly Mock _finder; - private string _upstreamUrlPath; - private Response _result; - private List _reRoutesConfig; - private InternalConfiguration _config; - private Response _match; - private string _upstreamHttpMethod; - private string _upstreamHost; - private string _upstreamQuery; - - public DownstreamRouteFinderTests() - { - _mockMatcher = new Mock(); - _finder = new Mock(); - _downstreamRouteFinder = new Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder(_mockMatcher.Object, _finder.Object); - } - - [Fact] - public void should_return_highest_priority_when_first() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 0, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 0, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig)) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) - .WithUpstreamHttpMethod(new List { "Post" }) - .Build()) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) - .WithUpstreamHttpMethod(new List { "Post" }) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_return_highest_priority_when_lowest() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 0, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 0, false, "someUpstreamPath")) - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig)) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) - .WithUpstreamHttpMethod(new List { "Post" }) - .Build()) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) - .WithUpstreamHttpMethod(new List { "Post" }) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_return_route() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_not_append_slash_to_upstream_url_path() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher")) - .BDDfy(); - } - - [Fact] - public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_return_correct_route_for_http_verb() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPathForAPost") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPathForAPost") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_not_return_route() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/")) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("somPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("somePath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("somePath", 1, false, "someUpstreamPath")) - .Build(), - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(false)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenAnErrorResponseIsReturned()) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_return_correct_route_for_http_verb_setting_multiple_upstream_http_method() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get", "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get", "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_return_correct_route_for_http_verb_setting_all_upstream_http_method() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List()) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List()) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Post" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build() - ))) - .BDDfy(); - } - - [Fact] - public void should_not_return_route_for_http_verb_not_setting_in_upstream_http_method() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) - .And( - x => - x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenAnErrorResponseIsReturned()) - .And(x => x.ThenTheUrlMatcherIsNotCalled()) - .BDDfy(); - } - - [Fact] - public void should_return_route_when_host_matches() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("MATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_return_route_when_upstreamhost_is_null() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("MATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_not_return_route_when_host_doesnt_match() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("DONTMATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { }) // empty list of methods - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { }) // empty list of methods - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenAnErrorResponseIsReturned()) - .And(x => x.ThenTheUrlMatcherIsNotCalled()) - .BDDfy(); - } - - [Fact] - public void should_not_return_route_when_host_doesnt_match_with_empty_upstream_http_method() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("DONTMATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List()) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List()) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then(x => x.ThenAnErrorResponseIsReturned()) - .And(x => x.ThenTheUrlMatcherIsNotCalled()) - .BDDfy(); - } - - [Fact] - public void should_return_route_when_host_does_match_with_empty_upstream_http_method() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("MATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List()) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List()) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 0)) - .BDDfy(); - } - - [Fact] - public void should_return_route_when_host_matches_but_null_host_on_same_path_first() - { - var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); - - this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) - .And(x => GivenTheUpstreamHostIs("MATCH")) - .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( - new OkResponse>( - new List()))) - .And(x => x.GivenTheConfigurationIs(new List - { - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("THENULLPATH") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) - .WithUpstreamHost("MATCH") - .Build() - }, string.Empty, serviceProviderConfig - )) - .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) - .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) - .When(x => x.WhenICallTheFinder()) - .Then( - x => x.ThenTheFollowingIsReturned(new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("someDownstreamPath") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "test")) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "test")) - .Build() - ))) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 0)) - .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 1)) - .BDDfy(); - } - - private void GivenTheUpstreamHostIs(string upstreamHost) - { - _upstreamHost = upstreamHost; - } - - private void GivenTheTemplateVariableAndNameFinderReturns(Response> response) - { - _finder - .Setup(x => x.Find(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(response); - } - - private void GivenTheUpstreamHttpMethodIs(string upstreamHttpMethod) - { - _upstreamHttpMethod = upstreamHttpMethod; - } - - private void ThenAnErrorResponseIsReturned() - { - _result.IsError.ShouldBeTrue(); - } - - private void ThenTheUrlMatcherIsCalledCorrectly() - { - _mockMatcher - .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Once); - } - - private void ThenTheUrlMatcherIsCalledCorrectly(int times, int index = 0) - { - _mockMatcher - .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[index].UpstreamTemplatePattern), Times.Exactly(times)); - } - - private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath) - { - _mockMatcher - .Verify(x => x.Match(expectedUpstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Once); - } - - private void ThenTheUrlMatcherIsNotCalled() - { - _mockMatcher - .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _reRoutesConfig[0].UpstreamTemplatePattern), Times.Never); - } - - private void GivenTheUrlMatcherReturns(Response match) - { - _match = match; - _mockMatcher - .Setup(x => x.Match(It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(_match); - } - - private void GivenTheConfigurationIs(List reRoutesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig) - { - _reRoutesConfig = reRoutesConfig; - _config = new InternalConfiguration(_reRoutesConfig, adminPath, serviceProviderConfig, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); - } - - private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) - { - _upstreamUrlPath = upstreamUrlPath; - } - - private void WhenICallTheFinder() - { - _result = _downstreamRouteFinder.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _config, _upstreamHost); - } - - private void ThenTheFollowingIsReturned(DownstreamRoute expected) - { - _result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value); - _result.Data.ReRoute.UpstreamTemplatePattern.Priority.ShouldBe(expected.ReRoute.UpstreamTemplatePattern.Priority); - - for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++) - { - _result.Data.TemplatePlaceholderNameAndValues[i].Name.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Name); - _result.Data.TemplatePlaceholderNameAndValues[i].Value.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Value); - } - - _result.IsError.ShouldBeFalse(); - } - } -} +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.DownstreamRouteFinder; +using Ocelot.DownstreamRouteFinder.Finder; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Responses; +using Ocelot.Values; +using Shouldly; +using System.Collections.Generic; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.DownstreamRouteFinder +{ + using System; + + public class DownstreamRouteFinderTests + { + private readonly IDownstreamRouteProvider _downstreamRouteFinder; + private readonly Mock _mockMatcher; + private readonly Mock _finder; + private string _upstreamUrlPath; + private Response _result; + private List _routesConfig; + private InternalConfiguration _config; + private Response _match; + private string _upstreamHttpMethod; + private string _upstreamHost; + private string _upstreamQuery; + + public DownstreamRouteFinderTests() + { + _mockMatcher = new Mock(); + _finder = new Mock(); + _downstreamRouteFinder = new Ocelot.DownstreamRouteFinder.Finder.DownstreamRouteFinder(_mockMatcher.Object, _finder.Object); + } + + [Fact] + public void should_return_highest_priority_when_first() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) + .Build(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 0, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 0, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig)) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) + .WithUpstreamHttpMethod(new List { "Post" }) + .Build()) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) + .WithUpstreamHttpMethod(new List { "Post" }) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_highest_priority_when_lowest() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 0, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 0, false, "someUpstreamPath")) + .Build(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig)) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) + .WithUpstreamHttpMethod(new List { "Post" }) + .Build()) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("test", 1, false, "someUpstreamPath")) + .WithUpstreamHttpMethod(new List { "Post" }) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_route() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_not_append_slash_to_upstream_url_path() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly("matchInUrlMatcher")) + .BDDfy(); + } + + [Fact] + public void should_return_route_if_upstream_path_and_upstream_template_are_the_same() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_correct_route_for_http_verb() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPathForAPost") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_not_return_route() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("dontMatchPath/")) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("somPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("somePath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("somePath", 1, false, "someUpstreamPath")) + .Build(), + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(false)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenAnErrorResponseIsReturned()) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_return_correct_route_for_http_verb_setting_multiple_upstream_http_method() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get", "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_return_correct_route_for_http_verb_setting_all_upstream_http_method() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List()) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List()) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Post" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build() + ))) + .BDDfy(); + } + + [Fact] + public void should_not_return_route_for_http_verb_not_setting_in_upstream_http_method() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) + .And( + x => + x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get", "Patch", "Delete" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Post")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenAnErrorResponseIsReturned()) + .And(x => x.ThenTheUrlMatcherIsNotCalled()) + .BDDfy(); + } + + [Fact] + public void should_return_route_when_host_matches() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("MATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .WithUpstreamHost("MATCH") + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_return_route_when_upstreamhost_is_null() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("MATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_not_return_route_when_host_doesnt_match() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("DONTMATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .WithUpstreamHost("MATCH") + .Build(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { }) // empty list of methods + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { }) // empty list of methods + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .WithUpstreamHost("MATCH") + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenAnErrorResponseIsReturned()) + .And(x => x.ThenTheUrlMatcherIsNotCalled()) + .BDDfy(); + } + + [Fact] + public void should_not_return_route_when_host_doesnt_match_with_empty_upstream_http_method() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("DONTMATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List()) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List()) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .WithUpstreamHost("MATCH") + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then(x => x.ThenAnErrorResponseIsReturned()) + .And(x => x.ThenTheUrlMatcherIsNotCalled()) + .BDDfy(); + } + + [Fact] + public void should_return_route_when_host_does_match_with_empty_upstream_http_method() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("MATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse>(new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List()) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List()) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .WithUpstreamHost("MATCH") + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 0)) + .BDDfy(); + } + + [Fact] + public void should_return_route_when_host_matches_but_null_host_on_same_path_first() + { + var serviceProviderConfig = new ServiceProviderConfigurationBuilder().Build(); + + this.Given(x => x.GivenThereIsAnUpstreamUrlPath("matchInUrlMatcher/")) + .And(x => GivenTheUpstreamHostIs("MATCH")) + .And(x => x.GivenTheTemplateVariableAndNameFinderReturns( + new OkResponse>( + new List()))) + .And(x => x.GivenTheConfigurationIs(new List + { + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("THENULLPATH") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath")) + .WithUpstreamHost("MATCH") + .Build() + }, string.Empty, serviceProviderConfig + )) + .And(x => x.GivenTheUrlMatcherReturns(new OkResponse(new UrlMatch(true)))) + .And(x => x.GivenTheUpstreamHttpMethodIs("Get")) + .When(x => x.WhenICallTheFinder()) + .Then( + x => x.ThenTheFollowingIsReturned(new DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("someDownstreamPath") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "test")) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "test")) + .Build() + ))) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 0)) + .And(x => x.ThenTheUrlMatcherIsCalledCorrectly(1, 1)) + .BDDfy(); + } + + private void GivenTheUpstreamHostIs(string upstreamHost) + { + _upstreamHost = upstreamHost; + } + + private void GivenTheTemplateVariableAndNameFinderReturns(Response> response) + { + _finder + .Setup(x => x.Find(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(response); + } + + private void GivenTheUpstreamHttpMethodIs(string upstreamHttpMethod) + { + _upstreamHttpMethod = upstreamHttpMethod; + } + + private void ThenAnErrorResponseIsReturned() + { + _result.IsError.ShouldBeTrue(); + } + + private void ThenTheUrlMatcherIsCalledCorrectly() + { + _mockMatcher + .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _routesConfig[0].UpstreamTemplatePattern), Times.Once); + } + + private void ThenTheUrlMatcherIsCalledCorrectly(int times, int index = 0) + { + _mockMatcher + .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _routesConfig[index].UpstreamTemplatePattern), Times.Exactly(times)); + } + + private void ThenTheUrlMatcherIsCalledCorrectly(string expectedUpstreamUrlPath) + { + _mockMatcher + .Verify(x => x.Match(expectedUpstreamUrlPath, _upstreamQuery, _routesConfig[0].UpstreamTemplatePattern), Times.Once); + } + + private void ThenTheUrlMatcherIsNotCalled() + { + _mockMatcher + .Verify(x => x.Match(_upstreamUrlPath, _upstreamQuery, _routesConfig[0].UpstreamTemplatePattern), Times.Never); + } + + private void GivenTheUrlMatcherReturns(Response match) + { + _match = match; + _mockMatcher + .Setup(x => x.Match(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(_match); + } + + private void GivenTheConfigurationIs(List routesConfig, string adminPath, ServiceProviderConfiguration serviceProviderConfig) + { + _routesConfig = routesConfig; + _config = new InternalConfiguration(_routesConfig, adminPath, serviceProviderConfig, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); + } + + private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath) + { + _upstreamUrlPath = upstreamUrlPath; + } + + private void WhenICallTheFinder() + { + _result = _downstreamRouteFinder.Get(_upstreamUrlPath, _upstreamQuery, _upstreamHttpMethod, _config, _upstreamHost); + } + + private void ThenTheFollowingIsReturned(Ocelot.DownstreamRouteFinder.DownstreamRouteHolder expected) + { + _result.Data.Route.DownstreamRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.Route.DownstreamRoute[0].DownstreamPathTemplate.Value); + _result.Data.Route.UpstreamTemplatePattern.Priority.ShouldBe(expected.Route.UpstreamTemplatePattern.Priority); + + for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++) + { + _result.Data.TemplatePlaceholderNameAndValues[i].Name.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Name); + _result.Data.TemplatePlaceholderNameAndValues[i].Value.ShouldBe(expected.TemplatePlaceholderNameAndValues[i].Value); + } + + _result.IsError.ShouldBeFalse(); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs index fa958cedf..edfa03ddb 100644 --- a/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/DownstreamRouteFinder/DownstreamRouteProviderFactoryTests.cs @@ -1,164 +1,164 @@ -namespace Ocelot.UnitTests.DownstreamRouteFinder -{ - using System; - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Configuration.Creator; - using Ocelot.DownstreamRouteFinder.Finder; - using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.Logging; - using Shouldly; - using System.Collections.Generic; - using TestStack.BDDfy; - using Xunit; - - public class DownstreamRouteProviderFactoryTests - { - private readonly DownstreamRouteProviderFactory _factory; - private IInternalConfiguration _config; - private IDownstreamRouteProvider _result; - private Mock _logger; - private Mock _loggerFactory; - - public DownstreamRouteProviderFactoryTests() - { - var services = new ServiceCollection(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - var provider = services.BuildServiceProvider(); - _logger = new Mock(); - _loggerFactory = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _factory = new DownstreamRouteProviderFactory(provider, _loggerFactory.Object); - } - - [Fact] - public void should_return_downstream_route_finder() - { - var reRoutes = new List - { - new ReRouteBuilder().Build() - }; - - this.Given(_ => GivenTheReRoutes(reRoutes)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheResultShouldBe()) - .BDDfy(); - } - - [Fact] - public void should_return_downstream_route_finder_when_not_dynamic_re_route_and_service_discovery_on() - { - var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("test").WithPort(50).WithType("test").Build(); - var reRoutes = new List - { - new ReRouteBuilder().WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("woot").Build()).Build() - }; - - this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheResultShouldBe()) - .BDDfy(); - } - - [Fact] - public void should_return_downstream_route_finder_as_no_service_discovery_given_no_scheme() - { - var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("").WithHost("test").WithPort(50).Build(); - var reRoutes = new List(); - - this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheResultShouldBe()) - .BDDfy(); - } - - [Fact] - public void should_return_downstream_route_finder_as_no_service_discovery_given_no_host() - { - var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("").WithPort(50).Build(); - var reRoutes = new List(); - - this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheResultShouldBe()) - .BDDfy(); - } - - [Fact] - public void should_return_downstream_route_finder_given_no_service_discovery_port() - { - var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("localhost").WithPort(0).Build(); - var reRoutes = new List(); - - this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheResultShouldBe()) - .BDDfy(); - } - - [Fact] - public void should_return_downstream_route_finder_given_no_service_discovery_type() - { - var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("localhost").WithPort(50).WithType("").Build(); - var reRoutes = new List(); - - this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheResultShouldBe()) - .BDDfy(); - } - - [Fact] - public void should_return_downstream_route_creator() - { - var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("test").WithPort(50).WithType("test").Build(); - var reRoutes = new List(); - - this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheResultShouldBe()) - .BDDfy(); - } - - [Fact] - public void should_return_downstream_route_creator_with_dynamic_re_route() - { - var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("test").WithPort(50).WithType("test").Build(); - var reRoutes = new List - { - new ReRouteBuilder().Build() - }; - - this.Given(_ => GivenTheReRoutes(reRoutes, spConfig)) - .When(_ => WhenIGet()) - .Then(_ => ThenTheResultShouldBe()) - .BDDfy(); - } - - private void ThenTheResultShouldBe() - { - _result.ShouldBeOfType(); - } - - private void WhenIGet() - { - _result = _factory.Get(_config); - } - - private void GivenTheReRoutes(List reRoutes) - { - _config = new InternalConfiguration(reRoutes, "", null, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); - } - - private void GivenTheReRoutes(List reRoutes, ServiceProviderConfiguration config) - { - _config = new InternalConfiguration(reRoutes, "", config, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); - } - } -} +namespace Ocelot.UnitTests.DownstreamRouteFinder +{ + using System; + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Configuration.Creator; + using Ocelot.DownstreamRouteFinder.Finder; + using Ocelot.DownstreamRouteFinder.UrlMatcher; + using Ocelot.Logging; + using Shouldly; + using System.Collections.Generic; + using TestStack.BDDfy; + using Xunit; + + public class DownstreamRouteProviderFactoryTests + { + private readonly DownstreamRouteProviderFactory _factory; + private IInternalConfiguration _config; + private IDownstreamRouteProvider _result; + private Mock _logger; + private Mock _loggerFactory; + + public DownstreamRouteProviderFactoryTests() + { + var services = new ServiceCollection(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + var provider = services.BuildServiceProvider(); + _logger = new Mock(); + _loggerFactory = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _factory = new DownstreamRouteProviderFactory(provider, _loggerFactory.Object); + } + + [Fact] + public void should_return_downstream_route_finder() + { + var routes = new List + { + new RouteBuilder().Build() + }; + + this.Given(_ => GivenTheRoutes(routes)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_finder_when_not_dynamic_re_route_and_service_discovery_on() + { + var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("test").WithPort(50).WithType("test").Build(); + var routes = new List + { + new RouteBuilder().WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("woot").Build()).Build() + }; + + this.Given(_ => GivenTheRoutes(routes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_finder_as_no_service_discovery_given_no_scheme() + { + var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("").WithHost("test").WithPort(50).Build(); + var routes = new List(); + + this.Given(_ => GivenTheRoutes(routes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_finder_as_no_service_discovery_given_no_host() + { + var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("").WithPort(50).Build(); + var routes = new List(); + + this.Given(_ => GivenTheRoutes(routes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_finder_given_no_service_discovery_port() + { + var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("localhost").WithPort(0).Build(); + var routes = new List(); + + this.Given(_ => GivenTheRoutes(routes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_finder_given_no_service_discovery_type() + { + var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("localhost").WithPort(50).WithType("").Build(); + var routes = new List(); + + this.Given(_ => GivenTheRoutes(routes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_creator() + { + var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("test").WithPort(50).WithType("test").Build(); + var routes = new List(); + + this.Given(_ => GivenTheRoutes(routes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + [Fact] + public void should_return_downstream_route_creator_with_dynamic_re_route() + { + var spConfig = new ServiceProviderConfigurationBuilder().WithScheme("http").WithHost("test").WithPort(50).WithType("test").Build(); + var routes = new List + { + new RouteBuilder().Build() + }; + + this.Given(_ => GivenTheRoutes(routes, spConfig)) + .When(_ => WhenIGet()) + .Then(_ => ThenTheResultShouldBe()) + .BDDfy(); + } + + private void ThenTheResultShouldBe() + { + _result.ShouldBeOfType(); + } + + private void WhenIGet() + { + _result = _factory.Get(_config); + } + + private void GivenTheRoutes(List routes) + { + _config = new InternalConfiguration(routes, "", null, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); + } + + private void GivenTheRoutes(List routes, ServiceProviderConfiguration config) + { + _config = new InternalConfiguration(routes, "", config, "", new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), new HttpHandlerOptionsBuilder().Build(), new Version("1.1")); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs index 0cf8ff44b..3e20ab6a1 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/DownstreamUrlCreatorMiddlewareTests.cs @@ -1,440 +1,440 @@ -namespace Ocelot.UnitTests.DownstreamUrlCreator -{ - using Microsoft.AspNetCore.Http; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.DownstreamUrlCreator.Middleware; - using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; - using Ocelot.Logging; - using Ocelot.Middleware; - using Ocelot.Request.Middleware; - using Ocelot.Responses; - using Ocelot.Values; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Net.Http; - using System.Threading.Tasks; - using Ocelot.Infrastructure.RequestData; - using TestStack.BDDfy; +namespace Ocelot.UnitTests.DownstreamUrlCreator +{ + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.UrlMatcher; + using Ocelot.DownstreamUrlCreator.Middleware; + using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; + using Ocelot.Logging; + using Ocelot.Middleware; + using Ocelot.Request.Middleware; + using Ocelot.Responses; + using Ocelot.Values; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Net.Http; + using System.Threading.Tasks; + using Ocelot.Infrastructure.RequestData; + using TestStack.BDDfy; using Xunit; using Ocelot.DownstreamRouteFinder.Middleware; - public class DownstreamUrlCreatorMiddlewareTests - { - private readonly Mock _downstreamUrlTemplateVariableReplacer; - private OkResponse _downstreamPath; - private readonly Mock _loggerFactory; - private Mock _logger; - private DownstreamUrlCreatorMiddleware _middleware; - private readonly RequestDelegate _next; - private readonly HttpRequestMessage _request; - private HttpContext _httpContext; - private Mock _repo; - - public DownstreamUrlCreatorMiddlewareTests() - { - _repo = new Mock(); - _httpContext = new DefaultHttpContext(); - _loggerFactory = new Mock(); - _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _downstreamUrlTemplateVariableReplacer = new Mock(); + public class DownstreamUrlCreatorMiddlewareTests + { + private readonly Mock _downstreamUrlTemplateVariableReplacer; + private OkResponse _downstreamPath; + private readonly Mock _loggerFactory; + private Mock _logger; + private DownstreamUrlCreatorMiddleware _middleware; + private readonly RequestDelegate _next; + private readonly HttpRequestMessage _request; + private HttpContext _httpContext; + private Mock _repo; + + public DownstreamUrlCreatorMiddlewareTests() + { + _repo = new Mock(); + _httpContext = new DefaultHttpContext(); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _downstreamUrlTemplateVariableReplacer = new Mock(); _request = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"); - _next = context => Task.CompletedTask; - } - - [Fact] - public void should_replace_scheme_and_path() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithDownstreamScheme("https") - .Build(); - - var config = new ServiceProviderConfigurationBuilder() - .Build(); - - this.Given(x => x.GivenTheDownStreamRouteIs( - new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123")) - .And(x => GivenTheServiceProviderConfigIs(config)) - .And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123")) - .And(x => ThenTheQueryStringIs("?q=123")) - .BDDfy(); - } - - [Fact] - public void should_replace_query_string() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithDownstreamScheme("https") - .Build(); - - var config = new ServiceProviderConfigurationBuilder() - .Build(); - - this.Given(x => x.GivenTheDownStreamRouteIs( - new DownstreamRoute( - new List - { - new PlaceholderNameAndValue("{subscriptionId}", "1"), - new PlaceholderNameAndValue("{unitId}", "2") - }, - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2")) - .And(x => GivenTheServiceProviderConfigIs(config)) - .And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates")) - .And(x => ThenTheQueryStringIs("")) - .BDDfy(); - } - - [Fact] - public void should_replace_query_string_but_leave_non_placeholder_queries() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithDownstreamScheme("https") - .Build(); - - var config = new ServiceProviderConfigurationBuilder() - .Build(); - - this.Given(x => x.GivenTheDownStreamRouteIs( - new DownstreamRoute( - new List - { - new PlaceholderNameAndValue("{subscriptionId}", "1"), - new PlaceholderNameAndValue("{unitId}", "2") - }, - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2&productId=2")) - .And(x => GivenTheServiceProviderConfigIs(config)) - .And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates?productId=2")) - .And(x => ThenTheQueryStringIs("?productId=2")) - .BDDfy(); - } - - [Fact] - public void should_replace_query_string_exact_match() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates/{unitIdIty}") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithDownstreamScheme("https") - .Build(); - - var config = new ServiceProviderConfigurationBuilder() - .Build(); - - this.Given(x => x.GivenTheDownStreamRouteIs( - new DownstreamRoute( - new List - { - new PlaceholderNameAndValue("{subscriptionId}", "1"), - new PlaceholderNameAndValue("{unitId}", "2"), - new PlaceholderNameAndValue("{unitIdIty}", "3") - }, - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2?unitIdIty=3")) - .And(x => GivenTheServiceProviderConfigIs(config)) - .And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates/3")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates/3")) - .And(x => ThenTheQueryStringIs("")) - .BDDfy(); - } - - [Fact] - public void should_not_create_service_fabric_url() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithUpstreamHttpMethod(new List { "Get" }) - .WithDownstreamScheme("https") - .Build(); - - var config = new ServiceProviderConfigurationBuilder() - .WithType("ServiceFabric") - .WithHost("localhost") - .WithPort(19081) - .Build(); - - this.Given(x => x.GivenTheDownStreamRouteIs( - new DownstreamRoute( - new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123")) - .And(x => GivenTheServiceProviderConfigIs(config)) - .And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123")) - .BDDfy(); - } - - [Fact] - public void should_create_service_fabric_url() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamScheme("http") - .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("http://localhost:19081")) - .And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1")) - .BDDfy(); - } - - [Fact] - public void should_create_service_fabric_url_with_query_string_for_stateless_service() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamScheme("http") - .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("http://localhost:19081?Tom=test&laura=1")) - .And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?Tom=test&laura=1")) - .BDDfy(); - } - - [Fact] - public void should_create_service_fabric_url_with_query_string_for_stateful_service() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamScheme("http") - .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("http://localhost:19081?PartitionKind=test&PartitionKey=1")) - .And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?PartitionKind=test&PartitionKey=1")) - .BDDfy(); - } - - [Fact] - public void should_create_service_fabric_url_with_version_from_upstream_path_template() - { - var downstreamRoute = new DownstreamRoute( - new List(), - new ReRouteBuilder().WithDownstreamReRoute( - new DownstreamReRouteBuilder() - .WithDownstreamScheme("http") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("/products").Build()) - .WithUseServiceDiscovery(true) - .Build() - ).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("http://localhost:19081?PartitionKind=test&PartitionKey=1")) - .And(x => x.GivenTheUrlReplacerWillReturnSequence("/products", "Service_1.0/Api")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Service_1.0/Api/products?PartitionKind=test&PartitionKey=1")) - .BDDfy(); - } - - [Fact] - public void issue_473_should_not_remove_additional_query_string() - { - var downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/Authorized/{action}?server={server}") - .WithUpstreamHttpMethod(new List { "Post", "Get" }) - .WithDownstreamScheme("http") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("/uc/Authorized/{server}/{action}").Build()) - .Build(); - - var config = new ServiceProviderConfigurationBuilder() - .Build(); - - this.Given(x => x.GivenTheDownStreamRouteIs( - new DownstreamRoute( - new List - { - new PlaceholderNameAndValue("{action}", "1"), - new PlaceholderNameAndValue("{server}", "2") - }, - new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) - .WithUpstreamHttpMethod(new List { "Post", "Get" }) - .Build()))) - .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/uc/Authorized/2/1/refresh?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d")) - .And(x => GivenTheServiceProviderConfigIs(config)) - .And(x => x.GivenTheUrlReplacerWillReturn("/Authorized/1?server=2")) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:5000/Authorized/1?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d&server=2")) - .And(x => ThenTheQueryStringIs("?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d&server=2")) - .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) - { + _next = context => Task.CompletedTask; + } + + [Fact] + public void should_replace_scheme_and_path() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithDownstreamScheme("https") + .Build(); + + var config = new ServiceProviderConfigurationBuilder() + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs( + new DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123")) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123")) + .And(x => ThenTheQueryStringIs("?q=123")) + .BDDfy(); + } + + [Fact] + public void should_replace_query_string() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithDownstreamScheme("https") + .Build(); + + var config = new ServiceProviderConfigurationBuilder() + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs( + new DownstreamRouteHolder( + new List + { + new PlaceholderNameAndValue("{subscriptionId}", "1"), + new PlaceholderNameAndValue("{unitId}", "2") + }, + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2")) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates")) + .And(x => ThenTheQueryStringIs("")) + .BDDfy(); + } + + [Fact] + public void should_replace_query_string_but_leave_non_placeholder_queries() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithDownstreamScheme("https") + .Build(); + + var config = new ServiceProviderConfigurationBuilder() + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs( + new DownstreamRouteHolder( + new List + { + new PlaceholderNameAndValue("{subscriptionId}", "1"), + new PlaceholderNameAndValue("{unitId}", "2") + }, + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2&productId=2")) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates?productId=2")) + .And(x => ThenTheQueryStringIs("?productId=2")) + .BDDfy(); + } + + [Fact] + public void should_replace_query_string_exact_match() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("/api/units/{subscriptionId}/{unitId}/updates/{unitIdIty}") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithDownstreamScheme("https") + .Build(); + + var config = new ServiceProviderConfigurationBuilder() + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs( + new DownstreamRouteHolder( + new List + { + new PlaceholderNameAndValue("{subscriptionId}", "1"), + new PlaceholderNameAndValue("{unitId}", "2"), + new PlaceholderNameAndValue("{unitIdIty}", "3") + }, + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/api/subscriptions/1/updates?unitId=2?unitIdIty=3")) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheUrlReplacerWillReturn("api/units/1/2/updates/3")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("https://localhost:5000/api/units/1/2/updates/3")) + .And(x => ThenTheQueryStringIs("")) + .BDDfy(); + } + + [Fact] + public void should_not_create_service_fabric_url() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithUpstreamHttpMethod(new List { "Get" }) + .WithDownstreamScheme("https") + .Build(); + + var config = new ServiceProviderConfigurationBuilder() + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs( + new DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .And(x => x.GivenTheDownstreamRequestUriIs("http://my.url/abc?q=123")) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheUrlReplacerWillReturn("/api/products/1")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("https://my.url:80/api/products/1?q=123")) + .BDDfy(); + } + + [Fact] + public void should_create_service_fabric_url() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamScheme("http") + .WithServiceName("Ocelot/OcelotApp") + .WithUseServiceDiscovery(true) + .Build(); + + var downstreamRouteHolder = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .Build()); + + var config = new ServiceProviderConfigurationBuilder() + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRouteHolder)) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081")) + .And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1")) + .BDDfy(); + } + + [Fact] + public void should_create_service_fabric_url_with_query_string_for_stateless_service() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamScheme("http") + .WithServiceName("Ocelot/OcelotApp") + .WithUseServiceDiscovery(true) + .Build(); + + var downstreamRouteHolder = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .Build()); + + var config = new ServiceProviderConfigurationBuilder() + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRouteHolder)) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081?Tom=test&laura=1")) + .And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?Tom=test&laura=1")) + .BDDfy(); + } + + [Fact] + public void should_create_service_fabric_url_with_query_string_for_stateful_service() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamScheme("http") + .WithServiceName("Ocelot/OcelotApp") + .WithUseServiceDiscovery(true) + .Build(); + + var downstreamRouteHolder = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .Build()); + + var config = new ServiceProviderConfigurationBuilder() + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRouteHolder)) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:19081?PartitionKind=test&PartitionKey=1")) + .And(x => x.GivenTheUrlReplacerWillReturnSequence("/api/products/1", "Ocelot/OcelotApp")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Ocelot/OcelotApp/api/products/1?PartitionKind=test&PartitionKey=1")) + .BDDfy(); + } + + [Fact] + public void should_create_service_fabric_url_with_version_from_upstream_path_template() + { + var downstreamRoute = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder( + new List(), + new RouteBuilder().WithDownstreamRoute( + new DownstreamRouteBuilder() + .WithDownstreamScheme("http") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("/products").Build()) + .WithUseServiceDiscovery(true) + .Build() + ).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("http://localhost:19081?PartitionKind=test&PartitionKey=1")) + .And(x => x.GivenTheUrlReplacerWillReturnSequence("/products", "Service_1.0/Api")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:19081/Service_1.0/Api/products?PartitionKind=test&PartitionKey=1")) + .BDDfy(); + } + + [Fact] + public void issue_473_should_not_remove_additional_query_string() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("/Authorized/{action}?server={server}") + .WithUpstreamHttpMethod(new List { "Post", "Get" }) + .WithDownstreamScheme("http") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("/uc/Authorized/{server}/{action}").Build()) + .Build(); + + var config = new ServiceProviderConfigurationBuilder() + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs( + new DownstreamRouteHolder( + new List + { + new PlaceholderNameAndValue("{action}", "1"), + new PlaceholderNameAndValue("{server}", "2") + }, + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .WithUpstreamHttpMethod(new List { "Post", "Get" }) + .Build()))) + .And(x => x.GivenTheDownstreamRequestUriIs("http://localhost:5000/uc/Authorized/2/1/refresh?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d")) + .And(x => GivenTheServiceProviderConfigIs(config)) + .And(x => x.GivenTheUrlReplacerWillReturn("/Authorized/1?server=2")) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheDownstreamRequestUriIs("http://localhost:5000/Authorized/1?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d&server=2")) + .And(x => ThenTheQueryStringIs("?refreshToken=2288356cfb1338fdc5ff4ca558ec785118dfe1ff2864340937da8226863ff66d&server=2")) + .BDDfy(); + } + + [Fact] + public void should_not_replace_by_empty_scheme() + { + var downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamScheme("") + .WithServiceName("Ocelot/OcelotApp") + .WithUseServiceDiscovery(true) + .Build(); + + var downstreamRouteHolder = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder( + new List(), + new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) + .Build()); + + var config = new ServiceProviderConfigurationBuilder() + .WithType("ServiceFabric") + .WithHost("localhost") + .WithPort(19081) + .Build(); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRouteHolder)) + .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, null); - _httpContext.Items.SetIInternalConfiguration(configuration); - } - - private void WhenICallTheMiddleware() - { - _middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object); - _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); - } - - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + _httpContext.Items.SetIInternalConfiguration(configuration); + } + + private void WhenICallTheMiddleware() + { + _middleware = new DownstreamUrlCreatorMiddleware(_next, _loggerFactory.Object, _downstreamUrlTemplateVariableReplacer.Object); + _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); + } + + private void GivenTheDownStreamRouteIs(Ocelot.DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) { _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); - } - - private void GivenTheDownstreamRequestUriIs(string uri) - { + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); + } + + private void GivenTheDownstreamRequestUriIs(string uri) + { _request.RequestUri = new Uri(uri); _httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(_request)); - } - - private void GivenTheUrlReplacerWillReturnSequence(params string[] paths) - { - var setup = _downstreamUrlTemplateVariableReplacer - .SetupSequence(x => x.Replace(It.IsAny(), It.IsAny>())); - foreach (var path in paths) - { - var response = new OkResponse(new DownstreamPath(path)); - setup.Returns(response); - } - } - - private void GivenTheUrlReplacerWillReturn(string path) - { - _downstreamPath = new OkResponse(new DownstreamPath(path)); - _downstreamUrlTemplateVariableReplacer - .Setup(x => x.Replace(It.IsAny(), It.IsAny>())) - .Returns(_downstreamPath); - } - - private void ThenTheDownstreamRequestUriIs(string expectedUri) - { - _httpContext.Items.DownstreamRequest().ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri); - } - - private void ThenTheQueryStringIs(string queryString) - { - _httpContext.Items.DownstreamRequest().Query.ShouldBe(queryString); - } - } -} + } + + private void GivenTheUrlReplacerWillReturnSequence(params string[] paths) + { + var setup = _downstreamUrlTemplateVariableReplacer + .SetupSequence(x => x.Replace(It.IsAny(), It.IsAny>())); + foreach (var path in paths) + { + var response = new OkResponse(new DownstreamPath(path)); + setup.Returns(response); + } + } + + private void GivenTheUrlReplacerWillReturn(string path) + { + _downstreamPath = new OkResponse(new DownstreamPath(path)); + _downstreamUrlTemplateVariableReplacer + .Setup(x => x.Replace(It.IsAny(), It.IsAny>())) + .Returns(_downstreamPath); + } + + private void ThenTheDownstreamRequestUriIs(string expectedUri) + { + _httpContext.Items.DownstreamRequest().ToHttpRequestMessage().RequestUri.OriginalString.ShouldBe(expectedUri); + } + + private void ThenTheQueryStringIs(string queryString) + { + _httpContext.Items.DownstreamRequest().Query.ShouldBe(queryString); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs index 1c27898aa..ac3381359 100644 --- a/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs +++ b/test/Ocelot.UnitTests/DownstreamUrlCreator/UrlTemplateReplacer/UpstreamUrlPathTemplateVariableReplacerTests.cs @@ -1,210 +1,210 @@ -using Ocelot.Configuration.Builder; -using Ocelot.DownstreamRouteFinder; -using Ocelot.DownstreamRouteFinder.UrlMatcher; -using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; -using Ocelot.Responses; -using Ocelot.Values; -using Shouldly; -using System.Collections.Generic; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer -{ - public class UpstreamUrlPathTemplateVariableReplacerTests - { - private DownstreamRoute _downstreamRoute; - private Response _result; - private readonly IDownstreamPathPlaceholderReplacer _downstreamPathReplacer; - - public UpstreamUrlPathTemplateVariableReplacerTests() - { - _downstreamPathReplacer = new DownstreamTemplatePathPlaceholderReplacer(); - } - - [Fact] - public void can_replace_no_template_variables() - { - this.Given(x => x.GivenThereIsAUrlMatch( - new DownstreamRoute( +using Ocelot.Configuration.Builder; +using Ocelot.DownstreamRouteFinder; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; +using Ocelot.Responses; +using Ocelot.Values; +using Shouldly; +using System.Collections.Generic; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer +{ + public class UpstreamUrlPathTemplateVariableReplacerTests + { + private DownstreamRouteHolder _downstreamRoute; + private Response _result; + private readonly IDownstreamPathPlaceholderReplacer _downstreamPathReplacer; + + public UpstreamUrlPathTemplateVariableReplacerTests() + { + _downstreamPathReplacer = new DownstreamTemplatePathPlaceholderReplacer(); + } + + [Fact] + public void can_replace_no_template_variables() + { + this.Given(x => x.GivenThereIsAUrlMatch( + new DownstreamRouteHolder( new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("")) - .BDDfy(); - } - - [Fact] - public void can_replace_no_template_variables_with_slash() - { - this.Given(x => x.GivenThereIsAUrlMatch( - new DownstreamRoute( + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("")) + .BDDfy(); + } + + [Fact] + public void can_replace_no_template_variables_with_slash() + { + this.Given(x => x.GivenThereIsAUrlMatch( + new DownstreamRouteHolder( new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("/") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("/")) - .BDDfy(); - } - - [Fact] - public void can_replace_url_no_slash() - { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("api") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api")) - .BDDfy(); - } - - [Fact] - public void can_replace_url_one_slash() - { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("api/") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/")) - .BDDfy(); - } - - [Fact] - public void can_replace_url_multiple_slash() - { - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("api/product/products/") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/")) - .BDDfy(); - } - - [Fact] - public void can_replace_url_one_template_variable() - { - var templateVariables = new List() - { - new PlaceholderNameAndValue("{productId}", "1") - }; - - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("productservice/products/{productId}/") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/")) - .BDDfy(); - } - - [Fact] - public void can_replace_url_one_template_variable_with_path_after() - { - var templateVariables = new List() - { - new PlaceholderNameAndValue("{productId}", "1") - }; - - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("productservice/products/{productId}/variants") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants")) - .BDDfy(); - } - - [Fact] - public void can_replace_url_two_template_variable() - { - var templateVariables = new List() - { - new PlaceholderNameAndValue("{productId}", "1"), - new PlaceholderNameAndValue("{variantId}", "12") - }; - - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12")) - .BDDfy(); + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("/") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("/")) + .BDDfy(); + } + + [Fact] + public void can_replace_url_no_slash() + { + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("api") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api")) + .BDDfy(); + } + + [Fact] + public void can_replace_url_one_slash() + { + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("api/") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/")) + .BDDfy(); + } + + [Fact] + public void can_replace_url_multiple_slash() + { + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("api/product/products/") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/")) + .BDDfy(); + } + + [Fact] + public void can_replace_url_one_template_variable() + { + var templateVariables = new List() + { + new PlaceholderNameAndValue("{productId}", "1") + }; + + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRouteHolder(templateVariables, + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/")) + .BDDfy(); } - [Fact] - public void can_replace_url_three_template_variable() - { - var templateVariables = new List() - { - new PlaceholderNameAndValue("{productId}", "1"), - new PlaceholderNameAndValue("{variantId}", "12"), - new PlaceholderNameAndValue("{categoryId}", "34") - }; - - this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}") - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()))) - .When(x => x.WhenIReplaceTheTemplateVariables()) - .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12")) - .BDDfy(); - } - - private void GivenThereIsAUrlMatch(DownstreamRoute downstreamRoute) - { - _downstreamRoute = downstreamRoute; - } - - private void WhenIReplaceTheTemplateVariables() - { - _result = _downstreamPathReplacer.Replace(_downstreamRoute.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value, _downstreamRoute.TemplatePlaceholderNameAndValues); - } - - private void ThenTheDownstreamUrlPathIsReturned(string expected) - { - _result.Data.Value.ShouldBe(expected); - } - } + [Fact] + public void can_replace_url_one_template_variable_with_path_after() + { + var templateVariables = new List() + { + new PlaceholderNameAndValue("{productId}", "1") + }; + + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRouteHolder(templateVariables, + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/variants") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants")) + .BDDfy(); + } + + [Fact] + public void can_replace_url_two_template_variable() + { + var templateVariables = new List() + { + new PlaceholderNameAndValue("{productId}", "1"), + new PlaceholderNameAndValue("{variantId}", "12") + }; + + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRouteHolder(templateVariables, + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("productservice/products/{productId}/variants/{variantId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12")) + .BDDfy(); + } + + [Fact] + public void can_replace_url_three_template_variable() + { + var templateVariables = new List() + { + new PlaceholderNameAndValue("{productId}", "1"), + new PlaceholderNameAndValue("{variantId}", "12"), + new PlaceholderNameAndValue("{categoryId}", "34") + }; + + this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRouteHolder(templateVariables, + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}") + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()))) + .When(x => x.WhenIReplaceTheTemplateVariables()) + .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12")) + .BDDfy(); + } + + private void GivenThereIsAUrlMatch(DownstreamRouteHolder downstreamRoute) + { + _downstreamRoute = downstreamRoute; + } + + private void WhenIReplaceTheTemplateVariables() + { + _result = _downstreamPathReplacer.Replace(_downstreamRoute.Route.DownstreamRoute[0].DownstreamPathTemplate.Value, _downstreamRoute.TemplatePlaceholderNameAndValues); + } + + private void ThenTheDownstreamUrlPathIsReturned(string expected) + { + _result.Data.Value.ShouldBe(expected); + } + } } diff --git a/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs index 04e40db4a..9e1fd3e2c 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs @@ -1,37 +1,37 @@ -namespace Ocelot.UnitTests.Eureka -{ - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration.Builder; - using Provider.Eureka; - using Shouldly; - using Steeltoe.Common.Discovery; - using Xunit; - - public class EurekaProviderFactoryTests - { - [Fact] - public void should_not_get() - { - var config = new ServiceProviderConfigurationBuilder().Build(); - var sp = new ServiceCollection().BuildServiceProvider(); - var provider = EurekaProviderFactory.Get(sp, config, null); - provider.ShouldBeNull(); - } - - [Fact] - public void should_get() - { - var config = new ServiceProviderConfigurationBuilder().WithType("eureka").Build(); - var client = new Mock(); - var services = new ServiceCollection(); - services.AddSingleton(client.Object); - var sp = services.BuildServiceProvider(); - var reRoute = new DownstreamReRouteBuilder() - .WithServiceName("") - .Build(); - var provider = EurekaProviderFactory.Get(sp, config, reRoute); - provider.ShouldBeOfType(); - } - } -} +namespace Ocelot.UnitTests.Eureka +{ + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration.Builder; + using Provider.Eureka; + using Shouldly; + using Steeltoe.Common.Discovery; + using Xunit; + + public class EurekaProviderFactoryTests + { + [Fact] + public void should_not_get() + { + var config = new ServiceProviderConfigurationBuilder().Build(); + var sp = new ServiceCollection().BuildServiceProvider(); + var provider = EurekaProviderFactory.Get(sp, config, null); + provider.ShouldBeNull(); + } + + [Fact] + public void should_get() + { + var config = new ServiceProviderConfigurationBuilder().WithType("eureka").Build(); + var client = new Mock(); + var services = new ServiceCollection(); + services.AddSingleton(client.Object); + var sp = services.BuildServiceProvider(); + var route = new DownstreamRouteBuilder() + .WithServiceName("") + .Build(); + var provider = EurekaProviderFactory.Get(sp, config, route); + provider.ShouldBeOfType(); + } + } +} diff --git a/test/Ocelot.UnitTests/Headers/ClaimsToHeadersMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/ClaimsToHeadersMiddlewareTests.cs index f7998373a..faebf712b 100644 --- a/test/Ocelot.UnitTests/Headers/ClaimsToHeadersMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/ClaimsToHeadersMiddlewareTests.cs @@ -22,7 +22,7 @@ public class ClaimsToHeadersMiddlewareTests { private readonly Mock _addHeaders; - private Response _downstreamRoute; + private Response _downstreamRoute; private Mock _loggerFactory; private Mock _logger; private ClaimsToHeadersMiddleware _middleware; @@ -44,9 +44,9 @@ public ClaimsToHeadersMiddlewareTests() [Fact] public void should_call_add_headers_to_request_correctly() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() + var downstreamRoute = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithClaimsToHeaders(new List { @@ -69,13 +69,13 @@ private void WhenICallTheMiddleware() _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); } - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + private void GivenTheDownStreamRouteIs(Ocelot.DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) { - _downstreamRoute = new OkResponse(downstreamRoute); + _downstreamRoute = new OkResponse(downstreamRoute); _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); } private void GivenTheAddHeadersToDownstreamRequestReturnsOk() diff --git a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs index 41f33a305..65aaf0960 100644 --- a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs @@ -52,7 +52,7 @@ public void should_call_pre_and_post_header_transforms() { this.Given(x => GivenTheFollowingRequest()) .And(x => GivenTheDownstreamRequestIs()) - .And(x => GivenTheReRouteHasPreFindAndReplaceSetUp()) + .And(x => GivenTheRouteHasPreFindAndReplaceSetUp()) .And(x => GivenTheHttpResponseMessageIs()) .When(x => WhenICallTheMiddleware()) .Then(x => ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly()) @@ -65,13 +65,13 @@ public void should_call_pre_and_post_header_transforms() private void ThenAddHeadersToResponseIsCalledCorrectly() { _addHeadersToResponse - .Verify(x => x.Add(_httpContext.Items.DownstreamReRoute().AddHeadersToDownstream, _httpContext.Items.DownstreamResponse()), Times.Once); + .Verify(x => x.Add(_httpContext.Items.DownstreamRoute().AddHeadersToDownstream, _httpContext.Items.DownstreamResponse()), Times.Once); } private void ThenAddHeadersToRequestIsCalledCorrectly() { _addHeadersToRequest - .Verify(x => x.SetHeadersOnDownstreamRequest(_httpContext.Items.DownstreamReRoute().AddHeadersToUpstream, _httpContext), Times.Once); + .Verify(x => x.SetHeadersOnDownstreamRequest(_httpContext.Items.DownstreamRoute().AddHeadersToUpstream, _httpContext), Times.Once); } private void WhenICallTheMiddleware() @@ -89,18 +89,18 @@ private void GivenTheHttpResponseMessageIs() _httpContext.Items.UpsertDownstreamResponse(new DownstreamResponse(new HttpResponseMessage())); } - private void GivenTheReRouteHasPreFindAndReplaceSetUp() + private void GivenTheRouteHasPreFindAndReplaceSetUp() { var fAndRs = new List(); - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder().WithUpstreamHeaderFindAndReplace(fAndRs) + var route = new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder().WithUpstreamHeaderFindAndReplace(fAndRs) .WithDownstreamHeaderFindAndReplace(fAndRs).Build()) .Build(); - var dR = new DownstreamRoute(null, reRoute); + var dR = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(null, route); _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(dR.TemplatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(dR.ReRoute.DownstreamReRoute[0]); + _httpContext.Items.UpsertDownstreamRoute(dR.Route.DownstreamRoute[0]); } private void ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly() diff --git a/test/Ocelot.UnitTests/LoadBalancer/CookieStickySessionsCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/CookieStickySessionsCreatorTests.cs index ece2286ed..b64e00da0 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/CookieStickySessionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/CookieStickySessionsCreatorTests.cs @@ -1,74 +1,74 @@ -namespace Ocelot.UnitTests.LoadBalancer -{ - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.LoadBalancer.LoadBalancers; - using Ocelot.ServiceDiscovery.Providers; - using Ocelot.Responses; - using Shouldly; - using TestStack.BDDfy; - using Xunit; - - public class CookieStickySessionsCreatorTests - { - private readonly CookieStickySessionsCreator _creator; - private readonly Mock _serviceProvider; - private DownstreamReRoute _reRoute; - private Response _loadBalancer; - private string _typeName; - - public CookieStickySessionsCreatorTests() - { - _creator = new CookieStickySessionsCreator(); - _serviceProvider = new Mock(); - } - - [Fact] - public void should_return_instance_of_expected_load_balancer_type() - { - var reRoute = new DownstreamReRouteBuilder() - .WithLoadBalancerOptions(new LoadBalancerOptions("myType", "myKey", 1000)) - .Build(); - - this.Given(x => x.GivenAReRoute(reRoute)) - .When(x => x.WhenIGetTheLoadBalancer()) - .Then(x => x.ThenTheLoadBalancerIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_return_expected_name() - { - this.When(x => x.WhenIGetTheLoadBalancerTypeName()) - .Then(x => x.ThenTheLoadBalancerTypeIs("CookieStickySessions")) - .BDDfy(); - } - - private void GivenAReRoute(DownstreamReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenIGetTheLoadBalancer() - { - _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); - } - - private void WhenIGetTheLoadBalancerTypeName() - { - _typeName = _creator.Type; - } - - private void ThenTheLoadBalancerIsReturned() - where T : ILoadBalancer - { - _loadBalancer.Data.ShouldBeOfType(); - } - - private void ThenTheLoadBalancerTypeIs(string type) - { - _typeName.ShouldBe(type); - } - } -} +namespace Ocelot.UnitTests.LoadBalancer +{ + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.LoadBalancer.LoadBalancers; + using Ocelot.ServiceDiscovery.Providers; + using Ocelot.Responses; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class CookieStickySessionsCreatorTests + { + private readonly CookieStickySessionsCreator _creator; + private readonly Mock _serviceProvider; + private DownstreamRoute _route; + private Response _loadBalancer; + private string _typeName; + + public CookieStickySessionsCreatorTests() + { + _creator = new CookieStickySessionsCreator(); + _serviceProvider = new Mock(); + } + + [Fact] + public void should_return_instance_of_expected_load_balancer_type() + { + var route = new DownstreamRouteBuilder() + .WithLoadBalancerOptions(new LoadBalancerOptions("myType", "myKey", 1000)) + .Build(); + + this.Given(x => x.GivenARoute(route)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_expected_name() + { + this.When(x => x.WhenIGetTheLoadBalancerTypeName()) + .Then(x => x.ThenTheLoadBalancerTypeIs("CookieStickySessions")) + .BDDfy(); + } + + private void GivenARoute(DownstreamRoute route) + { + _route = route; + } + + private void WhenIGetTheLoadBalancer() + { + _loadBalancer = _creator.Create(_route, _serviceProvider.Object); + } + + private void WhenIGetTheLoadBalancerTypeName() + { + _typeName = _creator.Type; + } + + private void ThenTheLoadBalancerIsReturned() + where T : ILoadBalancer + { + _loadBalancer.Data.ShouldBeOfType(); + } + + private void ThenTheLoadBalancerTypeIs(string type) + { + _typeName.ShouldBe(type); + } + } +} diff --git a/test/Ocelot.UnitTests/LoadBalancer/DelegateInvokingLoadBalancerCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/DelegateInvokingLoadBalancerCreatorTests.cs index 919c37fda..d63cffd33 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/DelegateInvokingLoadBalancerCreatorTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/DelegateInvokingLoadBalancerCreatorTests.cs @@ -19,16 +19,16 @@ namespace Ocelot.UnitTests.LoadBalancer public class DelegateInvokingLoadBalancerCreatorTests { private DelegateInvokingLoadBalancerCreator _creator; - private Func _creatorFunc; + private Func _creatorFunc; private readonly Mock _serviceProvider; - private DownstreamReRoute _reRoute; + private DownstreamRoute _route; private Response _loadBalancer; private string _typeName; public DelegateInvokingLoadBalancerCreatorTests() { - _creatorFunc = (reRoute, serviceDiscoveryProvider) => - new FakeLoadBalancer(reRoute, serviceDiscoveryProvider); + _creatorFunc = (route, serviceDiscoveryProvider) => + new FakeLoadBalancer(route, serviceDiscoveryProvider); _creator = new DelegateInvokingLoadBalancerCreator(_creatorFunc); _serviceProvider = new Mock(); } @@ -44,10 +44,10 @@ public void should_return_expected_name() [Fact] public void should_return_result_of_specified_creator_func() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .Build(); - this.Given(x => x.GivenAReRoute(reRoute)) + this.Given(x => x.GivenARoute(route)) .When(x => x.WhenIGetTheLoadBalancer()) .Then(x => x.ThenTheLoadBalancerIsReturned()) .BDDfy(); @@ -56,10 +56,10 @@ public void should_return_result_of_specified_creator_func() [Fact] public void should_return_error() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .Build(); - this.Given(x => x.GivenAReRoute(reRoute)) + this.Given(x => x.GivenARoute(route)) .And(x => x.GivenTheCreatorFuncThrows()) .When(x => x.WhenIGetTheLoadBalancer()) .Then(x => x.ThenAnErrorIsReturned()) @@ -68,7 +68,7 @@ public void should_return_error() private void GivenTheCreatorFuncThrows() { - _creatorFunc = (reRoute, serviceDiscoveryProvider) => throw new Exception(); + _creatorFunc = (route, serviceDiscoveryProvider) => throw new Exception(); _creator = new DelegateInvokingLoadBalancerCreator(_creatorFunc); } @@ -78,14 +78,14 @@ private void ThenAnErrorIsReturned() _loadBalancer.IsError.ShouldBeTrue(); } - private void GivenAReRoute(DownstreamReRoute reRoute) + private void GivenARoute(DownstreamRoute route) { - _reRoute = reRoute; + _route = route; } private void WhenIGetTheLoadBalancer() { - _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); + _loadBalancer = _creator.Create(_route, _serviceProvider.Object); } private void WhenIGetTheLoadBalancerTypeName() @@ -106,13 +106,13 @@ private void ThenTheLoadBalancerTypeIs(string type) private class FakeLoadBalancer : ILoadBalancer { - public FakeLoadBalancer(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceDiscoveryProvider) + public FakeLoadBalancer(DownstreamRoute downstreamRoute, IServiceDiscoveryProvider serviceDiscoveryProvider) { - ReRoute = reRoute; + DownstreamRoute = downstreamRoute; ServiceDiscoveryProvider = serviceDiscoveryProvider; } - public DownstreamReRoute ReRoute { get; } + public DownstreamRoute DownstreamRoute { get; } public IServiceDiscoveryProvider ServiceDiscoveryProvider { get; } public Task> Lease(HttpContext httpContext) diff --git a/test/Ocelot.UnitTests/LoadBalancer/LeastConnectionCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LeastConnectionCreatorTests.cs index 326ecbd7a..484bb6f02 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LeastConnectionCreatorTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LeastConnectionCreatorTests.cs @@ -1,74 +1,74 @@ -namespace Ocelot.UnitTests.LoadBalancer -{ - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.LoadBalancer.LoadBalancers; - using Ocelot.Responses; - using Ocelot.ServiceDiscovery.Providers; - using Shouldly; - using TestStack.BDDfy; - using Xunit; - - public class LeastConnectionCreatorTests - { - private readonly LeastConnectionCreator _creator; - private readonly Mock _serviceProvider; - private DownstreamReRoute _reRoute; - private Response _loadBalancer; - private string _typeName; - - public LeastConnectionCreatorTests() - { - _creator = new LeastConnectionCreator(); - _serviceProvider = new Mock(); - } - - [Fact] - public void should_return_instance_of_expected_load_balancer_type() - { - var reRoute = new DownstreamReRouteBuilder() - .WithServiceName("myService") - .Build(); - - this.Given(x => x.GivenAReRoute(reRoute)) - .When(x => x.WhenIGetTheLoadBalancer()) - .Then(x => x.ThenTheLoadBalancerIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_return_expected_name() - { - this.When(x => x.WhenIGetTheLoadBalancerTypeName()) - .Then(x => x.ThenTheLoadBalancerTypeIs("LeastConnection")) - .BDDfy(); - } - - private void GivenAReRoute(DownstreamReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenIGetTheLoadBalancer() - { - _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); - } - - private void WhenIGetTheLoadBalancerTypeName() - { - _typeName = _creator.Type; - } - - private void ThenTheLoadBalancerIsReturned() - where T : ILoadBalancer - { - _loadBalancer.Data.ShouldBeOfType(); - } - - private void ThenTheLoadBalancerTypeIs(string type) - { - _typeName.ShouldBe(type); - } - } -} +namespace Ocelot.UnitTests.LoadBalancer +{ + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.LoadBalancer.LoadBalancers; + using Ocelot.Responses; + using Ocelot.ServiceDiscovery.Providers; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class LeastConnectionCreatorTests + { + private readonly LeastConnectionCreator _creator; + private readonly Mock _serviceProvider; + private DownstreamRoute _route; + private Response _loadBalancer; + private string _typeName; + + public LeastConnectionCreatorTests() + { + _creator = new LeastConnectionCreator(); + _serviceProvider = new Mock(); + } + + [Fact] + public void should_return_instance_of_expected_load_balancer_type() + { + var route = new DownstreamRouteBuilder() + .WithServiceName("myService") + .Build(); + + this.Given(x => x.GivenARoute(route)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_expected_name() + { + this.When(x => x.WhenIGetTheLoadBalancerTypeName()) + .Then(x => x.ThenTheLoadBalancerTypeIs("LeastConnection")) + .BDDfy(); + } + + private void GivenARoute(DownstreamRoute route) + { + _route = route; + } + + private void WhenIGetTheLoadBalancer() + { + _loadBalancer = _creator.Create(_route, _serviceProvider.Object); + } + + private void WhenIGetTheLoadBalancerTypeName() + { + _typeName = _creator.Type; + } + + private void ThenTheLoadBalancerIsReturned() + where T : ILoadBalancer + { + _loadBalancer.Data.ShouldBeOfType(); + } + + private void ThenTheLoadBalancerTypeIs(string type) + { + _typeName.ShouldBe(type); + } + } +} diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs index da8b9f09e..4b20e1bcc 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerFactoryTests.cs @@ -21,7 +21,7 @@ namespace Ocelot.UnitTests.LoadBalancer public class LoadBalancerFactoryTests { - private DownstreamReRoute _reRoute; + private DownstreamRoute _route; private readonly LoadBalancerFactory _factory; private Response _result; private readonly Mock _serviceProviderFactory; @@ -46,11 +46,11 @@ public LoadBalancerFactoryTests() [Fact] public void should_return_no_load_balancer_by_default() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - this.Given(x => x.GivenAReRoute(reRoute)) + this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) @@ -61,12 +61,12 @@ public void should_return_no_load_balancer_by_default() [Fact] public void should_return_matching_load_balancer() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancerTwo", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - this.Given(x => x.GivenAReRoute(reRoute)) + this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) @@ -77,12 +77,12 @@ public void should_return_matching_load_balancer() [Fact] public void should_return_error_response_if_cannot_find_load_balancer_creator() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("DoesntExistLoadBalancer", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - this.Given(x => x.GivenAReRoute(reRoute)) + this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) @@ -94,12 +94,12 @@ public void should_return_error_response_if_cannot_find_load_balancer_creator() [Fact] public void should_return_error_response_if_creator_errors() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("BrokenLoadBalancer", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - this.Given(x => x.GivenAReRoute(reRoute)) + this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) @@ -110,12 +110,12 @@ public void should_return_error_response_if_creator_errors() [Fact] public void should_call_service_provider() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancerOne", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - this.Given(x => x.GivenAReRoute(reRoute)) + this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryReturns()) .When(x => x.WhenIGetTheLoadBalancer()) @@ -126,12 +126,12 @@ public void should_call_service_provider() [Fact] public void should_return_error_response_when_call_to_service_provider_fails() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancerOne", "", 0)) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - this.Given(x => x.GivenAReRoute(reRoute)) + this.Given(x => x.GivenARoute(route)) .And(x => GivenAServiceProviderConfig(new ServiceProviderConfigurationBuilder().Build())) .And(x => x.GivenTheServiceProviderFactoryFails()) .When(x => x.WhenIGetTheLoadBalancer()) @@ -147,31 +147,31 @@ private void GivenAServiceProviderConfig(ServiceProviderConfiguration servicePro private void GivenTheServiceProviderFactoryReturns() { _serviceProviderFactory - .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(new OkResponse(_serviceProvider.Object)); } private void GivenTheServiceProviderFactoryFails() { _serviceProviderFactory - .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(new ErrorResponse(new CannotFindDataError("For tests"))); } private void ThenTheServiceProviderIsCalledCorrectly() { _serviceProviderFactory - .Verify(x => x.Get(It.IsAny(), It.IsAny()), Times.Once); + .Verify(x => x.Get(It.IsAny(), It.IsAny()), Times.Once); } - private void GivenAReRoute(DownstreamReRoute reRoute) + private void GivenARoute(DownstreamRoute route) { - _reRoute = reRoute; + _route = route; } private void WhenIGetTheLoadBalancer() { - _result = _factory.Get(_reRoute, _serviceProviderConfig); + _result = _factory.Get(_route, _serviceProviderConfig); } private void ThenTheLoadBalancerIsReturned() @@ -203,7 +203,7 @@ public FakeLoadBalancerCreator(string type) Type = type; } - public Response Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) + public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) { return new OkResponse(new T()); } @@ -219,7 +219,7 @@ public BrokenLoadBalancerCreator() Type = typeof(T).Name; } - public Response Create(DownstreamReRoute reRoute, IServiceDiscoveryProvider serviceProvider) + public Response Create(DownstreamRoute route, IServiceDiscoveryProvider serviceProvider) { return new ErrorResponse(new ErrorInvokingLoadBalancerCreator(new Exception())); } diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs index 6828aa813..632fda82f 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerHouseTests.cs @@ -17,7 +17,7 @@ namespace Ocelot.UnitTests.LoadBalancer public class LoadBalancerHouseTests { - private DownstreamReRoute _reRoute; + private DownstreamRoute _route; private ILoadBalancer _loadBalancer; private readonly LoadBalancerHouse _loadBalancerHouse; private Response _getResult; @@ -34,11 +34,11 @@ public LoadBalancerHouseTests() [Fact] public void should_store_load_balancer_on_first_request() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerKey("test") .Build(); - this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) + this.Given(x => x.GivenThereIsALoadBalancer(route, new FakeLoadBalancer())) .Then(x => x.ThenItIsAdded()) .BDDfy(); } @@ -46,13 +46,13 @@ public void should_store_load_balancer_on_first_request() [Fact] public void should_not_store_load_balancer_on_second_request() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) .WithLoadBalancerKey("test") .Build(); - this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) - .When(x => x.WhenWeGetTheLoadBalancer(reRoute)) + this.Given(x => x.GivenThereIsALoadBalancer(route, new FakeLoadBalancer())) + .When(x => x.WhenWeGetTheLoadBalancer(route)) .Then(x => x.ThenItIsReturned()) .BDDfy(); } @@ -60,21 +60,21 @@ public void should_not_store_load_balancer_on_second_request() [Fact] public void should_store_load_balancers_by_key() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) .WithLoadBalancerKey("test") .Build(); - var reRouteTwo = new DownstreamReRouteBuilder() + var routeTwo = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeRoundRobinLoadBalancer", "", 0)) .WithLoadBalancerKey("testtwo") .Build(); - this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) - .And(x => x.GivenThereIsALoadBalancer(reRouteTwo, new FakeRoundRobinLoadBalancer())) - .When(x => x.WhenWeGetTheLoadBalancer(reRoute)) + this.Given(x => x.GivenThereIsALoadBalancer(route, new FakeLoadBalancer())) + .And(x => x.GivenThereIsALoadBalancer(routeTwo, new FakeRoundRobinLoadBalancer())) + .When(x => x.WhenWeGetTheLoadBalancer(route)) .Then(x => x.ThenTheLoadBalancerIs()) - .When(x => x.WhenWeGetTheLoadBalancer(reRouteTwo)) + .When(x => x.WhenWeGetTheLoadBalancer(routeTwo)) .Then(x => x.ThenTheLoadBalancerIs()) .BDDfy(); } @@ -82,39 +82,39 @@ public void should_store_load_balancers_by_key() [Fact] public void should_return_error_if_exception() { - var reRoute = new DownstreamReRouteBuilder().Build(); + var route = new DownstreamRouteBuilder().Build(); - this.When(x => x.WhenWeGetTheLoadBalancer(reRoute)) + this.When(x => x.WhenWeGetTheLoadBalancer(route)) .Then(x => x.ThenAnErrorIsReturned()) .BDDfy(); } [Fact] - public void should_get_new_load_balancer_if_reroute_load_balancer_has_changed() + public void should_get_new_load_balancer_if_route_load_balancer_has_changed() { - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("FakeLoadBalancer", "", 0)) .WithLoadBalancerKey("test") .Build(); - var reRouteTwo = new DownstreamReRouteBuilder() + var routeTwo = new DownstreamRouteBuilder() .WithLoadBalancerOptions(new LoadBalancerOptions("LeastConnection", "", 0)) .WithLoadBalancerKey("test") .Build(); - this.Given(x => x.GivenThereIsALoadBalancer(reRoute, new FakeLoadBalancer())) - .When(x => x.WhenWeGetTheLoadBalancer(reRoute)) + this.Given(x => x.GivenThereIsALoadBalancer(route, new FakeLoadBalancer())) + .When(x => x.WhenWeGetTheLoadBalancer(route)) .Then(x => x.ThenTheLoadBalancerIs()) - .When(x => x.WhenIGetTheReRouteWithTheSameKeyButDifferentLoadBalancer(reRouteTwo)) + .When(x => x.WhenIGetTheRouteWithTheSameKeyButDifferentLoadBalancer(routeTwo)) .Then(x => x.ThenTheLoadBalancerIs()) .BDDfy(); } - private void WhenIGetTheReRouteWithTheSameKeyButDifferentLoadBalancer(DownstreamReRoute reRoute) + private void WhenIGetTheRouteWithTheSameKeyButDifferentLoadBalancer(DownstreamRoute route) { - _reRoute = reRoute; - _factory.Setup(x => x.Get(_reRoute, _serviceProviderConfig)).Returns(new OkResponse(new LeastConnection(null, null))); - _getResult = _loadBalancerHouse.Get(_reRoute, _serviceProviderConfig); + _route = route; + _factory.Setup(x => x.Get(_route, _serviceProviderConfig)).Returns(new OkResponse(new LeastConnection(null, null))); + _getResult = _loadBalancerHouse.Get(_route, _serviceProviderConfig); } private void ThenAnErrorIsReturned() @@ -133,26 +133,26 @@ private void ThenItIsAdded() _getResult.IsError.ShouldBe(false); _getResult.ShouldBeOfType>(); _getResult.Data.ShouldBe(_loadBalancer); - _factory.Verify(x => x.Get(_reRoute, _serviceProviderConfig), Times.Once); + _factory.Verify(x => x.Get(_route, _serviceProviderConfig), Times.Once); } - private void GivenThereIsALoadBalancer(DownstreamReRoute reRoute, ILoadBalancer loadBalancer) + private void GivenThereIsALoadBalancer(DownstreamRoute route, ILoadBalancer loadBalancer) { - _reRoute = reRoute; + _route = route; _loadBalancer = loadBalancer; - _factory.Setup(x => x.Get(_reRoute, _serviceProviderConfig)).Returns(new OkResponse(loadBalancer)); - _getResult = _loadBalancerHouse.Get(reRoute, _serviceProviderConfig); + _factory.Setup(x => x.Get(_route, _serviceProviderConfig)).Returns(new OkResponse(loadBalancer)); + _getResult = _loadBalancerHouse.Get(route, _serviceProviderConfig); } - private void WhenWeGetTheLoadBalancer(DownstreamReRoute reRoute) + private void WhenWeGetTheLoadBalancer(DownstreamRoute route) { - _getResult = _loadBalancerHouse.Get(reRoute, _serviceProviderConfig); + _getResult = _loadBalancerHouse.Get(route, _serviceProviderConfig); } private void ThenItIsReturned() { _getResult.Data.ShouldBe(_loadBalancer); - _factory.Verify(x => x.Get(_reRoute, _serviceProviderConfig), Times.Once); + _factory.Verify(x => x.Get(_route, _serviceProviderConfig), Times.Once); } private class FakeLoadBalancer : ILoadBalancer diff --git a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs index b06c09ddb..47ab0c506 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/LoadBalancerMiddlewareTests.cs @@ -56,7 +56,7 @@ public LoadBalancerMiddlewareTests() [Fact] public void should_call_scoped_data_repository_correctly() { - var downstreamRoute = new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build(); @@ -76,7 +76,7 @@ public void should_call_scoped_data_repository_correctly() [Fact] public void should_set_pipeline_error_if_cannot_get_load_balancer() { - var downstreamRoute = new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build(); @@ -95,7 +95,7 @@ public void should_set_pipeline_error_if_cannot_get_load_balancer() [Fact] public void should_set_pipeline_error_if_cannot_get_least() { - var downstreamRoute = new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build(); @@ -115,7 +115,7 @@ public void should_set_pipeline_error_if_cannot_get_least() [Fact] public void should_set_scheme() { - var downstreamRoute = new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteBuilder() .WithUpstreamHttpMethod(new List { "Get" }) .Build(); @@ -174,16 +174,16 @@ private void GivenTheLoadBalancerReturns() .ReturnsAsync(new OkResponse(_hostAndPort)); } - private void GivenTheDownStreamRouteIs(DownstreamReRoute downstreamRoute, List placeholder) + private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute, List placeholder) { _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(placeholder); - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute); + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute); } private void GivenTheLoadBalancerHouseReturns() { _loadBalancerHouse - .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(new OkResponse(_loadBalancer.Object)); } @@ -195,7 +195,7 @@ private void GivenTheLoadBalancerHouseReturnsAnError() }); _loadBalancerHouse - .Setup(x => x.Get(It.IsAny(), It.IsAny())) + .Setup(x => x.Get(It.IsAny(), It.IsAny())) .Returns(_getLoadBalancerHouseError); } diff --git a/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerCreatorTests.cs index 46f4208b1..772e36f91 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerCreatorTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerCreatorTests.cs @@ -1,73 +1,73 @@ -namespace Ocelot.UnitTests.LoadBalancer -{ - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.LoadBalancer.LoadBalancers; - using Ocelot.Responses; - using Ocelot.ServiceDiscovery.Providers; - using Shouldly; - using TestStack.BDDfy; - using Xunit; - - public class NoLoadBalancerCreatorTests - { - private readonly NoLoadBalancerCreator _creator; - private readonly Mock _serviceProvider; - private DownstreamReRoute _reRoute; - private Response _loadBalancer; - private string _typeName; - - public NoLoadBalancerCreatorTests() - { - _creator = new NoLoadBalancerCreator(); - _serviceProvider = new Mock(); - } - - [Fact] - public void should_return_instance_of_expected_load_balancer_type() - { - var reRoute = new DownstreamReRouteBuilder() - .Build(); - - this.Given(x => x.GivenAReRoute(reRoute)) - .When(x => x.WhenIGetTheLoadBalancer()) - .Then(x => x.ThenTheLoadBalancerIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_return_expected_name() - { - this.When(x => x.WhenIGetTheLoadBalancerTypeName()) - .Then(x => x.ThenTheLoadBalancerTypeIs("NoLoadBalancer")) - .BDDfy(); - } - - private void GivenAReRoute(DownstreamReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenIGetTheLoadBalancer() - { - _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); - } - - private void WhenIGetTheLoadBalancerTypeName() - { - _typeName = _creator.Type; - } - - private void ThenTheLoadBalancerIsReturned() - where T : ILoadBalancer - { - _loadBalancer.Data.ShouldBeOfType(); - } - - private void ThenTheLoadBalancerTypeIs(string type) - { - _typeName.ShouldBe(type); - } - } -} +namespace Ocelot.UnitTests.LoadBalancer +{ + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.LoadBalancer.LoadBalancers; + using Ocelot.Responses; + using Ocelot.ServiceDiscovery.Providers; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class NoLoadBalancerCreatorTests + { + private readonly NoLoadBalancerCreator _creator; + private readonly Mock _serviceProvider; + private DownstreamRoute _route; + private Response _loadBalancer; + private string _typeName; + + public NoLoadBalancerCreatorTests() + { + _creator = new NoLoadBalancerCreator(); + _serviceProvider = new Mock(); + } + + [Fact] + public void should_return_instance_of_expected_load_balancer_type() + { + var route = new DownstreamRouteBuilder() + .Build(); + + this.Given(x => x.GivenARoute(route)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_expected_name() + { + this.When(x => x.WhenIGetTheLoadBalancerTypeName()) + .Then(x => x.ThenTheLoadBalancerTypeIs("NoLoadBalancer")) + .BDDfy(); + } + + private void GivenARoute(DownstreamRoute route) + { + _route = route; + } + + private void WhenIGetTheLoadBalancer() + { + _loadBalancer = _creator.Create(_route, _serviceProvider.Object); + } + + private void WhenIGetTheLoadBalancerTypeName() + { + _typeName = _creator.Type; + } + + private void ThenTheLoadBalancerIsReturned() + where T : ILoadBalancer + { + _loadBalancer.Data.ShouldBeOfType(); + } + + private void ThenTheLoadBalancerTypeIs(string type) + { + _typeName.ShouldBe(type); + } + } +} diff --git a/test/Ocelot.UnitTests/LoadBalancer/RoundRobinCreatorTests.cs b/test/Ocelot.UnitTests/LoadBalancer/RoundRobinCreatorTests.cs index b8c436a2a..0b4d62e30 100644 --- a/test/Ocelot.UnitTests/LoadBalancer/RoundRobinCreatorTests.cs +++ b/test/Ocelot.UnitTests/LoadBalancer/RoundRobinCreatorTests.cs @@ -1,73 +1,73 @@ -namespace Ocelot.UnitTests.LoadBalancer -{ - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.LoadBalancer.LoadBalancers; - using Ocelot.Responses; - using Ocelot.ServiceDiscovery.Providers; - using Shouldly; - using TestStack.BDDfy; - using Xunit; - - public class RoundRobinCreatorTests - { - private readonly RoundRobinCreator _creator; - private readonly Mock _serviceProvider; - private DownstreamReRoute _reRoute; - private Response _loadBalancer; - private string _typeName; - - public RoundRobinCreatorTests() - { - _creator = new RoundRobinCreator(); - _serviceProvider = new Mock(); - } - - [Fact] - public void should_return_instance_of_expected_load_balancer_type() - { - var reRoute = new DownstreamReRouteBuilder() - .Build(); - - this.Given(x => x.GivenAReRoute(reRoute)) - .When(x => x.WhenIGetTheLoadBalancer()) - .Then(x => x.ThenTheLoadBalancerIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_return_expected_name() - { - this.When(x => x.WhenIGetTheLoadBalancerTypeName()) - .Then(x => x.ThenTheLoadBalancerTypeIs("RoundRobin")) - .BDDfy(); - } - - private void GivenAReRoute(DownstreamReRoute reRoute) - { - _reRoute = reRoute; - } - - private void WhenIGetTheLoadBalancer() - { - _loadBalancer = _creator.Create(_reRoute, _serviceProvider.Object); - } - - private void WhenIGetTheLoadBalancerTypeName() - { - _typeName = _creator.Type; - } - - private void ThenTheLoadBalancerIsReturned() - where T : ILoadBalancer - { - _loadBalancer.Data.ShouldBeOfType(); - } - - private void ThenTheLoadBalancerTypeIs(string type) - { - _typeName.ShouldBe(type); - } - } -} +namespace Ocelot.UnitTests.LoadBalancer +{ + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.LoadBalancer.LoadBalancers; + using Ocelot.Responses; + using Ocelot.ServiceDiscovery.Providers; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + + public class RoundRobinCreatorTests + { + private readonly RoundRobinCreator _creator; + private readonly Mock _serviceProvider; + private DownstreamRoute _route; + private Response _loadBalancer; + private string _typeName; + + public RoundRobinCreatorTests() + { + _creator = new RoundRobinCreator(); + _serviceProvider = new Mock(); + } + + [Fact] + public void should_return_instance_of_expected_load_balancer_type() + { + var route = new DownstreamRouteBuilder() + .Build(); + + this.Given(x => x.GivenARoute(route)) + .When(x => x.WhenIGetTheLoadBalancer()) + .Then(x => x.ThenTheLoadBalancerIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_return_expected_name() + { + this.When(x => x.WhenIGetTheLoadBalancerTypeName()) + .Then(x => x.ThenTheLoadBalancerTypeIs("RoundRobin")) + .BDDfy(); + } + + private void GivenARoute(DownstreamRoute route) + { + _route = route; + } + + private void WhenIGetTheLoadBalancer() + { + _loadBalancer = _creator.Create(_route, _serviceProvider.Object); + } + + private void WhenIGetTheLoadBalancerTypeName() + { + _typeName = _creator.Type; + } + + private void ThenTheLoadBalancerIsReturned() + where T : ILoadBalancer + { + _loadBalancer.Data.ShouldBeOfType(); + } + + private void ThenTheLoadBalancerTypeIs(string type) + { + _typeName.ShouldBe(type); + } + } +} diff --git a/test/Ocelot.UnitTests/Multiplexing/DefinedAggregatorProviderTests.cs b/test/Ocelot.UnitTests/Multiplexing/DefinedAggregatorProviderTests.cs index 040dff2a5..8cd0bb88f 100644 --- a/test/Ocelot.UnitTests/Multiplexing/DefinedAggregatorProviderTests.cs +++ b/test/Ocelot.UnitTests/Multiplexing/DefinedAggregatorProviderTests.cs @@ -6,25 +6,25 @@ using Shouldly; using TestStack.BDDfy; using Xunit; -using static Ocelot.UnitTests.Multiplexing.UserDefinedResponseAggregatorTests; - +using static Ocelot.UnitTests.Multiplexing.UserDefinedResponseAggregatorTests; + namespace Ocelot.UnitTests.Multiplexing { public class DefinedAggregatorProviderTests { private ServiceLocatorDefinedAggregatorProvider _provider; private Response _aggregator; - private ReRoute _reRoute; + private Route _route; [Fact] public void should_find_aggregator() { - var reRoute = new ReRouteBuilder() + var route = new RouteBuilder() .WithAggregator("TestDefinedAggregator") .Build(); this.Given(_ => GivenDefinedAggregator()) - .And(_ => GivenReRoute(reRoute)) + .And(_ => GivenRoute(route)) .When(_ => WhenIGet()) .Then(_ => ThenTheAggregatorIsReturned()) .BDDfy(); @@ -33,12 +33,12 @@ public void should_find_aggregator() [Fact] public void should_not_find_aggregator() { - var reRoute = new ReRouteBuilder() + var route = new RouteBuilder() .WithAggregator("TestDefinedAggregator") .Build(); this.Given(_ => GivenNoDefinedAggregator()) - .And(_ => GivenReRoute(reRoute)) + .And(_ => GivenRoute(route)) .When(_ => WhenIGet()) .Then(_ => ThenAnErrorIsReturned()) .BDDfy(); @@ -66,14 +66,14 @@ private void GivenNoDefinedAggregator() _provider = new ServiceLocatorDefinedAggregatorProvider(services); } - private void GivenReRoute(ReRoute reRoute) + private void GivenRoute(Route route) { - _reRoute = reRoute; + _route = route; } private void WhenIGet() { - _aggregator = _provider.Get(_reRoute); + _aggregator = _provider.Get(_route); } private void ThenAnErrorIsReturned() diff --git a/test/Ocelot.UnitTests/Multiplexing/MultiplexingMiddlewareTests.cs b/test/Ocelot.UnitTests/Multiplexing/MultiplexingMiddlewareTests.cs index e633d2448..3f24ade0f 100644 --- a/test/Ocelot.UnitTests/Multiplexing/MultiplexingMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Multiplexing/MultiplexingMiddlewareTests.cs @@ -1,4 +1,4 @@ -namespace Ocelot.UnitTests.Multiplexing +namespace Ocelot.UnitTests.Multiplexing { using Microsoft.AspNetCore.Http; using Moq; @@ -14,68 +14,68 @@ using System.Threading.Tasks; using TestStack.BDDfy; using Xunit; - - public class MultiplexingMiddlewareTests - { - private readonly MultiplexingMiddleware _middleware; - private DownstreamRoute _downstreamRoute; - private int _count; - private Mock _aggregator; - private Mock _factory; + + public class MultiplexingMiddlewareTests + { + private readonly MultiplexingMiddleware _middleware; + private Ocelot.DownstreamRouteFinder.DownstreamRouteHolder _downstreamRoute; + private int _count; + private Mock _aggregator; + private Mock _factory; private HttpContext _httpContext; private RequestDelegate _next; private Mock _loggerFactory; private Mock _logger; - - public MultiplexingMiddlewareTests() - { - _httpContext = new DefaultHttpContext(); - _factory = new Mock(); - _aggregator = new Mock(); - _factory.Setup(x => x.Get(It.IsAny())).Returns(_aggregator.Object); + + public MultiplexingMiddlewareTests() + { + _httpContext = new DefaultHttpContext(); + _factory = new Mock(); + _aggregator = new Mock(); + _factory.Setup(x => x.Get(It.IsAny())).Returns(_aggregator.Object); _loggerFactory = new Mock(); _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); _next = context => Task.FromResult(_count++); - _middleware = new MultiplexingMiddleware(_next, _loggerFactory.Object, _factory.Object); - } - - [Fact] - public void should_multiplex() - { - var reRoute = new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().Build()).WithDownstreamReRoute(new DownstreamReRouteBuilder().Build()).Build(); - - this.Given(x => GivenTheFollowing(reRoute)) - .When(x => WhenIMultiplex()) - .Then(x => ThePipelineIsCalled(2)) - .BDDfy(); - } - - [Fact] - public void should_not_multiplex() - { - var reRoute = new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().Build()).Build(); - - this.Given(x => GivenTheFollowing(reRoute)) - .When(x => WhenIMultiplex()) - .Then(x => ThePipelineIsCalled(1)) - .BDDfy(); - } - - private void GivenTheFollowing(ReRoute reRoute) - { - _downstreamRoute = new DownstreamRoute(new List(), reRoute); - _httpContext.Items.UpsertDownstreamRoute(_downstreamRoute); - } - - private void WhenIMultiplex() - { - _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); - } - - private void ThePipelineIsCalled(int expected) - { - _count.ShouldBe(expected); - } - } -} + _middleware = new MultiplexingMiddleware(_next, _loggerFactory.Object, _factory.Object); + } + + [Fact] + public void should_multiplex() + { + var route = new RouteBuilder().WithDownstreamRoute(new DownstreamRouteBuilder().Build()).WithDownstreamRoute(new DownstreamRouteBuilder().Build()).Build(); + + this.Given(x => GivenTheFollowing(route)) + .When(x => WhenIMultiplex()) + .Then(x => ThePipelineIsCalled(2)) + .BDDfy(); + } + + [Fact] + public void should_not_multiplex() + { + var route = new RouteBuilder().WithDownstreamRoute(new DownstreamRouteBuilder().Build()).Build(); + + this.Given(x => GivenTheFollowing(route)) + .When(x => WhenIMultiplex()) + .Then(x => ThePipelineIsCalled(1)) + .BDDfy(); + } + + private void GivenTheFollowing(Route route) + { + _downstreamRoute = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(new List(), route); + _httpContext.Items.UpsertDownstreamRoute(_downstreamRoute); + } + + private void WhenIMultiplex() + { + _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); + } + + private void ThePipelineIsCalled(int expected) + { + _count.ShouldBe(expected); + } + } +} diff --git a/test/Ocelot.UnitTests/Multiplexing/ResponseAggregatorFactoryTests.cs b/test/Ocelot.UnitTests/Multiplexing/ResponseAggregatorFactoryTests.cs index 86bb570e3..fed2065a0 100644 --- a/test/Ocelot.UnitTests/Multiplexing/ResponseAggregatorFactoryTests.cs +++ b/test/Ocelot.UnitTests/Multiplexing/ResponseAggregatorFactoryTests.cs @@ -12,7 +12,7 @@ public class ResponseAggregatorFactoryTests { private readonly InMemoryResponseAggregatorFactory _factory; private Mock _provider; - private ReRoute _reRoute; + private Route _route; private IResponseAggregator _aggregator; public ResponseAggregatorFactoryTests() @@ -25,10 +25,10 @@ public ResponseAggregatorFactoryTests() [Fact] public void should_return_simple_json_aggregator() { - var reRoute = new ReRouteBuilder() + var route = new RouteBuilder() .Build(); - this.Given(_ => GivenReRoute(reRoute)) + this.Given(_ => GivenRoute(route)) .When(_ => WhenIGet()) .Then(_ => ThenTheAggregatorIs()) .BDDfy(); @@ -37,24 +37,24 @@ public void should_return_simple_json_aggregator() [Fact] public void should_return_user_defined_aggregator() { - var reRoute = new ReRouteBuilder() + var route = new RouteBuilder() .WithAggregator("doesntmatter") .Build(); - this.Given(_ => GivenReRoute(reRoute)) + this.Given(_ => GivenRoute(route)) .When(_ => WhenIGet()) .Then(_ => ThenTheAggregatorIs()) .BDDfy(); } - private void GivenReRoute(ReRoute reRoute) + private void GivenRoute(Route route) { - _reRoute = reRoute; + _route = route; } private void WhenIGet() { - _aggregator = _factory.Get(_reRoute); + _aggregator = _factory.Get(_route); } private void ThenTheAggregatorIs() diff --git a/test/Ocelot.UnitTests/Multiplexing/SimpleJsonResponseAggregatorTests.cs b/test/Ocelot.UnitTests/Multiplexing/SimpleJsonResponseAggregatorTests.cs index 5950d084e..6c92eb985 100644 --- a/test/Ocelot.UnitTests/Multiplexing/SimpleJsonResponseAggregatorTests.cs +++ b/test/Ocelot.UnitTests/Multiplexing/SimpleJsonResponseAggregatorTests.cs @@ -22,7 +22,7 @@ public class SimpleJsonResponseAggregatorTests private readonly SimpleJsonResponseAggregator _aggregator; private List _downstreamContexts; private HttpContext _upstreamContext; - private ReRoute _reRoute; + private Route _route; public SimpleJsonResponseAggregatorTests() { @@ -32,23 +32,23 @@ public SimpleJsonResponseAggregatorTests() [Fact] public void should_aggregate_n_responses_and_set_response_content_on_upstream_context_withConfig() { - var commentsDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("Comments").Build(); + var commentsDownstreamRoute = new DownstreamRouteBuilder().WithKey("Comments").Build(); - var userDetailsDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("UserDetails") + var userDetailsDownstreamRoute = new DownstreamRouteBuilder().WithKey("UserDetails") .WithUpstreamPathTemplate(new UpstreamPathTemplate("", 0, false, "/v1/users/{userId}")) .Build(); - var downstreamReRoutes = new List + var downstreamRoutes = new List { - commentsDownstreamReRoute, - userDetailsDownstreamReRoute + commentsDownstreamRoute, + userDetailsDownstreamRoute }; - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoutes(downstreamReRoutes) - .WithAggregateReRouteConfig(new List() + var route = new RouteBuilder() + .WithDownstreamRoutes(downstreamRoutes) + .WithAggregateRouteConfig(new List() { - new AggregateReRouteConfig(){ReRouteKey = "UserDetails",JsonPath = "$[*].writerId",Parameter = "userId"} + new AggregateRouteConfig(){RouteKey = "UserDetails",JsonPath = "$[*].writerId",Parameter = "userId"} }) .Build(); @@ -56,19 +56,19 @@ public void should_aggregate_n_responses_and_set_response_content_on_upstream_co var commentsDownstreamContext = new DefaultHttpContext(); commentsDownstreamContext.Items.UpsertDownstreamResponse(new DownstreamResponse(new StringContent(commentsResponseContent, Encoding.UTF8, "application/json"), HttpStatusCode.OK, new EditableList>>(), "some reason")); - commentsDownstreamContext.Items.UpsertDownstreamReRoute(commentsDownstreamReRoute); + commentsDownstreamContext.Items.UpsertDownstreamRoute(commentsDownstreamRoute); var userDetailsResponseContent = @"[{""id"":1,""firstName"":""abolfazl"",""lastName"":""rajabpour""},{""id"":2,""firstName"":""reza"",""lastName"":""rezaei""}]"; var userDetailsDownstreamContext = new DefaultHttpContext(); userDetailsDownstreamContext.Items.UpsertDownstreamResponse(new DownstreamResponse(new StringContent(userDetailsResponseContent, Encoding.UTF8, "application/json"), HttpStatusCode.OK, new List>>(), "some reason")); - userDetailsDownstreamContext.Items.UpsertDownstreamReRoute(userDetailsDownstreamReRoute); + userDetailsDownstreamContext.Items.UpsertDownstreamRoute(userDetailsDownstreamRoute); var downstreamContexts = new List { commentsDownstreamContext, userDetailsDownstreamContext }; var expected = "{\"Comments\":" + commentsResponseContent + ",\"UserDetails\":" + userDetailsResponseContent + "}"; this.Given(x => GivenTheUpstreamContext(new DefaultHttpContext())) - .And(x => GivenTheReRoute(reRoute)) + .And(x => GivenTheRoute(route)) .And(x => GivenTheDownstreamContext(downstreamContexts)) .When(x => WhenIAggregate()) .Then(x => ThenTheContentIs(expected)) @@ -80,34 +80,34 @@ public void should_aggregate_n_responses_and_set_response_content_on_upstream_co [Fact] public void should_aggregate_n_responses_and_set_response_content_on_upstream_context() { - var billDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("Bill").Build(); + var billDownstreamRoute = new DownstreamRouteBuilder().WithKey("Bill").Build(); - var georgeDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("George").Build(); + var georgeDownstreamRoute = new DownstreamRouteBuilder().WithKey("George").Build(); - var downstreamReRoutes = new List + var downstreamRoutes = new List { - billDownstreamReRoute, - georgeDownstreamReRoute + billDownstreamRoute, + georgeDownstreamRoute }; - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoutes(downstreamReRoutes) + var route = new RouteBuilder() + .WithDownstreamRoutes(downstreamRoutes) .Build(); var billDownstreamContext = new DefaultHttpContext(); billDownstreamContext.Items.UpsertDownstreamResponse(new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new EditableList>>(), "some reason")); - billDownstreamContext.Items.UpsertDownstreamReRoute(billDownstreamReRoute); + billDownstreamContext.Items.UpsertDownstreamRoute(billDownstreamRoute); var georgeDownstreamContext = new DefaultHttpContext(); georgeDownstreamContext.Items.UpsertDownstreamResponse(new DownstreamResponse(new StringContent("George says hi"), HttpStatusCode.OK, new List>>(), "some reason")); - georgeDownstreamContext.Items.UpsertDownstreamReRoute(georgeDownstreamReRoute); + georgeDownstreamContext.Items.UpsertDownstreamRoute(georgeDownstreamRoute); var downstreamContexts = new List { billDownstreamContext, georgeDownstreamContext }; var expected = "{\"Bill\":Bill says hi,\"George\":George says hi}"; this.Given(x => GivenTheUpstreamContext(new DefaultHttpContext())) - .And(x => GivenTheReRoute(reRoute)) + .And(x => GivenTheRoute(route)) .And(x => GivenTheDownstreamContext(downstreamContexts)) .When(x => WhenIAggregate()) .Then(x => ThenTheContentIs(expected)) @@ -119,27 +119,27 @@ public void should_aggregate_n_responses_and_set_response_content_on_upstream_co [Fact] public void should_return_error_if_any_downstreams_have_errored() { - var billDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("Bill").Build(); + var billDownstreamRoute = new DownstreamRouteBuilder().WithKey("Bill").Build(); - var georgeDownstreamReRoute = new DownstreamReRouteBuilder().WithKey("George").Build(); + var georgeDownstreamRoute = new DownstreamRouteBuilder().WithKey("George").Build(); - var downstreamReRoutes = new List + var downstreamRoutes = new List { - billDownstreamReRoute, - georgeDownstreamReRoute + billDownstreamRoute, + georgeDownstreamRoute }; - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoutes(downstreamReRoutes) + var route = new RouteBuilder() + .WithDownstreamRoutes(downstreamRoutes) .Build(); var billDownstreamContext = new DefaultHttpContext(); billDownstreamContext.Items.UpsertDownstreamResponse(new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new List>>(), "some reason")); - billDownstreamContext.Items.UpsertDownstreamReRoute(billDownstreamReRoute); + billDownstreamContext.Items.UpsertDownstreamRoute(billDownstreamRoute); var georgeDownstreamContext = new DefaultHttpContext(); georgeDownstreamContext.Items.UpsertDownstreamResponse(new DownstreamResponse(new StringContent("Error"), HttpStatusCode.OK, new List>>(), "some reason")); - georgeDownstreamContext.Items.UpsertDownstreamReRoute(georgeDownstreamReRoute); + georgeDownstreamContext.Items.UpsertDownstreamRoute(georgeDownstreamRoute); georgeDownstreamContext.Items.SetError(new AnyError()); @@ -148,7 +148,7 @@ public void should_return_error_if_any_downstreams_have_errored() var expected = "Error"; this.Given(x => GivenTheUpstreamContext(new DefaultHttpContext())) - .And(x => GivenTheReRoute(reRoute)) + .And(x => GivenTheRoute(route)) .And(x => GivenTheDownstreamContext(downstreamContexts)) .When(x => WhenIAggregate()) .Then(x => ThenTheContentIs(expected)) @@ -167,9 +167,9 @@ private void ThenTheErrorIsMapped() _upstreamContext.Items.DownstreamResponse().ShouldBe(_downstreamContexts[1].Items.DownstreamResponse()); } - private void GivenTheReRoute(ReRoute reRoute) + private void GivenTheRoute(Route route) { - _reRoute = reRoute; + _route = route; } private void GivenTheUpstreamContext(HttpContext upstreamContext) @@ -184,7 +184,7 @@ private void GivenTheDownstreamContext(List downstreamContexts) private void WhenIAggregate() { - _aggregator.Aggregate(_reRoute, _upstreamContext, _downstreamContexts).GetAwaiter().GetResult(); + _aggregator.Aggregate(_route, _upstreamContext, _downstreamContexts).GetAwaiter().GetResult(); } private void ThenTheContentIs(string expected) diff --git a/test/Ocelot.UnitTests/Multiplexing/UserDefinedResponseAggregatorTests.cs b/test/Ocelot.UnitTests/Multiplexing/UserDefinedResponseAggregatorTests.cs index aa9e26ef2..cc7527770 100644 --- a/test/Ocelot.UnitTests/Multiplexing/UserDefinedResponseAggregatorTests.cs +++ b/test/Ocelot.UnitTests/Multiplexing/UserDefinedResponseAggregatorTests.cs @@ -21,7 +21,7 @@ public class UserDefinedResponseAggregatorTests { private readonly UserDefinedResponseAggregator _aggregator; private readonly Mock _provider; - private ReRoute _reRoute; + private Route _route; private List _contexts; private HttpContext _context; @@ -34,7 +34,7 @@ public UserDefinedResponseAggregatorTests() [Fact] public void should_call_aggregator() { - var reRoute = new ReRouteBuilder().Build(); + var route = new RouteBuilder().Build(); var context = new DefaultHttpContext(); @@ -51,7 +51,7 @@ public void should_call_aggregator() }; this.Given(_ => GivenTheProviderReturnsAggregator()) - .And(_ => GivenReRoute(reRoute)) + .And(_ => GivenRoute(route)) .And(_ => GivenContexts(contexts)) .And(_ => GivenContext(context)) .When(_ => WhenIAggregate()) @@ -63,7 +63,7 @@ public void should_call_aggregator() [Fact] public void should_not_find_aggregator() { - var reRoute = new ReRouteBuilder().Build(); + var route = new RouteBuilder().Build(); var context = new DefaultHttpContext(); @@ -80,7 +80,7 @@ public void should_not_find_aggregator() }; this.Given(_ => GivenTheProviderReturnsError()) - .And(_ => GivenReRoute(reRoute)) + .And(_ => GivenRoute(route)) .And(_ => GivenContexts(contexts)) .And(_ => GivenContext(context)) .When(_ => WhenIAggregate()) @@ -97,7 +97,7 @@ private void ThenTheErrorIsReturned() private void GivenTheProviderReturnsError() { - _provider.Setup(x => x.Get(It.IsAny())).Returns(new ErrorResponse(new AnyError())); + _provider.Setup(x => x.Get(It.IsAny())).Returns(new ErrorResponse(new AnyError())); } private async Task ThenTheContentIsCorrect() @@ -108,7 +108,7 @@ private async Task ThenTheContentIsCorrect() private void ThenTheProviderIsCalled() { - _provider.Verify(x => x.Get(_reRoute), Times.Once); + _provider.Verify(x => x.Get(_route), Times.Once); } private void GivenContext(HttpContext context) @@ -123,18 +123,18 @@ private void GivenContexts(List contexts) private async Task WhenIAggregate() { - await _aggregator.Aggregate(_reRoute, _context, _contexts); + await _aggregator.Aggregate(_route, _context, _contexts); } private void GivenTheProviderReturnsAggregator() { var aggregator = new TestDefinedAggregator(); - _provider.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse(aggregator)); + _provider.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse(aggregator)); } - private void GivenReRoute(ReRoute reRoute) + private void GivenRoute(Route route) { - _reRoute = reRoute; + _route = route; } public class TestDefinedAggregator : IDefinedAggregator diff --git a/test/Ocelot.UnitTests/Polly/OcelotBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/Polly/OcelotBuilderExtensionsTests.cs index 448546497..4ea6bd6ed 100644 --- a/test/Ocelot.UnitTests/Polly/OcelotBuilderExtensionsTests.cs +++ b/test/Ocelot.UnitTests/Polly/OcelotBuilderExtensionsTests.cs @@ -1,45 +1,45 @@ -namespace Ocelot.UnitTests.Polly -{ - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration.Builder; - using Ocelot.DependencyInjection; - using Ocelot.Logging; - using Ocelot.Requester; - using Provider.Polly; - using Shouldly; - using System.IO; - using Xunit; - - public class OcelotBuilderExtensionsTests - { - [Fact] - public void should_build() - { - var loggerFactory = new Mock(); - var services = new ServiceCollection(); - var options = new QoSOptionsBuilder() - .WithTimeoutValue(100) - .WithExceptionsAllowedBeforeBreaking(1) - .WithDurationOfBreak(200) - .Build(); - var reRoute = new DownstreamReRouteBuilder().WithQosOptions(options) - .Build(); - - var configuration = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .Build(); - services - .AddOcelot(configuration) - .AddPolly(); - var provider = services.BuildServiceProvider(); - - var handler = provider.GetService(); - handler.ShouldNotBeNull(); - - var delgatingHandler = handler(reRoute, loggerFactory.Object); - delgatingHandler.ShouldNotBeNull(); - } - } -} +namespace Ocelot.UnitTests.Polly +{ + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration.Builder; + using Ocelot.DependencyInjection; + using Ocelot.Logging; + using Ocelot.Requester; + using Provider.Polly; + using Shouldly; + using System.IO; + using Xunit; + + public class OcelotBuilderExtensionsTests + { + [Fact] + public void should_build() + { + var loggerFactory = new Mock(); + var services = new ServiceCollection(); + var options = new QoSOptionsBuilder() + .WithTimeoutValue(100) + .WithExceptionsAllowedBeforeBreaking(1) + .WithDurationOfBreak(200) + .Build(); + var route = new DownstreamRouteBuilder().WithQosOptions(options) + .Build(); + + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .Build(); + services + .AddOcelot(configuration) + .AddPolly(); + var provider = services.BuildServiceProvider(); + + var handler = provider.GetService(); + handler.ShouldNotBeNull(); + + var delgatingHandler = handler(route, loggerFactory.Object); + delgatingHandler.ShouldNotBeNull(); + } + } +} diff --git a/test/Ocelot.UnitTests/Polly/PollyQoSProviderTests.cs b/test/Ocelot.UnitTests/Polly/PollyQoSProviderTests.cs index 4a4cc805d..e01523c7e 100644 --- a/test/Ocelot.UnitTests/Polly/PollyQoSProviderTests.cs +++ b/test/Ocelot.UnitTests/Polly/PollyQoSProviderTests.cs @@ -1,27 +1,27 @@ -namespace Ocelot.UnitTests.Polly -{ - using Moq; - using Ocelot.Configuration.Builder; - using Ocelot.Logging; - using Provider.Polly; - using Shouldly; - using Xunit; - - public class PollyQoSProviderTests - { - [Fact] - public void should_build() - { - var options = new QoSOptionsBuilder() - .WithTimeoutValue(100) - .WithExceptionsAllowedBeforeBreaking(1) - .WithDurationOfBreak(200) - .Build(); - var reRoute = new DownstreamReRouteBuilder().WithQosOptions(options) - .Build(); - var factory = new Mock(); - var pollyQoSProvider = new PollyQoSProvider(reRoute, factory.Object); - pollyQoSProvider.CircuitBreaker.ShouldNotBeNull(); - } - } -} +namespace Ocelot.UnitTests.Polly +{ + using Moq; + using Ocelot.Configuration.Builder; + using Ocelot.Logging; + using Provider.Polly; + using Shouldly; + using Xunit; + + public class PollyQoSProviderTests + { + [Fact] + public void should_build() + { + var options = new QoSOptionsBuilder() + .WithTimeoutValue(100) + .WithExceptionsAllowedBeforeBreaking(1) + .WithDurationOfBreak(200) + .Build(); + var route = new DownstreamRouteBuilder().WithQosOptions(options) + .Build(); + var factory = new Mock(); + var pollyQoSProvider = new PollyQoSProvider(route, factory.Object); + pollyQoSProvider.CircuitBreaker.ShouldNotBeNull(); + } + } +} diff --git a/test/Ocelot.UnitTests/QueryStrings/ClaimsToQueryStringMiddlewareTests.cs b/test/Ocelot.UnitTests/QueryStrings/ClaimsToQueryStringMiddlewareTests.cs index 6222199d2..d69b190f8 100644 --- a/test/Ocelot.UnitTests/QueryStrings/ClaimsToQueryStringMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/QueryStrings/ClaimsToQueryStringMiddlewareTests.cs @@ -1,102 +1,102 @@ -using Ocelot.Middleware; - -namespace Ocelot.UnitTests.QueryStrings -{ - using Microsoft.AspNetCore.Http; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder; - using Ocelot.DownstreamRouteFinder.UrlMatcher; - using Ocelot.Logging; - using Ocelot.QueryStrings; - using Ocelot.QueryStrings.Middleware; - using Ocelot.Request.Middleware; - using Ocelot.Responses; - using System.Collections.Generic; - using System.Net.Http; - using System.Security.Claims; - using System.Threading.Tasks; - using Ocelot.Infrastructure.RequestData; - using TestStack.BDDfy; +using Ocelot.Middleware; + +namespace Ocelot.UnitTests.QueryStrings +{ + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.DownstreamRouteFinder; + using Ocelot.DownstreamRouteFinder.UrlMatcher; + using Ocelot.Logging; + using Ocelot.QueryStrings; + using Ocelot.QueryStrings.Middleware; + using Ocelot.Request.Middleware; + using Ocelot.Responses; + using System.Collections.Generic; + using System.Net.Http; + using System.Security.Claims; + using System.Threading.Tasks; + using Ocelot.Infrastructure.RequestData; + using TestStack.BDDfy; using Xunit; using Ocelot.DownstreamRouteFinder.Middleware; - public class ClaimsToQueryStringMiddlewareTests - { - private readonly Mock _addQueries; - private Mock _loggerFactory; - private Mock _logger; - private ClaimsToQueryStringMiddleware _middleware; - private RequestDelegate _next; - private HttpContext _httpContext; - private Mock _repo; - - public ClaimsToQueryStringMiddlewareTests() - { - _repo = new Mock(); - _httpContext = new DefaultHttpContext(); - _loggerFactory = new Mock(); - _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _next = context => Task.CompletedTask; + public class ClaimsToQueryStringMiddlewareTests + { + private readonly Mock _addQueries; + private Mock _loggerFactory; + private Mock _logger; + private ClaimsToQueryStringMiddleware _middleware; + private RequestDelegate _next; + private HttpContext _httpContext; + private Mock _repo; + + public ClaimsToQueryStringMiddlewareTests() + { + _repo = new Mock(); + _httpContext = new DefaultHttpContext(); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = context => Task.CompletedTask; _addQueries = new Mock(); - _httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"))); - _middleware = new ClaimsToQueryStringMiddleware(_next, _loggerFactory.Object, _addQueries.Object); - } - - [Fact] - public void should_call_add_queries_correctly() - { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() - .WithDownstreamPathTemplate("any old string") - .WithClaimsToQueries(new List - { - new ClaimToThing("UserId", "Subject", "", 0) - }) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()) - .WithUpstreamHttpMethod(new List { "Get" }) - .Build()); - - this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) - .And(x => x.GivenTheAddHeadersToRequestReturnsOk()) - .When(x => x.WhenICallTheMiddleware()) - .Then(x => x.ThenTheAddQueriesToRequestIsCalledCorrectly()) - .BDDfy(); - } - - private void WhenICallTheMiddleware() - { - _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); - } - - private void GivenTheAddHeadersToRequestReturnsOk() - { - _addQueries - .Setup(x => x.SetQueriesOnDownstreamRequest( - It.IsAny>(), - It.IsAny>(), - It.IsAny())) - .Returns(new OkResponse()); - } - - private void ThenTheAddQueriesToRequestIsCalledCorrectly() - { - _addQueries - .Verify(x => x.SetQueriesOnDownstreamRequest( - It.IsAny>(), - It.IsAny>(), - _httpContext.Items.DownstreamRequest()), Times.Once); - } - - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + _httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"))); + _middleware = new ClaimsToQueryStringMiddleware(_next, _loggerFactory.Object, _addQueries.Object); + } + + [Fact] + public void should_call_add_queries_correctly() + { + var downstreamRoute = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithClaimsToQueries(new List + { + new ClaimToThing("UserId", "Subject", "", 0) + }) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) + .And(x => x.GivenTheAddHeadersToRequestReturnsOk()) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenTheAddQueriesToRequestIsCalledCorrectly()) + .BDDfy(); + } + + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_httpContext).GetAwaiter().GetResult(); + } + + private void GivenTheAddHeadersToRequestReturnsOk() + { + _addQueries + .Setup(x => x.SetQueriesOnDownstreamRequest( + It.IsAny>(), + It.IsAny>(), + It.IsAny())) + .Returns(new OkResponse()); + } + + private void ThenTheAddQueriesToRequestIsCalledCorrectly() + { + _addQueries + .Verify(x => x.SetQueriesOnDownstreamRequest( + It.IsAny>(), + It.IsAny>(), + _httpContext.Items.DownstreamRequest()), Times.Once); + } + + private void GivenTheDownStreamRouteIs(Ocelot.DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) { _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); - } - } -} + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); + } + } +} diff --git a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs index 3d8feda29..6e5b15b1d 100644 --- a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs @@ -50,23 +50,23 @@ public void should_call_middleware_and_ratelimiting() { var upstreamTemplate = new UpstreamPathTemplateBuilder().Build(); - var downstreamReRoute = new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteBuilder() .WithEnableRateLimiting(true) .WithRateLimitOptions(new RateLimitOptions(true, "ClientId", () => new List(), false, "", "", new RateLimitRule("1s", 100, 3), 429)) .WithUpstreamHttpMethod(new List { "Get" }) .WithUpstreamPathTemplate(upstreamTemplate) .Build(); - var reRoute = new ReRouteBuilder() - .WithDownstreamReRoute(downstreamReRoute) + var route = new RouteBuilder() + .WithDownstreamRoute(downstreamRoute) .WithUpstreamHttpMethod(new List { "Get" }) .Build(); - var downstreamRoute = new DownstreamRoute(new List(), reRoute); + var downstreamRouteHolder = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(new List(), route); - this.Given(x => x.WhenICallTheMiddlewareMultipleTimes(2, downstreamRoute)) + this.Given(x => x.WhenICallTheMiddlewareMultipleTimes(2, downstreamRouteHolder)) .Then(x => x.ThenThereIsNoDownstreamResponse()) - .When(x => x.WhenICallTheMiddlewareMultipleTimes(3, downstreamRoute)) + .When(x => x.WhenICallTheMiddlewareMultipleTimes(3, downstreamRouteHolder)) .Then(x => x.ThenTheResponseIs429()) .BDDfy(); } @@ -74,9 +74,9 @@ public void should_call_middleware_and_ratelimiting() [Fact] public void should_call_middleware_withWhitelistClient() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() + var downstreamRoute = new Ocelot.DownstreamRouteFinder.DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() .WithEnableRateLimiting(true) .WithRateLimitOptions( new Ocelot.Configuration.RateLimitOptions(true, "ClientId", () => new List() { "ocelotclient2" }, false, "", "", new RateLimitRule("1s", 100, 3), 429)) @@ -90,7 +90,7 @@ public void should_call_middleware_withWhitelistClient() .BDDfy(); } - private void WhenICallTheMiddlewareMultipleTimes(int times, DownstreamRoute downstreamRoute) + private void WhenICallTheMiddlewareMultipleTimes(int times, Ocelot.DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) { var httpContexts = new List(); @@ -98,7 +98,7 @@ private void WhenICallTheMiddlewareMultipleTimes(int times, DownstreamRoute down { var httpContext = new DefaultHttpContext(); httpContext.Response.Body = new FakeStream(); - httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); + httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); httpContext.Items.UpsertDownstreamRoute(downstreamRoute); var clientId = "ocelotclient1"; @@ -116,7 +116,7 @@ private void WhenICallTheMiddlewareMultipleTimes(int times, DownstreamRoute down } } - private void WhenICallTheMiddlewareWithWhiteClient(DownstreamRoute downstreamRoute) + private void WhenICallTheMiddlewareWithWhiteClient(Ocelot.DownstreamRouteFinder.DownstreamRouteHolder downstreamRoute) { var clientId = "ocelotclient2"; @@ -124,7 +124,7 @@ private void WhenICallTheMiddlewareWithWhiteClient(DownstreamRoute downstreamRou { var httpContext = new DefaultHttpContext(); httpContext.Response.Body = new FakeStream(); - httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); + httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); httpContext.Items.UpsertDownstreamRoute(downstreamRoute); var request = new HttpRequestMessage(new HttpMethod("GET"), _url); diff --git a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs index 473f119f6..5bbc9c7a5 100644 --- a/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Request/DownstreamRequestInitialiserMiddlewareTests.cs @@ -60,7 +60,7 @@ public void Should_handle_valid_httpRequest() } [Fact] - public void Should_map_downstream_reroute_method_to_downstream_request() + public void Should_map_downstream_route_method_to_downstream_request() { this.Given(_ => GivenTheHttpContextContainsARequest()) .And(_ => GivenTheMapperWillReturnAMappedRequest()) @@ -91,7 +91,7 @@ private void ThenTheDownstreamRequestMethodIs(string expected) private void GivenTheHttpContextContainsARequest() { - _httpContext.Items.UpsertDownstreamReRoute(new DownstreamReRouteBuilder().Build()); + _httpContext.Items.UpsertDownstreamRoute(new DownstreamRouteBuilder().Build()); } private void GivenTheMapperWillReturnAMappedRequest() @@ -99,7 +99,7 @@ private void GivenTheMapperWillReturnAMappedRequest() _mappedRequest = new OkResponse(new HttpRequestMessage(HttpMethod.Get, "http://www.bbc.co.uk")); _requestMapper - .Setup(rm => rm.Map(It.IsAny(), It.IsAny())) + .Setup(rm => rm.Map(It.IsAny(), It.IsAny())) .ReturnsAsync(_mappedRequest); } @@ -108,7 +108,7 @@ private void GivenTheMapperWillReturnAnError() _mappedRequest = new ErrorResponse(new UnmappableRequestError(new System.Exception("boooom!"))); _requestMapper - .Setup(rm => rm.Map(It.IsAny(), It.IsAny())) + .Setup(rm => rm.Map(It.IsAny(), It.IsAny())) .ReturnsAsync(_mappedRequest); } @@ -119,7 +119,7 @@ private void WhenTheMiddlewareIsInvoked() private void ThenTheContexRequestIsMappedToADownstreamRequest() { - _requestMapper.Verify(rm => rm.Map(_httpContext.Request, _httpContext.Items.DownstreamReRoute()), Times.Once); + _requestMapper.Verify(rm => rm.Map(_httpContext.Request, _httpContext.Items.DownstreamRoute()), Times.Once); } private void ThenTheDownstreamRequestIsStored() diff --git a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs index 501e290c1..ad930730f 100644 --- a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs +++ b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs @@ -1,484 +1,484 @@ -namespace Ocelot.UnitTests.Request.Mapper -{ - using Microsoft.AspNetCore.Http; - using Microsoft.Extensions.Primitives; - using Ocelot.Request.Mapper; - using Ocelot.Responses; - using Shouldly; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net.Http; - using System.Security.Cryptography; - using System.Text; - using System.Threading.Tasks; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using TestStack.BDDfy; - using Xunit; - - public class RequestMapperTests - { - private readonly HttpContext _httpContext; - private readonly HttpRequest _inputRequest; - - private readonly RequestMapper _requestMapper; - - private Response _mappedRequest; - - private List> _inputHeaders = null; - - private DownstreamReRoute _downstreamReRoute; - - public RequestMapperTests() - { - _httpContext = new DefaultHttpContext(); - _inputRequest = _httpContext.Request; - _requestMapper = new RequestMapper(); - } - - [Theory] - [InlineData("https", "my.url:123", "/abc/DEF", "?a=1&b=2", "https://my.url:123/abc/DEF?a=1&b=2")] - [InlineData("http", "blah.com", "/d ef", "?abc=123", "http://blah.com/d%20ef?abc=123")] // note! the input is encoded when building the input request - [InlineData("http", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")] - [InlineData("http", "點看.com", null, null, "http://xn--c1yn36f.com/")] - [InlineData("http", "xn--c1yn36f.com", null, null, "http://xn--c1yn36f.com/")] - public void Should_map_valid_request_uri(string scheme, string host, string path, string queryString, string expectedUri) - { - this.Given(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasScheme(scheme)) - .And(_ => GivenTheInputRequestHasHost(host)) - .And(_ => GivenTheInputRequestHasPath(path)) - .And(_ => GivenTheInputRequestHasQueryString(queryString)) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasUri(expectedUri)) - .BDDfy(); - } - - [Theory] - [InlineData("ftp", "google.com", "/abc/DEF", "?a=1&b=2")] - public void Should_error_on_unsupported_request_uri(string scheme, string host, string path, string queryString) - { - this.Given(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasScheme(scheme)) - .And(_ => GivenTheInputRequestHasHost(host)) - .And(_ => GivenTheInputRequestHasPath(path)) - .And(_ => GivenTheInputRequestHasQueryString(queryString)) - .When(_ => WhenMapped()) - .Then(_ => ThenAnErrorIsReturned()) - .And(_ => ThenTheMappedRequestIsNull()) - .BDDfy(); - } - - [Theory] - [InlineData("GET")] - [InlineData("POST")] - [InlineData("WHATEVER")] - public void Should_map_method(string method) - { - this.Given(_ => GivenTheInputRequestHasMethod(method)) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasMethod(method)) - .BDDfy(); - } - - [Theory] - [InlineData("", "GET")] - [InlineData(null, "GET")] - [InlineData("POST", "POST")] - public void Should_use_downstream_reroute_method_if_set(string input, string expected) - { - this.Given(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheDownstreamReRouteMethodIs(input)) - .And(_ => GivenTheInputRequestHasAValidUri()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasMethod(expected)) - .BDDfy(); - } - - [Fact] - public void Should_map_all_headers() - { - this.Given(_ => GivenTheInputRequestHasHeaders()) - .And(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasEachHeader()) - .BDDfy(); - } - - [Fact] - public void Should_handle_no_headers() - { - this.Given(_ => GivenTheInputRequestHasNoHeaders()) - .And(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasNoHeaders()) - .BDDfy(); - } - - [Fact] - public void Should_map_content() - { - this.Given(_ => GivenTheInputRequestHasContent("This is my content")) - .And(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasContent("This is my content")) - .BDDfy(); - } - - [Fact] - public void Should_handle_no_content() - { - this.Given(_ => GivenTheInputRequestHasNullContent()) - .And(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasNoContent()) - .BDDfy(); - } - - [Fact] - public void Should_handle_no_content_type() - { - this.Given(_ => GivenTheInputRequestHasNoContentType()) - .And(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasNoContent()) - .BDDfy(); - } - - [Fact] - public void Should_handle_no_content_length() - { - this.Given(_ => GivenTheInputRequestHasNoContentLength()) - .And(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasNoContent()) - .BDDfy(); - } - - [Fact] - public void Should_map_content_headers() - { - byte[] md5bytes = new byte[0]; - using (var md5 = MD5.Create()) - { - md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5")); - } - - this.Given(_ => GivenTheInputRequestHasContent("This is my content")) - .And(_ => GivenTheContentTypeIs("application/json")) - .And(_ => GivenTheContentEncodingIs("gzip, compress")) - .And(_ => GivenTheContentLanguageIs("english")) - .And(_ => GivenTheContentLocationIs("/my-receipts/38")) - .And(_ => GivenTheContentRangeIs("bytes 1-2/*")) - .And(_ => GivenTheContentDispositionIs("inline")) - .And(_ => GivenTheContentMD5Is(md5bytes)) - .And(_ => GivenTheInputRequestHasMethod("GET")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) - .And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress")) - .And(_ => ThenTheMappedRequestHasContentLanguageHeader("english")) - .And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38")) - .And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes)) - .And(_ => ThenTheMappedRequestHasContentRangeHeader()) - .And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline")) - .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) - .And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders()) - .BDDfy(); - } - - [Fact] - public void should_not_add_content_headers() - { - this.Given(_ => GivenTheInputRequestHasContent("This is my content")) - .And(_ => GivenTheContentTypeIs("application/json")) - .And(_ => GivenTheInputRequestHasMethod("POST")) - .And(_ => GivenTheInputRequestHasAValidUri()) - .And(_ => GivenTheDownstreamReRoute()) - .When(_ => WhenMapped()) - .Then(_ => ThenNoErrorIsReturned()) - .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) - .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) - .And(_ => ThenTheOtherContentTypeHeadersAreNotMapped()) - .BDDfy(); - } - - private void GivenTheDownstreamReRouteMethodIs(string input) - { - _downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownStreamHttpMethod(input) - .WithDownstreamHttpVersion(new Version("1.1")).Build(); - } - - private void GivenTheDownstreamReRoute() - { - _downstreamReRoute = new DownstreamReRouteBuilder() - .WithDownstreamHttpVersion(new Version("1.1")).Build(); - } - - private void GivenTheInputRequestHasNoContentLength() - { - _inputRequest.ContentLength = null; - } - - private void GivenTheInputRequestHasNoContentType() - { - _inputRequest.ContentType = null; - } - - - private void ThenTheContentHeadersAreNotAddedToNonContentHeaders() - { - _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition"); - _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5"); - _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange"); - _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage"); - _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentEncoding"); - _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLocation"); - _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Length"); - _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type"); - } - - private void ThenTheOtherContentTypeHeadersAreNotMapped() - { - _mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull(); - _mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull(); - _mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull(); - _mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty(); - _mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty(); - _mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull(); - } - - private void ThenTheMappedRequestHasContentDispositionHeader(string expected) - { - _mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected); - } - - private void GivenTheContentDispositionIs(string input) - { - _inputRequest.Headers.Add("Content-Disposition", input); - } - - private void ThenTheMappedRequestHasContentMD5Header(byte[] expected) - { - _mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected); - } - - private void GivenTheContentMD5Is(byte[] input) - { - var base64 = Convert.ToBase64String(input); - _inputRequest.Headers.Add("Content-MD5", base64); - } - - private void ThenTheMappedRequestHasContentRangeHeader() - { - _mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1); - _mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2); - } - - private void GivenTheContentRangeIs(string input) - { - _inputRequest.Headers.Add("Content-Range", input); - } - - private void ThenTheMappedRequestHasContentLocationHeader(string expected) - { - _mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected); - } - - private void GivenTheContentLocationIs(string input) - { - _inputRequest.Headers.Add("Content-Location", input); - } - - private void ThenTheMappedRequestHasContentLanguageHeader(string expected) - { - _mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected); - } - - private void GivenTheContentLanguageIs(string input) - { - _inputRequest.Headers.Add("Content-Language", input); - } - - private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo) - { - _mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected); - _mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo); - } - - private void GivenTheContentEncodingIs(string input) - { - _inputRequest.Headers.Add("Content-Encoding", input); - } - - private void GivenTheContentTypeIs(string contentType) - { - _inputRequest.ContentType = contentType; - } - - private void ThenTheMappedRequestHasContentTypeHeader(string expected) - { - _mappedRequest.Data.Content.Headers.ContentType.MediaType.ShouldBe(expected); - } - - private void ThenTheMappedRequestHasContentSize(long expected) - { - _mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected); - } - - private void GivenTheInputRequestHasMethod(string method) - { - _inputRequest.Method = method; - } - - private void GivenTheInputRequestHasScheme(string scheme) - { - _inputRequest.Scheme = scheme; - } - - private void GivenTheInputRequestHasHost(string host) - { - _inputRequest.Host = new HostString(host); - } - - private void GivenTheInputRequestHasPath(string path) - { - if (path != null) - { - _inputRequest.Path = path; - } - } - - private void GivenTheInputRequestHasQueryString(string querystring) - { - if (querystring != null) - { - _inputRequest.QueryString = new QueryString(querystring); - } - } - - private void GivenTheInputRequestHasAValidUri() - { - GivenTheInputRequestHasScheme("http"); - GivenTheInputRequestHasHost("www.google.com"); - } - - private void GivenTheInputRequestHasHeaders() - { - _inputHeaders = new List>() - { - new KeyValuePair("abc", new StringValues(new string[]{"123","456" })), - new KeyValuePair("def", new StringValues(new string[]{"789","012" })), - }; - - foreach (var inputHeader in _inputHeaders) - { - _inputRequest.Headers.Add(inputHeader); - } - } - - private void GivenTheInputRequestHasNoHeaders() - { - _inputRequest.Headers.Clear(); - } - - private void GivenTheInputRequestHasContent(string content) - { - _inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content)); - } - - private void GivenTheInputRequestHasNullContent() - { - _inputRequest.Body = null; - } - - private async Task WhenMapped() - { - _mappedRequest = await _requestMapper.Map(_inputRequest, _downstreamReRoute); - } - - private void ThenNoErrorIsReturned() - { - _mappedRequest.IsError.ShouldBeFalse(); - } - - private void ThenAnErrorIsReturned() - { - _mappedRequest.IsError.ShouldBeTrue(); - } - - private void ThenTheMappedRequestHasUri(string expectedUri) - { - _mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri); - } - - private void ThenTheMappedRequestHasMethod(string expectedMethod) - { - _mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod); - } - - private void ThenTheMappedRequestHasEachHeader() - { - _mappedRequest.Data.Headers.Count().ShouldBe(_inputHeaders.Count); - foreach (var header in _mappedRequest.Data.Headers) - { - var inputHeader = _inputHeaders.First(h => h.Key == header.Key); - inputHeader.ShouldNotBeNull(); - inputHeader.Value.Count().ShouldBe(header.Value.Count()); - foreach (var inputHeaderValue in inputHeader.Value) - { - header.Value.Any(v => v == inputHeaderValue); - } - } - } - - private void ThenTheMappedRequestHasNoHeaders() - { - _mappedRequest.Data.Headers.Count().ShouldBe(0); - } - - private void ThenTheMappedRequestHasContent(string expectedContent) - { - _mappedRequest.Data.Content.ReadAsStringAsync().GetAwaiter().GetResult().ShouldBe(expectedContent); - } - - private void ThenTheMappedRequestHasNoContent() - { - _mappedRequest.Data.Content.ShouldBeNull(); - } - - private void ThenTheMappedRequestIsNull() - { - _mappedRequest.Data.ShouldBeNull(); - } - } -} +namespace Ocelot.UnitTests.Request.Mapper +{ + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Primitives; + using Ocelot.Request.Mapper; + using Ocelot.Responses; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net.Http; + using System.Security.Cryptography; + using System.Text; + using System.Threading.Tasks; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using TestStack.BDDfy; + using Xunit; + + public class RequestMapperTests + { + private readonly HttpContext _httpContext; + private readonly HttpRequest _inputRequest; + + private readonly RequestMapper _requestMapper; + + private Response _mappedRequest; + + private List> _inputHeaders = null; + + private DownstreamRoute _downstreamRoute; + + public RequestMapperTests() + { + _httpContext = new DefaultHttpContext(); + _inputRequest = _httpContext.Request; + _requestMapper = new RequestMapper(); + } + + [Theory] + [InlineData("https", "my.url:123", "/abc/DEF", "?a=1&b=2", "https://my.url:123/abc/DEF?a=1&b=2")] + [InlineData("http", "blah.com", "/d ef", "?abc=123", "http://blah.com/d%20ef?abc=123")] // note! the input is encoded when building the input request + [InlineData("http", "myusername:mypassword@abc.co.uk", null, null, "http://myusername:mypassword@abc.co.uk/")] + [InlineData("http", "點看.com", null, null, "http://xn--c1yn36f.com/")] + [InlineData("http", "xn--c1yn36f.com", null, null, "http://xn--c1yn36f.com/")] + public void Should_map_valid_request_uri(string scheme, string host, string path, string queryString, string expectedUri) + { + this.Given(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasScheme(scheme)) + .And(_ => GivenTheInputRequestHasHost(host)) + .And(_ => GivenTheInputRequestHasPath(path)) + .And(_ => GivenTheInputRequestHasQueryString(queryString)) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasUri(expectedUri)) + .BDDfy(); + } + + [Theory] + [InlineData("ftp", "google.com", "/abc/DEF", "?a=1&b=2")] + public void Should_error_on_unsupported_request_uri(string scheme, string host, string path, string queryString) + { + this.Given(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasScheme(scheme)) + .And(_ => GivenTheInputRequestHasHost(host)) + .And(_ => GivenTheInputRequestHasPath(path)) + .And(_ => GivenTheInputRequestHasQueryString(queryString)) + .When(_ => WhenMapped()) + .Then(_ => ThenAnErrorIsReturned()) + .And(_ => ThenTheMappedRequestIsNull()) + .BDDfy(); + } + + [Theory] + [InlineData("GET")] + [InlineData("POST")] + [InlineData("WHATEVER")] + public void Should_map_method(string method) + { + this.Given(_ => GivenTheInputRequestHasMethod(method)) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasMethod(method)) + .BDDfy(); + } + + [Theory] + [InlineData("", "GET")] + [InlineData(null, "GET")] + [InlineData("POST", "POST")] + public void Should_use_downstream_route_method_if_set(string input, string expected) + { + this.Given(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheDownstreamRouteMethodIs(input)) + .And(_ => GivenTheInputRequestHasAValidUri()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasMethod(expected)) + .BDDfy(); + } + + [Fact] + public void Should_map_all_headers() + { + this.Given(_ => GivenTheInputRequestHasHeaders()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasEachHeader()) + .BDDfy(); + } + + [Fact] + public void Should_handle_no_headers() + { + this.Given(_ => GivenTheInputRequestHasNoHeaders()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasNoHeaders()) + .BDDfy(); + } + + [Fact] + public void Should_map_content() + { + this.Given(_ => GivenTheInputRequestHasContent("This is my content")) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasContent("This is my content")) + .BDDfy(); + } + + [Fact] + public void Should_handle_no_content() + { + this.Given(_ => GivenTheInputRequestHasNullContent()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasNoContent()) + .BDDfy(); + } + + [Fact] + public void Should_handle_no_content_type() + { + this.Given(_ => GivenTheInputRequestHasNoContentType()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasNoContent()) + .BDDfy(); + } + + [Fact] + public void Should_handle_no_content_length() + { + this.Given(_ => GivenTheInputRequestHasNoContentLength()) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasNoContent()) + .BDDfy(); + } + + [Fact] + public void Should_map_content_headers() + { + byte[] md5bytes = new byte[0]; + using (var md5 = MD5.Create()) + { + md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5")); + } + + this.Given(_ => GivenTheInputRequestHasContent("This is my content")) + .And(_ => GivenTheContentTypeIs("application/json")) + .And(_ => GivenTheContentEncodingIs("gzip, compress")) + .And(_ => GivenTheContentLanguageIs("english")) + .And(_ => GivenTheContentLocationIs("/my-receipts/38")) + .And(_ => GivenTheContentRangeIs("bytes 1-2/*")) + .And(_ => GivenTheContentDispositionIs("inline")) + .And(_ => GivenTheContentMD5Is(md5bytes)) + .And(_ => GivenTheInputRequestHasMethod("GET")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) + .And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress")) + .And(_ => ThenTheMappedRequestHasContentLanguageHeader("english")) + .And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38")) + .And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes)) + .And(_ => ThenTheMappedRequestHasContentRangeHeader()) + .And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline")) + .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) + .And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders()) + .BDDfy(); + } + + [Fact] + public void should_not_add_content_headers() + { + this.Given(_ => GivenTheInputRequestHasContent("This is my content")) + .And(_ => GivenTheContentTypeIs("application/json")) + .And(_ => GivenTheInputRequestHasMethod("POST")) + .And(_ => GivenTheInputRequestHasAValidUri()) + .And(_ => GivenTheDownstreamRoute()) + .When(_ => WhenMapped()) + .Then(_ => ThenNoErrorIsReturned()) + .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) + .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) + .And(_ => ThenTheOtherContentTypeHeadersAreNotMapped()) + .BDDfy(); + } + + private void GivenTheDownstreamRouteMethodIs(string input) + { + _downstreamRoute = new DownstreamRouteBuilder() + .WithDownStreamHttpMethod(input) + .WithDownstreamHttpVersion(new Version("1.1")).Build(); + } + + private void GivenTheDownstreamRoute() + { + _downstreamRoute = new DownstreamRouteBuilder() + .WithDownstreamHttpVersion(new Version("1.1")).Build(); + } + + private void GivenTheInputRequestHasNoContentLength() + { + _inputRequest.ContentLength = null; + } + + private void GivenTheInputRequestHasNoContentType() + { + _inputRequest.ContentType = null; + } + + + private void ThenTheContentHeadersAreNotAddedToNonContentHeaders() + { + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentEncoding"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLocation"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Length"); + _mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type"); + } + + private void ThenTheOtherContentTypeHeadersAreNotMapped() + { + _mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull(); + _mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull(); + _mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull(); + _mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty(); + _mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty(); + _mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull(); + } + + private void ThenTheMappedRequestHasContentDispositionHeader(string expected) + { + _mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected); + } + + private void GivenTheContentDispositionIs(string input) + { + _inputRequest.Headers.Add("Content-Disposition", input); + } + + private void ThenTheMappedRequestHasContentMD5Header(byte[] expected) + { + _mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected); + } + + private void GivenTheContentMD5Is(byte[] input) + { + var base64 = Convert.ToBase64String(input); + _inputRequest.Headers.Add("Content-MD5", base64); + } + + private void ThenTheMappedRequestHasContentRangeHeader() + { + _mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1); + _mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2); + } + + private void GivenTheContentRangeIs(string input) + { + _inputRequest.Headers.Add("Content-Range", input); + } + + private void ThenTheMappedRequestHasContentLocationHeader(string expected) + { + _mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected); + } + + private void GivenTheContentLocationIs(string input) + { + _inputRequest.Headers.Add("Content-Location", input); + } + + private void ThenTheMappedRequestHasContentLanguageHeader(string expected) + { + _mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected); + } + + private void GivenTheContentLanguageIs(string input) + { + _inputRequest.Headers.Add("Content-Language", input); + } + + private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo) + { + _mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected); + _mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo); + } + + private void GivenTheContentEncodingIs(string input) + { + _inputRequest.Headers.Add("Content-Encoding", input); + } + + private void GivenTheContentTypeIs(string contentType) + { + _inputRequest.ContentType = contentType; + } + + private void ThenTheMappedRequestHasContentTypeHeader(string expected) + { + _mappedRequest.Data.Content.Headers.ContentType.MediaType.ShouldBe(expected); + } + + private void ThenTheMappedRequestHasContentSize(long expected) + { + _mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected); + } + + private void GivenTheInputRequestHasMethod(string method) + { + _inputRequest.Method = method; + } + + private void GivenTheInputRequestHasScheme(string scheme) + { + _inputRequest.Scheme = scheme; + } + + private void GivenTheInputRequestHasHost(string host) + { + _inputRequest.Host = new HostString(host); + } + + private void GivenTheInputRequestHasPath(string path) + { + if (path != null) + { + _inputRequest.Path = path; + } + } + + private void GivenTheInputRequestHasQueryString(string querystring) + { + if (querystring != null) + { + _inputRequest.QueryString = new QueryString(querystring); + } + } + + private void GivenTheInputRequestHasAValidUri() + { + GivenTheInputRequestHasScheme("http"); + GivenTheInputRequestHasHost("www.google.com"); + } + + private void GivenTheInputRequestHasHeaders() + { + _inputHeaders = new List>() + { + new KeyValuePair("abc", new StringValues(new string[]{"123","456" })), + new KeyValuePair("def", new StringValues(new string[]{"789","012" })), + }; + + foreach (var inputHeader in _inputHeaders) + { + _inputRequest.Headers.Add(inputHeader); + } + } + + private void GivenTheInputRequestHasNoHeaders() + { + _inputRequest.Headers.Clear(); + } + + private void GivenTheInputRequestHasContent(string content) + { + _inputRequest.Body = new MemoryStream(Encoding.UTF8.GetBytes(content)); + } + + private void GivenTheInputRequestHasNullContent() + { + _inputRequest.Body = null; + } + + private async Task WhenMapped() + { + _mappedRequest = await _requestMapper.Map(_inputRequest, _downstreamRoute); + } + + private void ThenNoErrorIsReturned() + { + _mappedRequest.IsError.ShouldBeFalse(); + } + + private void ThenAnErrorIsReturned() + { + _mappedRequest.IsError.ShouldBeTrue(); + } + + private void ThenTheMappedRequestHasUri(string expectedUri) + { + _mappedRequest.Data.RequestUri.OriginalString.ShouldBe(expectedUri); + } + + private void ThenTheMappedRequestHasMethod(string expectedMethod) + { + _mappedRequest.Data.Method.ToString().ShouldBe(expectedMethod); + } + + private void ThenTheMappedRequestHasEachHeader() + { + _mappedRequest.Data.Headers.Count().ShouldBe(_inputHeaders.Count); + foreach (var header in _mappedRequest.Data.Headers) + { + var inputHeader = _inputHeaders.First(h => h.Key == header.Key); + inputHeader.ShouldNotBeNull(); + inputHeader.Value.Count().ShouldBe(header.Value.Count()); + foreach (var inputHeaderValue in inputHeader.Value) + { + header.Value.Any(v => v == inputHeaderValue); + } + } + } + + private void ThenTheMappedRequestHasNoHeaders() + { + _mappedRequest.Data.Headers.Count().ShouldBe(0); + } + + private void ThenTheMappedRequestHasContent(string expectedContent) + { + _mappedRequest.Data.Content.ReadAsStringAsync().GetAwaiter().GetResult().ShouldBe(expectedContent); + } + + private void ThenTheMappedRequestHasNoContent() + { + _mappedRequest.Data.Content.ShouldBeNull(); + } + + private void ThenTheMappedRequestIsNull() + { + _mappedRequest.Data.ShouldBeNull(); + } + } +} diff --git a/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs similarity index 83% rename from test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs rename to test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs index c17f51c94..4c1474a0f 100644 --- a/test/Ocelot.UnitTests/RequestId/ReRouteRequestIdMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RequestId/RequestIdMiddlewareTests.cs @@ -21,40 +21,40 @@ using TestStack.BDDfy; using Xunit; - public class ReRouteRequestIdMiddlewareTests + public class RequestIdMiddlewareTests { private readonly HttpRequestMessage _downstreamRequest; private string _value; private string _key; private Mock _loggerFactory; private Mock _logger; - private readonly ReRouteRequestIdMiddleware _middleware; + private readonly RequestIdMiddleware _middleware; private RequestDelegate _next; private readonly Mock _repo; private HttpContext _httpContext; - public ReRouteRequestIdMiddlewareTests() + public RequestIdMiddlewareTests() { _httpContext = new DefaultHttpContext(); _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "http://test.com"); _repo = new Mock(); _loggerFactory = new Mock(); _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); _next = context => { _httpContext.Response.Headers.Add("LSRequestId", _httpContext.TraceIdentifier); return Task.CompletedTask; }; - _middleware = new ReRouteRequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object); + _middleware = new RequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object); _httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(_downstreamRequest)); } [Fact] public void should_pass_down_request_id_from_upstream_request() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId") .WithUpstreamHttpMethod(new List { "Get" }) @@ -75,9 +75,9 @@ public void should_pass_down_request_id_from_upstream_request() [Fact] public void should_add_request_id_when_not_on_upstream_request() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId") .WithUpstreamHttpMethod(new List { "Get" }) @@ -95,9 +95,9 @@ public void should_add_request_id_when_not_on_upstream_request() [Fact] public void should_add_request_id_scoped_repo_for_logging_later() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId") .WithUpstreamHttpMethod(new List { "Get" }) @@ -119,9 +119,9 @@ public void should_add_request_id_scoped_repo_for_logging_later() [Fact] public void should_update_request_id_scoped_repo_for_logging_later() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId") .WithUpstreamHttpMethod(new List { "Get" }) @@ -143,9 +143,9 @@ public void should_update_request_id_scoped_repo_for_logging_later() [Fact] public void should_not_update_if_global_request_id_is_same_as_re_route_request_id() { - var downstreamRoute = new DownstreamRoute(new List(), - new ReRouteBuilder() - .WithDownstreamReRoute(new DownstreamReRouteBuilder() + var downstreamRoute = new DownstreamRouteHolder(new List(), + new RouteBuilder() + .WithDownstreamRoute(new DownstreamRouteBuilder() .WithDownstreamPathTemplate("any old string") .WithRequestIdKey("LSRequestId") .WithUpstreamHttpMethod(new List { "Get" }) @@ -194,11 +194,11 @@ private void ThenTheRequestIdIsNotUpdated() _repo.Verify(x => x.Update("RequestId", _value), Times.Never); } - private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + private void GivenTheDownStreamRouteIs(DownstreamRouteHolder downstreamRoute) { _httpContext.Items.UpsertTemplatePlaceholderNameAndValues(downstreamRoute.TemplatePlaceholderNameAndValues); - _httpContext.Items.UpsertDownstreamReRoute(downstreamRoute.ReRoute.DownstreamReRoute[0]); + _httpContext.Items.UpsertDownstreamRoute(downstreamRoute.Route.DownstreamRoute[0]); } private void GivenTheRequestIdIsAddedToTheRequest(string key, string value) diff --git a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs index 050b2da8e..ec9d425d3 100644 --- a/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/DelegatingHandlerHandlerProviderFactoryTests.cs @@ -1,512 +1,512 @@ -namespace Ocelot.UnitTests.Requester -{ - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Logging; - using Ocelot.Requester; - using Ocelot.Requester.QoS; - using Ocelot.Responses; - using Responder; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Net.Http; - using TestStack.BDDfy; - using Xunit; - - public class DelegatingHandlerHandlerProviderFactoryTests - { - private DelegatingHandlerHandlerFactory _factory; - private readonly Mock _loggerFactory; - private readonly Mock _logger; - private DownstreamReRoute _downstreamReRoute; - private Response>> _result; - private readonly Mock _qosFactory; - private readonly Mock _tracingFactory; - private IServiceProvider _serviceProvider; - private readonly IServiceCollection _services; - private readonly QosDelegatingHandlerDelegate _qosDelegate; - - public DelegatingHandlerHandlerProviderFactoryTests() - { - _qosDelegate = (a, b) => new FakeQoSHandler(); - _tracingFactory = new Mock(); - _qosFactory = new Mock(); - _loggerFactory = new Mock(); - _logger = new Mock(); - _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); - _services = new ServiceCollection(); - _services.AddSingleton(_qosDelegate); - } - - [Fact] - public void should_follow_ordering_add_specifics() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) - .WithDelegatingHandlers(new List - { - "FakeDelegatingHandler", - "FakeDelegatingHandlerTwo" - }) - .WithLoadBalancerKey("") - .Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) - .And(x => GivenTheTracingFactoryReturns()) - .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) - .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(6)) - .And(x => ThenHandlerAtPositionIs(0)) - .And(x => ThenHandlerAtPositionIs(1)) - .And(x => ThenHandlerAtPositionIs(2)) - .And(x => ThenHandlerAtPositionIs(3)) - .And(x => ThenHandlerAtPositionIs(4)) - .And(x => ThenHandlerAtPositionIs(5)) - .BDDfy(); - } - - [Fact] - public void should_follow_ordering_order_specifics_and_globals() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) - .WithDelegatingHandlers(new List - { - "FakeDelegatingHandlerTwo", - "FakeDelegatingHandler", - "FakeDelegatingHandlerFour" - }) - .WithLoadBalancerKey("") - .Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) - .And(x => GivenTheTracingFactoryReturns()) - .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) - .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(6)) - .And(x => ThenHandlerAtPositionIs(0)) //first because global not in config - .And(x => ThenHandlerAtPositionIs(1)) //first from config - .And(x => ThenHandlerAtPositionIs(2)) //second from config - .And(x => ThenHandlerAtPositionIs(3)) //third from config (global) - .And(x => ThenHandlerAtPositionIs(4)) - .And(x => ThenHandlerAtPositionIs(5)) - .BDDfy(); - } - - [Fact] - public void should_follow_ordering_order_specifics() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) - .WithDelegatingHandlers(new List - { - "FakeDelegatingHandlerTwo", - "FakeDelegatingHandler" - }) - .WithLoadBalancerKey("") - .Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) - .And(x => GivenTheTracingFactoryReturns()) - .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) - .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(6)) - .And(x => ThenHandlerAtPositionIs(0)) - .And(x => ThenHandlerAtPositionIs(1)) - .And(x => ThenHandlerAtPositionIs(2)) - .And(x => ThenHandlerAtPositionIs(3)) - .And(x => ThenHandlerAtPositionIs(4)) - .And(x => ThenHandlerAtPositionIs(5)) - .BDDfy(); - } - - [Fact] - public void should_follow_ordering_order_and_only_add_specifics_in_config() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) - .WithDelegatingHandlers(new List - { - "FakeDelegatingHandler", - }) - .WithLoadBalancerKey("") - .Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) - .And(x => GivenTheTracingFactoryReturns()) - .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) - .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(5)) - .And(x => ThenHandlerAtPositionIs(0)) - .And(x => ThenHandlerAtPositionIs(1)) - .And(x => ThenHandlerAtPositionIs(2)) - .And(x => ThenHandlerAtPositionIs(3)) - .And(x => ThenHandlerAtPositionIs(4)) - .BDDfy(); - } - - [Fact] - public void should_follow_ordering_dont_add_specifics() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) - .WithLoadBalancerKey("") - .Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) - .And(x => GivenTheTracingFactoryReturns()) - .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) - .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(4)) - .And(x => ThenHandlerAtPositionIs(0)) - .And(x => ThenHandlerAtPositionIs(1)) - .And(x => ThenHandlerAtPositionIs(2)) - .And(x => ThenHandlerAtPositionIs(3)) - .BDDfy(); - } - - [Fact] - public void should_apply_re_route_specific() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)) - .WithDelegatingHandlers(new List - { - "FakeDelegatingHandler", - "FakeDelegatingHandlerTwo" - }) - .WithLoadBalancerKey("") - .Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(2)) - .And(x => ThenTheDelegatesAreAddedCorrectly()) - .BDDfy(); - } - - [Fact] - public void should_all_from_all_routes_provider_and_qos() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) - .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(3)) - .And(x => ThenTheDelegatesAreAddedCorrectly()) - .And(x => ThenItIsQosHandler(2)) - .BDDfy(); - } - - [Fact] - public void should_return_provider_with_no_delegates() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheServiceProviderReturnsNothing()) - .When(x => WhenIGet()) - .Then(x => ThenNoDelegatesAreInTheProvider()) - .BDDfy(); - } - - [Fact] - public void should_return_provider_with_qos_delegate() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) - .And(x => GivenTheServiceProviderReturnsNothing()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(1)) - .And(x => ThenItIsQosHandler(0)) - .BDDfy(); - } - - [Fact] - public void should_return_provider_with_qos_delegate_when_timeout_value_set() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) - .And(x => GivenTheServiceProviderReturnsNothing()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(1)) - .And(x => ThenItIsQosHandler(0)) - .BDDfy(); - } - - [Fact] - public void should_log_error_and_return_no_qos_provider_delegate_when_qos_factory_returns_error() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) - .WithLoadBalancerKey("") - .Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturnsError()) - .And(x => GivenTheTracingFactoryReturns()) - .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) - .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(4)) - .And(x => ThenHandlerAtPositionIs(0)) - .And(x => ThenHandlerAtPositionIs(1)) - .And(x => ThenHandlerAtPositionIs(2)) - .And(x => ThenHandlerAtPositionIs(3)) - .And(_ => ThenTheWarningIsLogged()) - .BDDfy(); - } - - [Fact] - public void should_log_error_and_return_no_qos_provider_delegate_when_qos_factory_returns_null() - { - var qosOptions = new QoSOptionsBuilder() - .WithTimeoutValue(1) - .WithDurationOfBreak(1) - .WithExceptionsAllowedBeforeBreaking(1) - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) - .WithLoadBalancerKey("") - .Build(); - - this.Given(x => GivenTheFollowingRequest(reRoute)) - .And(x => GivenTheQosFactoryReturnsNull()) - .And(x => GivenTheTracingFactoryReturns()) - .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) - .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) - .When(x => WhenIGet()) - .Then(x => ThenThereIsDelegatesInProvider(4)) - .And(x => ThenHandlerAtPositionIs(0)) - .And(x => ThenHandlerAtPositionIs(1)) - .And(x => ThenHandlerAtPositionIs(2)) - .And(x => ThenHandlerAtPositionIs(3)) - .And(_ => ThenTheWarningIsLogged()) - .BDDfy(); - } - - private void ThenTheWarningIsLogged() - { - _logger.Verify(x => x.LogWarning($"ReRoute {_downstreamReRoute.UpstreamPathTemplate} specifies use QoS but no QosHandler found in DI container. Will use not use a QosHandler, please check your setup!"), Times.Once); - } - - private void ThenHandlerAtPositionIs(int pos) - where T : DelegatingHandler - { - var delegates = _result.Data; - var del = delegates[pos].Invoke(); - del.ShouldBeOfType(); - } - - private void GivenTheTracingFactoryReturns() - { - _tracingFactory - .Setup(x => x.Get()) - .Returns(new FakeTracingHandler()); - } - - private void GivenTheServiceProviderReturnsGlobalDelegatingHandlers() - where TOne : DelegatingHandler - where TTwo : DelegatingHandler - { - _services.AddTransient(); - _services.AddTransient(s => - { - var service = s.GetService(); - return new GlobalDelegatingHandler(service); - }); - _services.AddTransient(); - _services.AddTransient(s => - { - var service = s.GetService(); - return new GlobalDelegatingHandler(service); - }); - } - - private void GivenTheServiceProviderReturnsSpecificDelegatingHandlers() - where TOne : DelegatingHandler - where TTwo : DelegatingHandler - { - _services.AddTransient(); - _services.AddTransient(); - } - - private void GivenTheServiceProviderReturnsNothing() - { - _serviceProvider = _services.BuildServiceProvider(); - } - - private void ThenAnErrorIsReturned() - { - _result.IsError.ShouldBeTrue(); - } - - private void ThenTheDelegatesAreAddedCorrectly() - { - var delegates = _result.Data; - - var del = delegates[0].Invoke(); - var handler = (FakeDelegatingHandler)del; - handler.Order.ShouldBe(1); - - del = delegates[1].Invoke(); - var handlerTwo = (FakeDelegatingHandlerTwo)del; - handlerTwo.Order.ShouldBe(2); - } - - private void GivenTheQosFactoryReturns(DelegatingHandler handler) - { - _qosFactory - .Setup(x => x.Get(It.IsAny())) - .Returns(new OkResponse(handler)); - } - - private void GivenTheQosFactoryReturnsError() - { - _qosFactory - .Setup(x => x.Get(It.IsAny())) - .Returns(new ErrorResponse(new AnyError())); - } - - private void GivenTheQosFactoryReturnsNull() - { - _qosFactory - .Setup(x => x.Get(It.IsAny())) - .Returns((ErrorResponse)null); - } - - private void ThenItIsQosHandler(int i) - { - var delegates = _result.Data; - var del = delegates[i].Invoke(); - del.ShouldBeOfType(); - } - - private void ThenThereIsDelegatesInProvider(int count) - { - _result.ShouldNotBeNull(); - _result.Data.Count.ShouldBe(count); - } - - private void GivenTheFollowingRequest(DownstreamReRoute request) - { - _downstreamReRoute = request; - } - - private void WhenIGet() - { - _serviceProvider = _services.BuildServiceProvider(); - _factory = new DelegatingHandlerHandlerFactory(_tracingFactory.Object, _qosFactory.Object, _serviceProvider, _loggerFactory.Object); - _result = _factory.Get(_downstreamReRoute); - } - - private void ThenNoDelegatesAreInTheProvider() - { - _result.ShouldNotBeNull(); - _result.Data.Count.ShouldBe(0); - } - } - - internal class FakeTracingHandler : DelegatingHandler, ITracingHandler - { - } - - internal class FakeQoSHandler : DelegatingHandler - { - } -} +namespace Ocelot.UnitTests.Requester +{ + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Logging; + using Ocelot.Requester; + using Ocelot.Requester.QoS; + using Ocelot.Responses; + using Responder; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Net.Http; + using TestStack.BDDfy; + using Xunit; + + public class DelegatingHandlerHandlerProviderFactoryTests + { + private DelegatingHandlerHandlerFactory _factory; + private readonly Mock _loggerFactory; + private readonly Mock _logger; + private DownstreamRoute _downstreamRoute; + private Response>> _result; + private readonly Mock _qosFactory; + private readonly Mock _tracingFactory; + private IServiceProvider _serviceProvider; + private readonly IServiceCollection _services; + private readonly QosDelegatingHandlerDelegate _qosDelegate; + + public DelegatingHandlerHandlerProviderFactoryTests() + { + _qosDelegate = (a, b) => new FakeQoSHandler(); + _tracingFactory = new Mock(); + _qosFactory = new Mock(); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _services = new ServiceCollection(); + _services.AddSingleton(_qosDelegate); + } + + [Fact] + public void should_follow_ordering_add_specifics() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) + .WithDelegatingHandlers(new List + { + "FakeDelegatingHandler", + "FakeDelegatingHandlerTwo" + }) + .WithLoadBalancerKey("") + .Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) + .And(x => GivenTheTracingFactoryReturns()) + .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) + .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(6)) + .And(x => ThenHandlerAtPositionIs(0)) + .And(x => ThenHandlerAtPositionIs(1)) + .And(x => ThenHandlerAtPositionIs(2)) + .And(x => ThenHandlerAtPositionIs(3)) + .And(x => ThenHandlerAtPositionIs(4)) + .And(x => ThenHandlerAtPositionIs(5)) + .BDDfy(); + } + + [Fact] + public void should_follow_ordering_order_specifics_and_globals() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) + .WithDelegatingHandlers(new List + { + "FakeDelegatingHandlerTwo", + "FakeDelegatingHandler", + "FakeDelegatingHandlerFour" + }) + .WithLoadBalancerKey("") + .Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) + .And(x => GivenTheTracingFactoryReturns()) + .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) + .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(6)) + .And(x => ThenHandlerAtPositionIs(0)) //first because global not in config + .And(x => ThenHandlerAtPositionIs(1)) //first from config + .And(x => ThenHandlerAtPositionIs(2)) //second from config + .And(x => ThenHandlerAtPositionIs(3)) //third from config (global) + .And(x => ThenHandlerAtPositionIs(4)) + .And(x => ThenHandlerAtPositionIs(5)) + .BDDfy(); + } + + [Fact] + public void should_follow_ordering_order_specifics() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) + .WithDelegatingHandlers(new List + { + "FakeDelegatingHandlerTwo", + "FakeDelegatingHandler" + }) + .WithLoadBalancerKey("") + .Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) + .And(x => GivenTheTracingFactoryReturns()) + .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) + .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(6)) + .And(x => ThenHandlerAtPositionIs(0)) + .And(x => ThenHandlerAtPositionIs(1)) + .And(x => ThenHandlerAtPositionIs(2)) + .And(x => ThenHandlerAtPositionIs(3)) + .And(x => ThenHandlerAtPositionIs(4)) + .And(x => ThenHandlerAtPositionIs(5)) + .BDDfy(); + } + + [Fact] + public void should_follow_ordering_order_and_only_add_specifics_in_config() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) + .WithDelegatingHandlers(new List + { + "FakeDelegatingHandler", + }) + .WithLoadBalancerKey("") + .Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) + .And(x => GivenTheTracingFactoryReturns()) + .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) + .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(5)) + .And(x => ThenHandlerAtPositionIs(0)) + .And(x => ThenHandlerAtPositionIs(1)) + .And(x => ThenHandlerAtPositionIs(2)) + .And(x => ThenHandlerAtPositionIs(3)) + .And(x => ThenHandlerAtPositionIs(4)) + .BDDfy(); + } + + [Fact] + public void should_follow_ordering_dont_add_specifics() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) + .WithLoadBalancerKey("") + .Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) + .And(x => GivenTheTracingFactoryReturns()) + .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) + .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(4)) + .And(x => ThenHandlerAtPositionIs(0)) + .And(x => ThenHandlerAtPositionIs(1)) + .And(x => ThenHandlerAtPositionIs(2)) + .And(x => ThenHandlerAtPositionIs(3)) + .BDDfy(); + } + + [Fact] + public void should_apply_re_route_specific() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)) + .WithDelegatingHandlers(new List + { + "FakeDelegatingHandler", + "FakeDelegatingHandlerTwo" + }) + .WithLoadBalancerKey("") + .Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(2)) + .And(x => ThenTheDelegatesAreAddedCorrectly()) + .BDDfy(); + } + + [Fact] + public void should_all_from_all_routes_provider_and_qos() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) + .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(3)) + .And(x => ThenTheDelegatesAreAddedCorrectly()) + .And(x => ThenItIsQosHandler(2)) + .BDDfy(); + } + + [Fact] + public void should_return_provider_with_no_delegates() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheServiceProviderReturnsNothing()) + .When(x => WhenIGet()) + .Then(x => ThenNoDelegatesAreInTheProvider()) + .BDDfy(); + } + + [Fact] + public void should_return_provider_with_qos_delegate() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) + .And(x => GivenTheServiceProviderReturnsNothing()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(1)) + .And(x => ThenItIsQosHandler(0)) + .BDDfy(); + } + + [Fact] + public void should_return_provider_with_qos_delegate_when_timeout_value_set() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false, true, int.MaxValue)).WithLoadBalancerKey("").Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturns(new FakeQoSHandler())) + .And(x => GivenTheServiceProviderReturnsNothing()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(1)) + .And(x => ThenItIsQosHandler(0)) + .BDDfy(); + } + + [Fact] + public void should_log_error_and_return_no_qos_provider_delegate_when_qos_factory_returns_error() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) + .WithLoadBalancerKey("") + .Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturnsError()) + .And(x => GivenTheTracingFactoryReturns()) + .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) + .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(4)) + .And(x => ThenHandlerAtPositionIs(0)) + .And(x => ThenHandlerAtPositionIs(1)) + .And(x => ThenHandlerAtPositionIs(2)) + .And(x => ThenHandlerAtPositionIs(3)) + .And(_ => ThenTheWarningIsLogged()) + .BDDfy(); + } + + [Fact] + public void should_log_error_and_return_no_qos_provider_delegate_when_qos_factory_returns_null() + { + var qosOptions = new QoSOptionsBuilder() + .WithTimeoutValue(1) + .WithDurationOfBreak(1) + .WithExceptionsAllowedBeforeBreaking(1) + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true, true, int.MaxValue)) + .WithLoadBalancerKey("") + .Build(); + + this.Given(x => GivenTheFollowingRequest(route)) + .And(x => GivenTheQosFactoryReturnsNull()) + .And(x => GivenTheTracingFactoryReturns()) + .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers()) + .And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers()) + .When(x => WhenIGet()) + .Then(x => ThenThereIsDelegatesInProvider(4)) + .And(x => ThenHandlerAtPositionIs(0)) + .And(x => ThenHandlerAtPositionIs(1)) + .And(x => ThenHandlerAtPositionIs(2)) + .And(x => ThenHandlerAtPositionIs(3)) + .And(_ => ThenTheWarningIsLogged()) + .BDDfy(); + } + + private void ThenTheWarningIsLogged() + { + _logger.Verify(x => x.LogWarning($"Route {_downstreamRoute.UpstreamPathTemplate} specifies use QoS but no QosHandler found in DI container. Will use not use a QosHandler, please check your setup!"), Times.Once); + } + + private void ThenHandlerAtPositionIs(int pos) + where T : DelegatingHandler + { + var delegates = _result.Data; + var del = delegates[pos].Invoke(); + del.ShouldBeOfType(); + } + + private void GivenTheTracingFactoryReturns() + { + _tracingFactory + .Setup(x => x.Get()) + .Returns(new FakeTracingHandler()); + } + + private void GivenTheServiceProviderReturnsGlobalDelegatingHandlers() + where TOne : DelegatingHandler + where TTwo : DelegatingHandler + { + _services.AddTransient(); + _services.AddTransient(s => + { + var service = s.GetService(); + return new GlobalDelegatingHandler(service); + }); + _services.AddTransient(); + _services.AddTransient(s => + { + var service = s.GetService(); + return new GlobalDelegatingHandler(service); + }); + } + + private void GivenTheServiceProviderReturnsSpecificDelegatingHandlers() + where TOne : DelegatingHandler + where TTwo : DelegatingHandler + { + _services.AddTransient(); + _services.AddTransient(); + } + + private void GivenTheServiceProviderReturnsNothing() + { + _serviceProvider = _services.BuildServiceProvider(); + } + + private void ThenAnErrorIsReturned() + { + _result.IsError.ShouldBeTrue(); + } + + private void ThenTheDelegatesAreAddedCorrectly() + { + var delegates = _result.Data; + + var del = delegates[0].Invoke(); + var handler = (FakeDelegatingHandler)del; + handler.Order.ShouldBe(1); + + del = delegates[1].Invoke(); + var handlerTwo = (FakeDelegatingHandlerTwo)del; + handlerTwo.Order.ShouldBe(2); + } + + private void GivenTheQosFactoryReturns(DelegatingHandler handler) + { + _qosFactory + .Setup(x => x.Get(It.IsAny())) + .Returns(new OkResponse(handler)); + } + + private void GivenTheQosFactoryReturnsError() + { + _qosFactory + .Setup(x => x.Get(It.IsAny())) + .Returns(new ErrorResponse(new AnyError())); + } + + private void GivenTheQosFactoryReturnsNull() + { + _qosFactory + .Setup(x => x.Get(It.IsAny())) + .Returns((ErrorResponse)null); + } + + private void ThenItIsQosHandler(int i) + { + var delegates = _result.Data; + var del = delegates[i].Invoke(); + del.ShouldBeOfType(); + } + + private void ThenThereIsDelegatesInProvider(int count) + { + _result.ShouldNotBeNull(); + _result.Data.Count.ShouldBe(count); + } + + private void GivenTheFollowingRequest(DownstreamRoute request) + { + _downstreamRoute = request; + } + + private void WhenIGet() + { + _serviceProvider = _services.BuildServiceProvider(); + _factory = new DelegatingHandlerHandlerFactory(_tracingFactory.Object, _qosFactory.Object, _serviceProvider, _loggerFactory.Object); + _result = _factory.Get(_downstreamRoute); + } + + private void ThenNoDelegatesAreInTheProvider() + { + _result.ShouldNotBeNull(); + _result.Data.Count.ShouldBe(0); + } + } + + internal class FakeTracingHandler : DelegatingHandler, ITracingHandler + { + } + + internal class FakeQoSHandler : DelegatingHandler + { + } +} diff --git a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs index 616568388..5d2a68260 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs @@ -1,446 +1,446 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Moq; -using Ocelot.Configuration; -using Ocelot.Configuration.Builder; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; using Ocelot.DownstreamRouteFinder.Middleware; -using Ocelot.Logging; -using Ocelot.Middleware; -using Ocelot.Request.Middleware; -using Ocelot.Requester; -using Ocelot.Responses; -using Shouldly; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Requester -{ - public class HttpClientBuilderTests : IDisposable - { - private HttpClientBuilder _builder; - private readonly Mock _factory; - private IHttpClient _httpClient; - private HttpResponseMessage _response; - private HttpContext _context; - private readonly Mock _cacheHandlers; - private readonly Mock _logger; - private int _count; - private IWebHost _host; - private IHttpClient _againHttpClient; - private IHttpClient _firstHttpClient; - private MemoryHttpClientCache _realCache; - - public HttpClientBuilderTests() - { - _cacheHandlers = new Mock(); - _logger = new Mock(); - _factory = new Mock(); - _builder = new HttpClientBuilder(_factory.Object, _cacheHandlers.Object, _logger.Object); - } - - [Fact] - public void should_build_http_client() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - this.Given(x => GivenTheFactoryReturns()) - .And(x => GivenARequest(reRoute)) - .When(x => WhenIBuild()) - .Then(x => ThenTheHttpClientShouldNotBeNull()) - .BDDfy(); - } - - [Fact] - public void should_get_from_cache() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - this.Given(x => GivenARealCache()) - .And(x => GivenTheFactoryReturns()) - .And(x => GivenARequest(reRoute)) - .And(x => WhenIBuildTheFirstTime()) - .And(x => WhenISave()) - .And(x => WhenIBuildAgain()) - .And(x => WhenISave()) - .When(x => WhenIBuildAgain()) - .Then(x => ThenTheHttpClientIsFromTheCache()) - .BDDfy(); - } - - [Fact] - public void should_get_from_cache_with_different_query_string() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - this.Given(x => GivenARealCache()) - .And(x => GivenTheFactoryReturns()) - .And(x => GivenARequest(reRoute, "http://wwww.someawesomewebsite.com/woot?badman=1")) - .And(x => WhenIBuildTheFirstTime()) - .And(x => WhenISave()) - .And(x => WhenIBuildAgain()) - .And(x => GivenARequest(reRoute, "http://wwww.someawesomewebsite.com/woot?badman=2")) - .And(x => WhenISave()) - .When(x => WhenIBuildAgain()) - .Then(x => ThenTheHttpClientIsFromTheCache()) - .BDDfy(); - } - - [Fact] - public void should_not_get_from_cache_with_different_query_string() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRouteA = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - var reRouteB = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - this.Given(x => GivenARealCache()) - .And(x => GivenTheFactoryReturns()) - .And(x => GivenARequest(reRouteA, "http://wwww.someawesomewebsite.com/woot?badman=1")) - .And(x => WhenIBuildTheFirstTime()) - .And(x => WhenISave()) - .And(x => WhenIBuildAgain()) - .And(x => GivenARequest(reRouteB, "http://wwww.someawesomewebsite.com/woot?badman=2")) - .And(x => WhenISave()) - .When(x => WhenIBuildAgain()) - .Then(x => ThenTheHttpClientIsNotFromTheCache()) - .BDDfy(); - } - - [Fact] - public void should_log_if_ignoring_ssl_errors() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .WithDangerousAcceptAnyServerCertificateValidator(true) - .Build(); - - this.Given(x => GivenTheFactoryReturns()) - .And(x => GivenARequest(reRoute)) - .When(x => WhenIBuild()) - .Then(x => ThenTheHttpClientShouldNotBeNull()) - .Then(x => ThenTheDangerousAcceptAnyServerCertificateValidatorWarningIsLogged()) - .BDDfy(); - } - - [Fact] - public void should_call_delegating_handlers_in_order() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - var fakeOne = new FakeDelegatingHandler(); - var fakeTwo = new FakeDelegatingHandler(); - - var handlers = new List>() - { - () => fakeOne, - () => fakeTwo - }; - - this.Given(x => GivenTheFactoryReturns(handlers)) - .And(x => GivenARequest(reRoute)) - .And(x => WhenIBuild()) - .When(x => WhenICallTheClient()) - .Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo)) - .And(x => ThenSomethingIsReturned()) - .BDDfy(); - } - - [Fact] - public void should_re_use_cookies_from_container() - { - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - this.Given(_ => GivenADownstreamService()) - .And(_ => GivenARequest(reRoute)) - .And(_ => GivenTheFactoryReturnsNothing()) - .And(_ => WhenIBuild()) - .And(_ => WhenICallTheClient("http://localhost:5003")) - .And(_ => ThenTheCookieIsSet()) - .And(_ => GivenTheClientIsCached()) - .And(_ => WhenIBuild()) - .When(_ => WhenICallTheClient("http://localhost:5003")) - .Then(_ => ThenTheResponseIsOk()) - .BDDfy(); - } - - [Theory] - [InlineData("GET")] - [InlineData("POST")] - [InlineData("PUT")] - [InlineData("DELETE")] - [InlineData("PATCH")] - public void should_add_verb_to_cache_key(string verb) - { - var downstreamUrl = "http://localhost:5012/"; - - var method = new HttpMethod(verb); - - var qosOptions = new QoSOptionsBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder() - .WithQosOptions(qosOptions) - .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) - .WithLoadBalancerKey("") - .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) - .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - this.Given(_ => GivenADownstreamService()) - .And(_ => GivenARequestWithAUrlAndMethod(reRoute, downstreamUrl, method)) - .And(_ => GivenTheFactoryReturnsNothing()) - .And(_ => WhenIBuild()) - .And(_ => GivenCacheIsCalledWithExpectedKey($"{method.ToString()}:{downstreamUrl}")) - .BDDfy(); - } - - private void GivenARealCache() - { - _realCache = new MemoryHttpClientCache(); - _builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object); - } - - private void ThenTheHttpClientIsFromTheCache() - { - _againHttpClient.ShouldBe(_firstHttpClient); - } - - private void ThenTheHttpClientIsNotFromTheCache() - { - _againHttpClient.ShouldNotBe(_firstHttpClient); - } - - private void WhenISave() - { - _builder.Save(); - } - - private void GivenCacheIsCalledWithExpectedKey(string expectedKey) - { - _cacheHandlers.Verify(x => x.Get(It.IsAny()), Times.Once); - } - - private void ThenTheDangerousAcceptAnyServerCertificateValidatorWarningIsLogged() - { - _logger.Verify(x => x.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {_context.Items.DownstreamReRoute().UpstreamPathTemplate}, DownstreamPathTemplate: {_context.Items.DownstreamReRoute().DownstreamPathTemplate}"), Times.Once); - } - - private void GivenTheClientIsCached() - { - _cacheHandlers.Setup(x => x.Get(It.IsAny())).Returns(_httpClient); - } - - private void ThenTheCookieIsSet() - { - _response.Headers.TryGetValues("Set-Cookie", out var test).ShouldBeTrue(); - } - - private void WhenICallTheClient(string url) - { - _response = _httpClient - .SendAsync(new HttpRequestMessage(HttpMethod.Get, url)) - .GetAwaiter() - .GetResult(); - } - - private void ThenTheResponseIsOk() - { - _response.StatusCode.ShouldBe(HttpStatusCode.OK); - } - - private void GivenADownstreamService() - { - _host = new WebHostBuilder() - .UseUrls("http://localhost:5003") - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => - { - app.Run(context => - { - if (_count == 0) - { - context.Response.Cookies.Append("test", "0"); - context.Response.StatusCode = 200; - _count++; - return Task.CompletedTask; - } - - if (_count == 1) - { - if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue)) - { - context.Response.StatusCode = 200; - return Task.CompletedTask; - } - - context.Response.StatusCode = 500; - } - - return Task.CompletedTask; - }); - }) - .Build(); - - _host.Start(); - } - - private void GivenARequest(DownstreamReRoute downstream) - { - GivenARequest(downstream, "http://localhost:5003"); - } - - private void GivenARequest(DownstreamReRoute downstream, string downstreamUrl) - { - GivenARequestWithAUrlAndMethod(downstream, downstreamUrl, HttpMethod.Get); - } - - private void GivenARequestWithAUrlAndMethod(DownstreamReRoute downstream, string url, HttpMethod method) - { +using Ocelot.Logging; +using Ocelot.Middleware; +using Ocelot.Request.Middleware; +using Ocelot.Requester; +using Ocelot.Responses; +using Shouldly; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.Requester +{ + public class HttpClientBuilderTests : IDisposable + { + private HttpClientBuilder _builder; + private readonly Mock _factory; + private IHttpClient _httpClient; + private HttpResponseMessage _response; + private HttpContext _context; + private readonly Mock _cacheHandlers; + private readonly Mock _logger; + private int _count; + private IWebHost _host; + private IHttpClient _againHttpClient; + private IHttpClient _firstHttpClient; + private MemoryHttpClientCache _realCache; + + public HttpClientBuilderTests() + { + _cacheHandlers = new Mock(); + _logger = new Mock(); + _factory = new Mock(); + _builder = new HttpClientBuilder(_factory.Object, _cacheHandlers.Object, _logger.Object); + } + + [Fact] + public void should_build_http_client() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .Build(); + + this.Given(x => GivenTheFactoryReturns()) + .And(x => GivenARequest(route)) + .When(x => WhenIBuild()) + .Then(x => ThenTheHttpClientShouldNotBeNull()) + .BDDfy(); + } + + [Fact] + public void should_get_from_cache() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .Build(); + + this.Given(x => GivenARealCache()) + .And(x => GivenTheFactoryReturns()) + .And(x => GivenARequest(route)) + .And(x => WhenIBuildTheFirstTime()) + .And(x => WhenISave()) + .And(x => WhenIBuildAgain()) + .And(x => WhenISave()) + .When(x => WhenIBuildAgain()) + .Then(x => ThenTheHttpClientIsFromTheCache()) + .BDDfy(); + } + + [Fact] + public void should_get_from_cache_with_different_query_string() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .Build(); + + this.Given(x => GivenARealCache()) + .And(x => GivenTheFactoryReturns()) + .And(x => GivenARequest(route, "http://wwww.someawesomewebsite.com/woot?badman=1")) + .And(x => WhenIBuildTheFirstTime()) + .And(x => WhenISave()) + .And(x => WhenIBuildAgain()) + .And(x => GivenARequest(route, "http://wwww.someawesomewebsite.com/woot?badman=2")) + .And(x => WhenISave()) + .When(x => WhenIBuildAgain()) + .Then(x => ThenTheHttpClientIsFromTheCache()) + .BDDfy(); + } + + [Fact] + public void should_not_get_from_cache_with_different_query_string() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var routeA = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .Build(); + + var routeB = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithContainsQueryString(true).WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .Build(); + + this.Given(x => GivenARealCache()) + .And(x => GivenTheFactoryReturns()) + .And(x => GivenARequest(routeA, "http://wwww.someawesomewebsite.com/woot?badman=1")) + .And(x => WhenIBuildTheFirstTime()) + .And(x => WhenISave()) + .And(x => WhenIBuildAgain()) + .And(x => GivenARequest(routeB, "http://wwww.someawesomewebsite.com/woot?badman=2")) + .And(x => WhenISave()) + .When(x => WhenIBuildAgain()) + .Then(x => ThenTheHttpClientIsNotFromTheCache()) + .BDDfy(); + } + + [Fact] + public void should_log_if_ignoring_ssl_errors() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .WithDangerousAcceptAnyServerCertificateValidator(true) + .Build(); + + this.Given(x => GivenTheFactoryReturns()) + .And(x => GivenARequest(route)) + .When(x => WhenIBuild()) + .Then(x => ThenTheHttpClientShouldNotBeNull()) + .Then(x => ThenTheDangerousAcceptAnyServerCertificateValidatorWarningIsLogged()) + .BDDfy(); + } + + [Fact] + public void should_call_delegating_handlers_in_order() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .Build(); + + var fakeOne = new FakeDelegatingHandler(); + var fakeTwo = new FakeDelegatingHandler(); + + var handlers = new List>() + { + () => fakeOne, + () => fakeTwo + }; + + this.Given(x => GivenTheFactoryReturns(handlers)) + .And(x => GivenARequest(route)) + .And(x => WhenIBuild()) + .When(x => WhenICallTheClient()) + .Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo)) + .And(x => ThenSomethingIsReturned()) + .BDDfy(); + } + + [Fact] + public void should_re_use_cookies_from_container() + { + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, true, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .Build(); + + this.Given(_ => GivenADownstreamService()) + .And(_ => GivenARequest(route)) + .And(_ => GivenTheFactoryReturnsNothing()) + .And(_ => WhenIBuild()) + .And(_ => WhenICallTheClient("http://localhost:5003")) + .And(_ => ThenTheCookieIsSet()) + .And(_ => GivenTheClientIsCached()) + .And(_ => WhenIBuild()) + .When(_ => WhenICallTheClient("http://localhost:5003")) + .Then(_ => ThenTheResponseIsOk()) + .BDDfy(); + } + + [Theory] + [InlineData("GET")] + [InlineData("POST")] + [InlineData("PUT")] + [InlineData("DELETE")] + [InlineData("PATCH")] + public void should_add_verb_to_cache_key(string verb) + { + var downstreamUrl = "http://localhost:5012/"; + + var method = new HttpMethod(verb); + + var qosOptions = new QoSOptionsBuilder() + .Build(); + + var route = new DownstreamRouteBuilder() + .WithQosOptions(qosOptions) + .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) + .WithLoadBalancerKey("") + .WithUpstreamPathTemplate(new UpstreamPathTemplateBuilder().WithOriginalValue("").Build()) + .WithQosOptions(new QoSOptionsBuilder().Build()) + .Build(); + + this.Given(_ => GivenADownstreamService()) + .And(_ => GivenARequestWithAUrlAndMethod(route, downstreamUrl, method)) + .And(_ => GivenTheFactoryReturnsNothing()) + .And(_ => WhenIBuild()) + .And(_ => GivenCacheIsCalledWithExpectedKey($"{method.ToString()}:{downstreamUrl}")) + .BDDfy(); + } + + private void GivenARealCache() + { + _realCache = new MemoryHttpClientCache(); + _builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object); + } + + private void ThenTheHttpClientIsFromTheCache() + { + _againHttpClient.ShouldBe(_firstHttpClient); + } + + private void ThenTheHttpClientIsNotFromTheCache() + { + _againHttpClient.ShouldNotBe(_firstHttpClient); + } + + private void WhenISave() + { + _builder.Save(); + } + + private void GivenCacheIsCalledWithExpectedKey(string expectedKey) + { + _cacheHandlers.Verify(x => x.Get(It.IsAny()), Times.Once); + } + + private void ThenTheDangerousAcceptAnyServerCertificateValidatorWarningIsLogged() + { + _logger.Verify(x => x.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamRoute, UpstreamPathTemplate: {_context.Items.DownstreamRoute().UpstreamPathTemplate}, DownstreamPathTemplate: {_context.Items.DownstreamRoute().DownstreamPathTemplate}"), Times.Once); + } + + private void GivenTheClientIsCached() + { + _cacheHandlers.Setup(x => x.Get(It.IsAny())).Returns(_httpClient); + } + + private void ThenTheCookieIsSet() + { + _response.Headers.TryGetValues("Set-Cookie", out var test).ShouldBeTrue(); + } + + private void WhenICallTheClient(string url) + { + _response = _httpClient + .SendAsync(new HttpRequestMessage(HttpMethod.Get, url)) + .GetAwaiter() + .GetResult(); + } + + private void ThenTheResponseIsOk() + { + _response.StatusCode.ShouldBe(HttpStatusCode.OK); + } + + private void GivenADownstreamService() + { + _host = new WebHostBuilder() + .UseUrls("http://localhost:5003") + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => + { + app.Run(context => + { + if (_count == 0) + { + context.Response.Cookies.Append("test", "0"); + context.Response.StatusCode = 200; + _count++; + return Task.CompletedTask; + } + + if (_count == 1) + { + if (context.Request.Cookies.TryGetValue("test", out var cookieValue) || context.Request.Headers.TryGetValue("Set-Cookie", out var headerValue)) + { + context.Response.StatusCode = 200; + return Task.CompletedTask; + } + + context.Response.StatusCode = 500; + } + + return Task.CompletedTask; + }); + }) + .Build(); + + _host.Start(); + } + + private void GivenARequest(DownstreamRoute downstream) + { + GivenARequest(downstream, "http://localhost:5003"); + } + + private void GivenARequest(DownstreamRoute downstream, string downstreamUrl) + { + GivenARequestWithAUrlAndMethod(downstream, downstreamUrl, HttpMethod.Get); + } + + private void GivenARequestWithAUrlAndMethod(DownstreamRoute downstream, string url, HttpMethod method) + { _context = new DefaultHttpContext(); - _context.Items.UpsertDownstreamReRoute(downstream); - _context.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri(url), Method = method })); - } - - private void ThenSomethingIsReturned() - { - _response.ShouldNotBeNull(); - } - - private void WhenICallTheClient() - { - _response = _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://test.com")).GetAwaiter().GetResult(); - } - - private void ThenTheFakeAreHandledInOrder(FakeDelegatingHandler fakeOne, FakeDelegatingHandler fakeTwo) - { - fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled); - } - - private void GivenTheFactoryReturns() - { - var handlers = new List>() { () => new FakeDelegatingHandler() }; - - _factory - .Setup(x => x.Get(It.IsAny())) - .Returns(new OkResponse>>(handlers)); - } - - private void GivenTheFactoryReturnsNothing() - { - var handlers = new List>(); - - _factory - .Setup(x => x.Get(It.IsAny())) - .Returns(new OkResponse>>(handlers)); - } - - private void GivenTheFactoryReturns(List> handlers) - { - _factory - .Setup(x => x.Get(It.IsAny())) - .Returns(new OkResponse>>(handlers)); - } - - private void WhenIBuild() - { - _httpClient = _builder.Create(_context.Items.DownstreamReRoute()); - } - - private void WhenIBuildTheFirstTime() - { - _firstHttpClient = _builder.Create(_context.Items.DownstreamReRoute()); - } - - private void WhenIBuildAgain() - { - _builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object); - _againHttpClient = _builder.Create(_context.Items.DownstreamReRoute()); - } - - private void ThenTheHttpClientShouldNotBeNull() - { - _httpClient.ShouldNotBeNull(); - } - - public void Dispose() - { - _response?.Dispose(); - _host?.Dispose(); - } - } -} + _context.Items.UpsertDownstreamRoute(downstream); + _context.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri(url), Method = method })); + } + + private void ThenSomethingIsReturned() + { + _response.ShouldNotBeNull(); + } + + private void WhenICallTheClient() + { + _response = _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://test.com")).GetAwaiter().GetResult(); + } + + private void ThenTheFakeAreHandledInOrder(FakeDelegatingHandler fakeOne, FakeDelegatingHandler fakeTwo) + { + fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled); + } + + private void GivenTheFactoryReturns() + { + var handlers = new List>() { () => new FakeDelegatingHandler() }; + + _factory + .Setup(x => x.Get(It.IsAny())) + .Returns(new OkResponse>>(handlers)); + } + + private void GivenTheFactoryReturnsNothing() + { + var handlers = new List>(); + + _factory + .Setup(x => x.Get(It.IsAny())) + .Returns(new OkResponse>>(handlers)); + } + + private void GivenTheFactoryReturns(List> handlers) + { + _factory + .Setup(x => x.Get(It.IsAny())) + .Returns(new OkResponse>>(handlers)); + } + + private void WhenIBuild() + { + _httpClient = _builder.Create(_context.Items.DownstreamRoute()); + } + + private void WhenIBuildTheFirstTime() + { + _firstHttpClient = _builder.Create(_context.Items.DownstreamRoute()); + } + + private void WhenIBuildAgain() + { + _builder = new HttpClientBuilder(_factory.Object, _realCache, _logger.Object); + _againHttpClient = _builder.Create(_context.Items.DownstreamRoute()); + } + + private void ThenTheHttpClientShouldNotBeNull() + { + _httpClient.ShouldNotBeNull(); + } + + public void Dispose() + { + _response?.Dispose(); + _host?.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs index 7912e3346..37cac0045 100644 --- a/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs +++ b/test/Ocelot.UnitTests/Requester/HttpClientHttpRequesterTest.cs @@ -2,7 +2,7 @@ using Moq; using Ocelot.Configuration; using Ocelot.Configuration.Builder; -using Ocelot.DownstreamRouteFinder.Middleware; +using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Logging; using Ocelot.Middleware; using Ocelot.Request.Middleware; @@ -34,7 +34,7 @@ public HttpClientHttpRequesterTest() { _httpContext = new DefaultHttpContext(); _factory = new Mock(); - _factory.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse>>(new List>())); + _factory.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse>>(new List>())); _logger = new Mock(); _loggerFactory = new Mock(); _loggerFactory @@ -57,18 +57,18 @@ public void should_call_request_correctly() var qosOptions = new QoSOptionsBuilder() .Build(); - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(upstreamTemplate) .WithQosOptions(new QoSOptionsBuilder().Build()) .Build(); - - var httpContext = new DefaultHttpContext(); - httpContext.Items.UpsertDownstreamReRoute(reRoute); - httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") })); - + + var httpContext = new DefaultHttpContext(); + httpContext.Items.UpsertDownstreamRoute(route); + httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") })); + this.Given(x => x.GivenTheRequestIs(httpContext)) .And(x => GivenTheHouseReturnsOkHandler()) .When(x => x.WhenIGetResponse()) @@ -84,17 +84,17 @@ public void should_call_request_unable_to_complete_request() var qosOptions = new QoSOptionsBuilder() .Build(); - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(upstreamTemplate) .WithQosOptions(new QoSOptionsBuilder().Build()) - .Build(); - - var httpContext = new DefaultHttpContext(); - httpContext.Items.UpsertDownstreamReRoute(reRoute); - httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") })); + .Build(); + + var httpContext = new DefaultHttpContext(); + httpContext.Items.UpsertDownstreamRoute(route); + httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") })); this.Given(x => x.GivenTheRequestIs(httpContext)) .When(x => x.WhenIGetResponse()) @@ -110,17 +110,17 @@ public void http_client_request_times_out() var qosOptions = new QoSOptionsBuilder() .Build(); - var reRoute = new DownstreamReRouteBuilder() + var route = new DownstreamRouteBuilder() .WithQosOptions(qosOptions) .WithHttpHandlerOptions(new HttpHandlerOptions(false, false, false, true, int.MaxValue)) .WithLoadBalancerKey("") .WithUpstreamPathTemplate(upstreamTemplate) .WithQosOptions(new QoSOptionsBuilder().WithTimeoutValue(1).Build()) - .Build(); - - var httpContext = new DefaultHttpContext(); - httpContext.Items.UpsertDownstreamReRoute(reRoute); - httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") })); + .Build(); + + var httpContext = new DefaultHttpContext(); + httpContext.Items.UpsertDownstreamRoute(route); + httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") })); this.Given(_ => GivenTheRequestIs(httpContext)) .And(_ => GivenTheHouseReturnsTimeoutHandler()) @@ -163,7 +163,7 @@ private void GivenTheHouseReturnsOkHandler() () => new OkDelegatingHandler() }; - _factory.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse>>(handlers)); + _factory.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse>>(handlers)); } private void GivenTheHouseReturnsTimeoutHandler() @@ -173,7 +173,7 @@ private void GivenTheHouseReturnsTimeoutHandler() () => new TimeoutDelegatingHandler() }; - _factory.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse>>(handlers)); + _factory.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse>>(handlers)); _mapper.Setup(x => x.Map(It.IsAny())).Returns(new UnableToCompleteRequestError(new Exception())); } diff --git a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs index 08242b691..4a045487f 100644 --- a/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Requester/HttpRequesterMiddlewareTests.cs @@ -17,9 +17,9 @@ namespace Ocelot.UnitTests.Requester using Ocelot.Configuration; using Ocelot.Infrastructure.RequestData; using TestStack.BDDfy; - using Xunit; - using Ocelot.DownstreamRouteFinder.Middleware; - + using Xunit; + using Ocelot.DownstreamRouteFinder.Middleware; + public class HttpRequesterMiddlewareTests { private readonly Mock _requester; @@ -84,8 +84,8 @@ private void WhenICallTheMiddleware() } private void GivenTheRequestIs() - { - _httpContext.Items.UpsertDownstreamReRoute(new DownstreamReRouteBuilder().Build()); + { + _httpContext.Items.UpsertDownstreamRoute(new DownstreamRouteBuilder().Build()); } private void GivenTheRequesterReturns(Response response) diff --git a/test/Ocelot.UnitTests/Requester/QoSFactoryTests.cs b/test/Ocelot.UnitTests/Requester/QoSFactoryTests.cs index 93c1c58ea..3e81c48c4 100644 --- a/test/Ocelot.UnitTests/Requester/QoSFactoryTests.cs +++ b/test/Ocelot.UnitTests/Requester/QoSFactoryTests.cs @@ -1,55 +1,55 @@ -namespace Ocelot.UnitTests.Requester -{ - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Logging; - using Ocelot.Requester; - using Ocelot.Requester.QoS; - using Shouldly; - using System.Net.Http; - using Xunit; - - public class QoSFactoryTests - { - private QoSFactory _factory; - private ServiceCollection _services; - private readonly Mock _loggerFactory; - - public QoSFactoryTests() - { - _services = new ServiceCollection(); - _loggerFactory = new Mock(); - var provider = _services.BuildServiceProvider(); - _factory = new QoSFactory(provider, _loggerFactory.Object); - } - - [Fact] - public void should_return_error() - { - var downstreamReRoute = new DownstreamReRouteBuilder().Build(); - var handler = _factory.Get(downstreamReRoute); - handler.IsError.ShouldBeTrue(); - handler.Errors[0].ShouldBeOfType(); - } - - [Fact] - public void should_return_handler() - { - _services = new ServiceCollection(); - DelegatingHandler QosDelegatingHandlerDelegate(DownstreamReRoute a, IOcelotLoggerFactory b) => new FakeDelegatingHandler(); - _services.AddSingleton(QosDelegatingHandlerDelegate); - var provider = _services.BuildServiceProvider(); - _factory = new QoSFactory(provider, _loggerFactory.Object); - var downstreamReRoute = new DownstreamReRouteBuilder().Build(); - var handler = _factory.Get(downstreamReRoute); - handler.IsError.ShouldBeFalse(); - handler.Data.ShouldBeOfType(); - } - - private class FakeDelegatingHandler : DelegatingHandler - { - } - } -} +namespace Ocelot.UnitTests.Requester +{ + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Logging; + using Ocelot.Requester; + using Ocelot.Requester.QoS; + using Shouldly; + using System.Net.Http; + using Xunit; + + public class QoSFactoryTests + { + private QoSFactory _factory; + private ServiceCollection _services; + private readonly Mock _loggerFactory; + + public QoSFactoryTests() + { + _services = new ServiceCollection(); + _loggerFactory = new Mock(); + var provider = _services.BuildServiceProvider(); + _factory = new QoSFactory(provider, _loggerFactory.Object); + } + + [Fact] + public void should_return_error() + { + var downstreamRoute = new DownstreamRouteBuilder().Build(); + var handler = _factory.Get(downstreamRoute); + handler.IsError.ShouldBeTrue(); + handler.Errors[0].ShouldBeOfType(); + } + + [Fact] + public void should_return_handler() + { + _services = new ServiceCollection(); + DelegatingHandler QosDelegatingHandlerDelegate(DownstreamRoute a, IOcelotLoggerFactory b) => new FakeDelegatingHandler(); + _services.AddSingleton(QosDelegatingHandlerDelegate); + var provider = _services.BuildServiceProvider(); + _factory = new QoSFactory(provider, _loggerFactory.Object); + var downstreamRoute = new DownstreamRouteBuilder().Build(); + var handler = _factory.Get(downstreamRoute); + handler.IsError.ShouldBeFalse(); + handler.Data.ShouldBeOfType(); + } + + private class FakeDelegatingHandler : DelegatingHandler + { + } + } +} diff --git a/test/Ocelot.UnitTests/Security/IPSecurityPolicyTests.cs b/test/Ocelot.UnitTests/Security/IPSecurityPolicyTests.cs index 21f4f33a1..b9c1a0655 100644 --- a/test/Ocelot.UnitTests/Security/IPSecurityPolicyTests.cs +++ b/test/Ocelot.UnitTests/Security/IPSecurityPolicyTests.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Http; using Ocelot.Configuration; using Ocelot.Configuration.Builder; - using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.Middleware; using Ocelot.Request.Middleware; using Ocelot.Responses; @@ -16,7 +15,7 @@ public class IPSecurityPolicyTests { - private readonly DownstreamReRouteBuilder _downstreamReRouteBuilder; + private readonly DownstreamRouteBuilder _downstreamRouteBuilder; private readonly IPSecurityPolicy _ipSecurityPolicy; private Response response; private HttpContext _httpContext; @@ -26,14 +25,14 @@ public IPSecurityPolicyTests() _httpContext = new DefaultHttpContext(); _httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"))); _httpContext.Connection.RemoteIpAddress = Dns.GetHostAddresses("192.168.1.1")[0]; - _downstreamReRouteBuilder = new DownstreamReRouteBuilder(); + _downstreamRouteBuilder = new DownstreamRouteBuilder(); _ipSecurityPolicy = new IPSecurityPolicy(); } [Fact] public void should_No_blocked_Ip_and_allowed_Ip() { - this.Given(x => x.GivenSetDownstreamReRoute()) + this.Given(x => x.GivenSetDownstreamRoute()) .When(x => x.WhenTheSecurityPolicy()) .Then(x => x.ThenSecurityPassing()) .BDDfy(); @@ -44,7 +43,7 @@ public void should_blockedIp_clientIp_block() { _httpContext.Connection.RemoteIpAddress = Dns.GetHostAddresses("192.168.1.1")[0]; this.Given(x => x.GivenSetBlockedIP()) - .Given(x => x.GivenSetDownstreamReRoute()) + .Given(x => x.GivenSetDownstreamRoute()) .When(x => x.WhenTheSecurityPolicy()) .Then(x => x.ThenNotSecurityPassing()) .BDDfy(); @@ -55,7 +54,7 @@ public void should_blockedIp_clientIp_Not_block() { _httpContext.Connection.RemoteIpAddress = Dns.GetHostAddresses("192.168.1.2")[0]; this.Given(x => x.GivenSetBlockedIP()) - .Given(x => x.GivenSetDownstreamReRoute()) + .Given(x => x.GivenSetDownstreamRoute()) .When(x => x.WhenTheSecurityPolicy()) .Then(x => x.ThenSecurityPassing()) .BDDfy(); @@ -66,7 +65,7 @@ public void should_allowedIp_clientIp_block() { _httpContext.Connection.RemoteIpAddress = Dns.GetHostAddresses("192.168.1.1")[0]; this.Given(x => x.GivenSetAllowedIP()) - .Given(x => x.GivenSetDownstreamReRoute()) + .Given(x => x.GivenSetDownstreamRoute()) .When(x => x.WhenTheSecurityPolicy()) .Then(x => x.ThenSecurityPassing()) .BDDfy(); @@ -77,7 +76,7 @@ public void should_allowedIp_clientIp_Not_block() { _httpContext.Connection.RemoteIpAddress = Dns.GetHostAddresses("192.168.1.2")[0]; this.Given(x => x.GivenSetAllowedIP()) - .Given(x => x.GivenSetDownstreamReRoute()) + .Given(x => x.GivenSetDownstreamRoute()) .When(x => x.WhenTheSecurityPolicy()) .Then(x => x.ThenNotSecurityPassing()) .BDDfy(); @@ -85,22 +84,22 @@ public void should_allowedIp_clientIp_Not_block() private void GivenSetAllowedIP() { - _downstreamReRouteBuilder.WithSecurityOptions(new SecurityOptions(new List { "192.168.1.1" }, new List())); + _downstreamRouteBuilder.WithSecurityOptions(new SecurityOptions(new List { "192.168.1.1" }, new List())); } private void GivenSetBlockedIP() { - _downstreamReRouteBuilder.WithSecurityOptions(new SecurityOptions(new List(), new List { "192.168.1.1" })); + _downstreamRouteBuilder.WithSecurityOptions(new SecurityOptions(new List(), new List { "192.168.1.1" })); } - private void GivenSetDownstreamReRoute() + private void GivenSetDownstreamRoute() { - _httpContext.Items.UpsertDownstreamReRoute(_downstreamReRouteBuilder.Build()); + _httpContext.Items.UpsertDownstreamRoute(_downstreamRouteBuilder.Build()); } private void WhenTheSecurityPolicy() { - response = _ipSecurityPolicy.Security(_httpContext.Items.DownstreamReRoute(), _httpContext).GetAwaiter().GetResult(); + response = _ipSecurityPolicy.Security(_httpContext.Items.DownstreamRoute(), _httpContext).GetAwaiter().GetResult(); } private void ThenSecurityPassing() diff --git a/test/Ocelot.UnitTests/Security/SecurityMiddlewareTests.cs b/test/Ocelot.UnitTests/Security/SecurityMiddlewareTests.cs index b329fbc44..5237c6cdf 100644 --- a/test/Ocelot.UnitTests/Security/SecurityMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Security/SecurityMiddlewareTests.cs @@ -15,11 +15,11 @@ using Xunit; namespace Ocelot.UnitTests.Security -{ - using Ocelot.DownstreamRouteFinder.Middleware; - using Ocelot.Infrastructure.RequestData; - using Shouldly; - +{ + using Ocelot.DownstreamRouteFinder.Middleware; + using Ocelot.Infrastructure.RequestData; + using Shouldly; + public class SecurityMiddlewareTests { private List> _securityPolicyList; @@ -42,7 +42,7 @@ public SecurityMiddlewareTests() { return Task.CompletedTask; }; - _middleware = new SecurityMiddleware(_next, _loggerFactory.Object, _securityPolicyList.Select(f => f.Object).ToList()); + _middleware = new SecurityMiddleware(_next, _loggerFactory.Object, _securityPolicyList.Select(f => f.Object).ToList()); _httpContext.Items.UpsertDownstreamRequest(new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"))); } @@ -69,7 +69,7 @@ private void GivenPassingSecurityVerification() foreach (var item in _securityPolicyList) { Response response = new OkResponse(); - item.Setup(x => x.Security(_httpContext.Items.DownstreamReRoute(), _httpContext)).Returns(Task.FromResult(response)); + item.Setup(x => x.Security(_httpContext.Items.DownstreamRoute(), _httpContext)).Returns(Task.FromResult(response)); } } @@ -82,12 +82,12 @@ private void GivenNotPassingSecurityVerification() { Error error = new UnauthenticatedError($"Not passing security verification"); Response response = new ErrorResponse(error); - item.Setup(x => x.Security(_httpContext.Items.DownstreamReRoute(), _httpContext)).Returns(Task.FromResult(response)); + item.Setup(x => x.Security(_httpContext.Items.DownstreamRoute(), _httpContext)).Returns(Task.FromResult(response)); } else { Response response = new OkResponse(); - item.Setup(x => x.Security(_httpContext.Items.DownstreamReRoute(), _httpContext)).Returns(Task.FromResult(response)); + item.Setup(x => x.Security(_httpContext.Items.DownstreamRoute(), _httpContext)).Returns(Task.FromResult(response)); } } } @@ -98,12 +98,12 @@ private void WhenICallTheMiddleware() } private void ThenTheRequestIsPassingSecurity() - { + { _httpContext.Items.Errors().Count.ShouldBe(0); } private void ThenTheRequestIsNotPassingSecurity() - { + { _httpContext.Items.Errors().Count.ShouldBeGreaterThan(0); } } diff --git a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceDiscoveryProviderFactoryTests.cs b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceDiscoveryProviderFactoryTests.cs index 0b5362ed7..3579b63b5 100644 --- a/test/Ocelot.UnitTests/ServiceDiscovery/ServiceDiscoveryProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/ServiceDiscovery/ServiceDiscoveryProviderFactoryTests.cs @@ -1,187 +1,187 @@ -namespace Ocelot.UnitTests.ServiceDiscovery -{ - using Microsoft.Extensions.DependencyInjection; - using Moq; - using Ocelot.Configuration; - using Ocelot.Configuration.Builder; - using Ocelot.Logging; - using Ocelot.Responses; - using Ocelot.ServiceDiscovery; - using Ocelot.ServiceDiscovery.Providers; - using Shouldly; - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using TestStack.BDDfy; - using Values; - using Xunit; - - public class ServiceDiscoveryProviderFactoryTests - { - private ServiceProviderConfiguration _serviceConfig; - private Response _result; - private ServiceDiscoveryProviderFactory _factory; - private DownstreamReRoute _reRoute; - private readonly Mock _loggerFactory; - private Mock _logger; - private IServiceProvider _provider; - private readonly IServiceCollection _collection; - - public ServiceDiscoveryProviderFactoryTests() - { - _loggerFactory = new Mock(); - _logger = new Mock(); - _collection = new ServiceCollection(); - _provider = _collection.BuildServiceProvider(); - _factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, _provider); - } - - [Fact] - public void should_return_no_service_provider() - { - var serviceConfig = new ServiceProviderConfigurationBuilder() - .Build(); - - var reRoute = new DownstreamReRouteBuilder().Build(); - - this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) - .When(x => x.WhenIGetTheServiceProvider()) - .Then(x => x.ThenTheServiceProviderIs()) - .BDDfy(); - } - - [Fact] - public void should_return_list_of_configuration_services() - { - var serviceConfig = new ServiceProviderConfigurationBuilder() - .Build(); - - var downstreamAddresses = new List() - { - new DownstreamHostAndPort("asdf.com", 80), - new DownstreamHostAndPort("abc.com", 80) - }; - - var reRoute = new DownstreamReRouteBuilder().WithDownstreamAddresses(downstreamAddresses).Build(); - - this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) - .When(x => x.WhenIGetTheServiceProvider()) - .Then(x => x.ThenTheServiceProviderIs()) - .Then(x => ThenTheFollowingServicesAreReturned(downstreamAddresses)) - .BDDfy(); - } - - [Fact] - public void should_return_provider_because_type_matches_reflected_type_from_delegate() - { - var reRoute = new DownstreamReRouteBuilder() - .WithServiceName("product") - .WithUseServiceDiscovery(true) - .Build(); - - var serviceConfig = new ServiceProviderConfigurationBuilder() - .WithType(nameof(Fake)) - .Build(); - - this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) - .And(x => GivenAFakeDelegate()) - .When(x => x.WhenIGetTheServiceProvider()) - .Then(x => x.ThenTheDelegateIsCalled()) - .BDDfy(); - } - - [Fact] - public void should_not_return_provider_because_type_doesnt_match_reflected_type_from_delegate() - { - var reRoute = new DownstreamReRouteBuilder() - .WithServiceName("product") - .WithUseServiceDiscovery(true) - .Build(); - - var serviceConfig = new ServiceProviderConfigurationBuilder() - .WithType("Wookie") - .Build(); - - this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) - .And(x => GivenAFakeDelegate()) - .When(x => x.WhenIGetTheServiceProvider()) - .Then(x => x.ThenTheResultIsError()) - .BDDfy(); - } - - [Fact] - public void should_return_service_fabric_provider() - { - var reRoute = new DownstreamReRouteBuilder() - .WithServiceName("product") - .WithUseServiceDiscovery(true) - .Build(); - - var serviceConfig = new ServiceProviderConfigurationBuilder() - .WithType("ServiceFabric") - .Build(); - - this.Given(x => x.GivenTheReRoute(serviceConfig, reRoute)) - .When(x => x.WhenIGetTheServiceProvider()) - .Then(x => x.ThenTheServiceProviderIs()) - .BDDfy(); - } - - private void GivenAFakeDelegate() - { - ServiceDiscoveryFinderDelegate fake = (provider, config, name) => new Fake(); - _collection.AddSingleton(fake); - _provider = _collection.BuildServiceProvider(); - _factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, _provider); - } - - private class Fake : IServiceDiscoveryProvider - { - public Task> Get() - { - return null; - } - } - - private void ThenTheDelegateIsCalled() - { - _result.Data.GetType().Name.ShouldBe("Fake"); - } - - private void ThenTheResultIsError() - { - _result.IsError.ShouldBeTrue(); - } - - private void ThenTheFollowingServicesAreReturned(List downstreamAddresses) - { - var result = (ConfigurationServiceProvider)_result.Data; - var services = result.Get().Result; - - for (int i = 0; i < services.Count; i++) - { - var service = services[i]; - var downstreamAddress = downstreamAddresses[i]; - - service.HostAndPort.DownstreamHost.ShouldBe(downstreamAddress.Host); - service.HostAndPort.DownstreamPort.ShouldBe(downstreamAddress.Port); - } - } - - private void GivenTheReRoute(ServiceProviderConfiguration serviceConfig, DownstreamReRoute reRoute) - { - _serviceConfig = serviceConfig; - _reRoute = reRoute; - } - - private void WhenIGetTheServiceProvider() - { - _result = _factory.Get(_serviceConfig, _reRoute); - } - - private void ThenTheServiceProviderIs() - { - _result.Data.ShouldBeOfType(); - } - } -} +namespace Ocelot.UnitTests.ServiceDiscovery +{ + using Microsoft.Extensions.DependencyInjection; + using Moq; + using Ocelot.Configuration; + using Ocelot.Configuration.Builder; + using Ocelot.Logging; + using Ocelot.Responses; + using Ocelot.ServiceDiscovery; + using Ocelot.ServiceDiscovery.Providers; + using Shouldly; + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using TestStack.BDDfy; + using Values; + using Xunit; + + public class ServiceDiscoveryProviderFactoryTests + { + private ServiceProviderConfiguration _serviceConfig; + private Response _result; + private ServiceDiscoveryProviderFactory _factory; + private DownstreamRoute _route; + private readonly Mock _loggerFactory; + private Mock _logger; + private IServiceProvider _provider; + private readonly IServiceCollection _collection; + + public ServiceDiscoveryProviderFactoryTests() + { + _loggerFactory = new Mock(); + _logger = new Mock(); + _collection = new ServiceCollection(); + _provider = _collection.BuildServiceProvider(); + _factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, _provider); + } + + [Fact] + public void should_return_no_service_provider() + { + var serviceConfig = new ServiceProviderConfigurationBuilder() + .Build(); + + var route = new DownstreamRouteBuilder().Build(); + + this.Given(x => x.GivenTheRoute(serviceConfig, route)) + .When(x => x.WhenIGetTheServiceProvider()) + .Then(x => x.ThenTheServiceProviderIs()) + .BDDfy(); + } + + [Fact] + public void should_return_list_of_configuration_services() + { + var serviceConfig = new ServiceProviderConfigurationBuilder() + .Build(); + + var downstreamAddresses = new List() + { + new DownstreamHostAndPort("asdf.com", 80), + new DownstreamHostAndPort("abc.com", 80) + }; + + var route = new DownstreamRouteBuilder().WithDownstreamAddresses(downstreamAddresses).Build(); + + this.Given(x => x.GivenTheRoute(serviceConfig, route)) + .When(x => x.WhenIGetTheServiceProvider()) + .Then(x => x.ThenTheServiceProviderIs()) + .Then(x => ThenTheFollowingServicesAreReturned(downstreamAddresses)) + .BDDfy(); + } + + [Fact] + public void should_return_provider_because_type_matches_reflected_type_from_delegate() + { + var route = new DownstreamRouteBuilder() + .WithServiceName("product") + .WithUseServiceDiscovery(true) + .Build(); + + var serviceConfig = new ServiceProviderConfigurationBuilder() + .WithType(nameof(Fake)) + .Build(); + + this.Given(x => x.GivenTheRoute(serviceConfig, route)) + .And(x => GivenAFakeDelegate()) + .When(x => x.WhenIGetTheServiceProvider()) + .Then(x => x.ThenTheDelegateIsCalled()) + .BDDfy(); + } + + [Fact] + public void should_not_return_provider_because_type_doesnt_match_reflected_type_from_delegate() + { + var route = new DownstreamRouteBuilder() + .WithServiceName("product") + .WithUseServiceDiscovery(true) + .Build(); + + var serviceConfig = new ServiceProviderConfigurationBuilder() + .WithType("Wookie") + .Build(); + + this.Given(x => x.GivenTheRoute(serviceConfig, route)) + .And(x => GivenAFakeDelegate()) + .When(x => x.WhenIGetTheServiceProvider()) + .Then(x => x.ThenTheResultIsError()) + .BDDfy(); + } + + [Fact] + public void should_return_service_fabric_provider() + { + var route = new DownstreamRouteBuilder() + .WithServiceName("product") + .WithUseServiceDiscovery(true) + .Build(); + + var serviceConfig = new ServiceProviderConfigurationBuilder() + .WithType("ServiceFabric") + .Build(); + + this.Given(x => x.GivenTheRoute(serviceConfig, route)) + .When(x => x.WhenIGetTheServiceProvider()) + .Then(x => x.ThenTheServiceProviderIs()) + .BDDfy(); + } + + private void GivenAFakeDelegate() + { + ServiceDiscoveryFinderDelegate fake = (provider, config, name) => new Fake(); + _collection.AddSingleton(fake); + _provider = _collection.BuildServiceProvider(); + _factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object, _provider); + } + + private class Fake : IServiceDiscoveryProvider + { + public Task> Get() + { + return null; + } + } + + private void ThenTheDelegateIsCalled() + { + _result.Data.GetType().Name.ShouldBe("Fake"); + } + + private void ThenTheResultIsError() + { + _result.IsError.ShouldBeTrue(); + } + + private void ThenTheFollowingServicesAreReturned(List downstreamAddresses) + { + var result = (ConfigurationServiceProvider)_result.Data; + var services = result.Get().Result; + + for (int i = 0; i < services.Count; i++) + { + var service = services[i]; + var downstreamAddress = downstreamAddresses[i]; + + service.HostAndPort.DownstreamHost.ShouldBe(downstreamAddress.Host); + service.HostAndPort.DownstreamPort.ShouldBe(downstreamAddress.Port); + } + } + + private void GivenTheRoute(ServiceProviderConfiguration serviceConfig, DownstreamRoute route) + { + _serviceConfig = serviceConfig; + _route = route; + } + + private void WhenIGetTheServiceProvider() + { + _result = _factory.Get(_serviceConfig, _route); + } + + private void ThenTheServiceProviderIs() + { + _result.Data.ShouldBeOfType(); + } + } +}