mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Merge pull request #13 from TomPallister/develop
Update Ocelot configuration without downtime
This commit is contained in:
commit
26c7d1e37b
@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<package >
|
|
||||||
<metadata>
|
|
||||||
<id>Ocelot</id>
|
|
||||||
<version>1.0.0</version>
|
|
||||||
<authors>Tom Pallister</authors>
|
|
||||||
<owners>Tom Pallister</owners>
|
|
||||||
<licenseUrl>https://github.com/TomPallister/Ocelot/blob/develop/LICENSE.md</licenseUrl>
|
|
||||||
<projectUrl>https://github.com/TomPallister/Ocelot</projectUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<description>Ocelot Api Gateway</description>
|
|
||||||
<releaseNotes>Latest Ocelot</releaseNotes>
|
|
||||||
<copyright></copyright>
|
|
||||||
<tags></tags>
|
|
||||||
<dependencies>
|
|
||||||
<dependency id="Microsoft.AspNetCore.Server.IISIntegration" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.Extensions.Configuration.EnvironmentVariables" version= "1.1.0" />
|
|
||||||
<dependency id="Microsoft.Extensions.Configuration.FileExtensions" version= "1.1.0" />
|
|
||||||
<dependency id="Microsoft.Extensions.Configuration.Json" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.Extensions.Logging" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.Extensions.Logging.Console" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.Extensions.Logging.Debug" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.Extensions.Options.ConfigurationExtensions" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Http" version="1.1.0" />
|
|
||||||
<dependency id="System.Text.RegularExpressions" version="4.3.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication.OAuth" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication.JwtBearer" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication.OpenIdConnect" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication.Cookies" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication.Google" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication.Facebook" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication.Twitter" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication.MicrosoftAccount" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Authentication" version="1.1.0" />
|
|
||||||
<dependency id="IdentityServer4.AccessTokenValidation" version="1.0.2" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Mvc" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.AspNetCore.Server.Kestrel" version="1.1.0" />
|
|
||||||
<dependency id="Microsoft.NETCore.App" version="1.1.0" />
|
|
||||||
<dependency id="CacheManager.Core" version="0.9.2" />
|
|
||||||
<dependency id="CacheManager.Microsoft.Extensions.Configuration" version="0.9.2" />
|
|
||||||
<dependency id="CacheManager.Microsoft.Extensions.Logging" version="0.9.2" />
|
|
||||||
</dependencies>
|
|
||||||
</metadata>
|
|
||||||
<files>
|
|
||||||
<file src="src\Ocelot\bin\Release\netcoreapp1.4\Ocelot.dll" target="lib\netstandard1.4" />
|
|
||||||
<file src="src\Ocelot\bin\Release\netcoreapp1.4\Ocelot.pdb" target="lib\netstandard1.4" />
|
|
||||||
</files>
|
|
||||||
</package>
|
|
10
Ocelot.sln
10
Ocelot.sln
@ -8,7 +8,6 @@ EndProject
|
|||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3FA7C349-DBE8-4904-A2CE-015B8869CE6C}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.gitignore = .gitignore
|
.gitignore = .gitignore
|
||||||
appveyor.yml = appveyor.yml
|
|
||||||
build-and-release-unstable.ps1 = build-and-release-unstable.ps1
|
build-and-release-unstable.ps1 = build-and-release-unstable.ps1
|
||||||
build-and-run-tests.ps1 = build-and-run-tests.ps1
|
build-and-run-tests.ps1 = build-and-run-tests.ps1
|
||||||
build.cake = build.cake
|
build.cake = build.cake
|
||||||
@ -19,7 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
GitVersion.yml = GitVersion.yml
|
GitVersion.yml = GitVersion.yml
|
||||||
global.json = global.json
|
global.json = global.json
|
||||||
LICENSE.md = LICENSE.md
|
LICENSE.md = LICENSE.md
|
||||||
Ocelot.nuspec = Ocelot.nuspec
|
ocelot.postman_collection.json = ocelot.postman_collection.json
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
release.ps1 = release.ps1
|
release.ps1 = release.ps1
|
||||||
ReleaseNotes.md = ReleaseNotes.md
|
ReleaseNotes.md = ReleaseNotes.md
|
||||||
@ -41,6 +40,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.ManualTest", "test\O
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.xproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.Benchmarks", "test\Ocelot.Benchmarks\Ocelot.Benchmarks.xproj", "{106B49E6-95F6-4A7B-B81C-96BFA74AF035}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Ocelot.IntegrationTests", "test\Ocelot.IntegrationTests\Ocelot.IntegrationTests.xproj", "{D4575572-99CA-4530-8737-C296EDA326F8}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -67,6 +68,10 @@ Global
|
|||||||
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
|
{106B49E6-95F6-4A7B-B81C-96BFA74AF035}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4575572-99CA-4530-8737-C296EDA326F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D4575572-99CA-4530-8737-C296EDA326F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -77,5 +82,6 @@ Global
|
|||||||
{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
|
{F8C224FE-36BE-45F5-9B0E-666D8F4A9B52} = {5B401523-36DA-4491-B73A-7590A26E420B}
|
||||||
{02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
|
{02BBF4C5-517E-4157-8D21-4B8B9E118B7A} = {5B401523-36DA-4491-B73A-7590A26E420B}
|
||||||
{106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
|
{106B49E6-95F6-4A7B-B81C-96BFA74AF035} = {5B401523-36DA-4491-B73A-7590A26E420B}
|
||||||
|
{D4575572-99CA-4530-8737-C296EDA326F8} = {5B401523-36DA-4491-B73A-7590A26E420B}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
86
README.md
86
README.md
@ -99,20 +99,48 @@ public class Startup
|
|||||||
.WithDictionaryHandle();
|
.WithDictionaryHandle();
|
||||||
};
|
};
|
||||||
|
|
||||||
services.AddOcelotOutputCaching(settings);
|
services.AddOcelotOutputCaching(settings);
|
||||||
services.AddOcelotFileConfiguration(Configuration);
|
services.AddOcelot(Configuration);
|
||||||
services.AddOcelot();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||||
|
|
||||||
app.UseOcelot();
|
await app.UseOcelot();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Then in your Program.cs you will want to have the following. This can be changed if you
|
||||||
|
don't wan't to use the default url e.g. UseUrls(someUrls) and should work as long as you keep the WebHostBuilder registration.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
IWebHostBuilder builder = new WebHostBuilder();
|
||||||
|
|
||||||
|
builder.ConfigureServices(s => {
|
||||||
|
s.AddSingleton(builder);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseStartup<Startup>();
|
||||||
|
|
||||||
|
var host = builder.Build();
|
||||||
|
|
||||||
|
host.Run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Sadly we need to inject the IWebHostBuilder interface to get the applications scheme, url and port later. I cannot
|
||||||
|
find a better way of doing this at the moment without setting this in a static or some kind of config.
|
||||||
|
|
||||||
This is pretty much all you need to get going.......more to come!
|
This is pretty much all you need to get going.......more to come!
|
||||||
|
|
||||||
## Routing
|
## Routing
|
||||||
@ -167,6 +195,46 @@ This means that when Ocelot tries to match the incoming upstream url with an ups
|
|||||||
evaluation will be case sensitive. This setting defaults to false so only set it if you want
|
evaluation will be case sensitive. This setting defaults to false so only set it if you want
|
||||||
the ReRoute to be case sensitive is my advice!
|
the ReRoute to be case sensitive is my advice!
|
||||||
|
|
||||||
|
## Administration
|
||||||
|
|
||||||
|
Ocelot supports changing configuration during runtime via an authenticated HTTP API. The API is authenticated
|
||||||
|
using bearer tokens that you request from iteself. This is provided by the amazing [IdentityServer](https://github.com/IdentityServer/IdentityServer4)
|
||||||
|
project that I have been using for a few years now. Check them out.
|
||||||
|
|
||||||
|
In order to enable the administration section you need to do a few things. First of all add this to your
|
||||||
|
initial configuration.json. The value can be anything you want and it is obviously reccomended don't use
|
||||||
|
a url you would like to route through with Ocelot as this will not work. The administration uses the
|
||||||
|
MapWhen functionality of asp.net core and all requests to root/administration will be sent there not
|
||||||
|
to the Ocelot middleware.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"GlobalConfiguration": {
|
||||||
|
"AdministrationPath": "/administration"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
This will get the admin area set up but not the authentication. Please note that this is a very basic approach to
|
||||||
|
this problem and if needed we can obviously improve on this!
|
||||||
|
|
||||||
|
You need to set 3 environmental variables.
|
||||||
|
|
||||||
|
OCELOT_USERNAME
|
||||||
|
OCELOT_HASH
|
||||||
|
OCELOT_SALT
|
||||||
|
|
||||||
|
These need to be the admin username you want to use with Ocelot and the hash and salt of the password you want to
|
||||||
|
use given hashing algorythm. When requesting bearer tokens for use with the administration api you will need to
|
||||||
|
supply username and password.
|
||||||
|
|
||||||
|
In order to create a hash and salt of your password please check out HashCreationTests.should_create_hash_and_salt()
|
||||||
|
this technique is based on [this](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing)
|
||||||
|
using SHA256 rather than SHA1.
|
||||||
|
|
||||||
|
Now if you went with the configuration options above and want to access the API you can use the postman scripts
|
||||||
|
called ocelot.postman_collection.json in the solution to change the Ocelot configuration. Obviously these
|
||||||
|
will need to be changed if you are running Ocelot on a different url to http://localhost:5000.
|
||||||
|
|
||||||
|
The scripts show you how to request a bearer token from ocelot and then use it to GET the existing configuration and POST
|
||||||
|
a configuration.
|
||||||
|
|
||||||
## Service Discovery
|
## Service Discovery
|
||||||
|
|
||||||
@ -470,6 +538,8 @@ forwarded to the downstream service. Obviously this would break everything :(
|
|||||||
and doesnt check the response is OK. I think the fact you can even call stuff
|
and doesnt check the response is OK. I think the fact you can even call stuff
|
||||||
that isnt available is annoying. Let alone it be null.
|
that isnt available is annoying. Let alone it be null.
|
||||||
|
|
||||||
|
[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
|
||||||
|
|
||||||
## Coming up
|
## Coming up
|
||||||
|
|
||||||
You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
|
You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
|
||||||
|
564
README.md.orig
Normal file
564
README.md.orig
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
# Ocelot
|
||||||
|
|
||||||
|
[](https://ci.appveyor.com/project/TomPallister/ocelot-fcfpb)
|
||||||
|
|
||||||
|
[](https://gitter.im/Ocelotey/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
|
|
||||||
|
[ Get more details at **codescene.io**.](https://codescene.io/projects/697/jobs/latest-successful/results)
|
||||||
|
|
||||||
|
Attempt at a .NET Api Gateway
|
||||||
|
|
||||||
|
This project is aimed at people using .NET running
|
||||||
|
a micro services / service orientated architecture
|
||||||
|
that need a unified point of entry into their system.
|
||||||
|
|
||||||
|
In particular I want easy integration with
|
||||||
|
IdentityServer reference and bearer tokens.
|
||||||
|
|
||||||
|
We have been unable to find this in my current workplace
|
||||||
|
without having to write our own Javascript middlewares
|
||||||
|
to handle the IdentityServer reference tokens. We would
|
||||||
|
rather use the IdentityServer code that already exists
|
||||||
|
to do this.
|
||||||
|
|
||||||
|
Ocelot is a bunch of middlewares in a specific order.
|
||||||
|
|
||||||
|
Ocelot manipulates the HttpRequest object into a state specified by its configuration until
|
||||||
|
it reaches a request builder middleware where it creates a HttpRequestMessage object which is
|
||||||
|
used to make a request to a downstream service. The middleware that makes the request is
|
||||||
|
the last thing in the Ocelot pipeline. It does not call the next middleware.
|
||||||
|
The response from the downstream service is stored in a per request scoped repository
|
||||||
|
and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware
|
||||||
|
that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client.
|
||||||
|
That is basically it with a bunch of other features.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Pull requests, issues and commentary welcome! No special process just create a request and get in
|
||||||
|
touch either via gitter or create an issue.
|
||||||
|
|
||||||
|
## How to install
|
||||||
|
|
||||||
|
Ocelot is designed to work with ASP.NET core only and is currently
|
||||||
|
built to netcoreapp1.1 [this](https://docs.microsoft.com/en-us/dotnet/articles/standard/library) documentation may prove helpful when working out if Ocelot would be suitable for you.
|
||||||
|
|
||||||
|
Install Ocelot and it's dependecies using nuget. At the moment
|
||||||
|
all we have is the pre version. Once we have something working in
|
||||||
|
a half decent way we will drop a version.
|
||||||
|
|
||||||
|
`Install-Package Ocelot`
|
||||||
|
|
||||||
|
All versions can be found [here](https://www.nuget.org/packages/Ocelot/)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
An example configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/configuration.json)
|
||||||
|
and an explained configuration can be found [here](https://github.com/TomPallister/Ocelot/blob/develop/configuration-explanation.txt). More detailed instructions to come on how to configure this.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ReRoutes": [],
|
||||||
|
"GlobalConfiguration": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
More information on how to use these options is below..
|
||||||
|
|
||||||
|
## Startup
|
||||||
|
|
||||||
|
An example startup using a json file for configuration can be seen below.
|
||||||
|
Currently this is the only way to get configuration into Ocelot.
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
public class Startup
|
||||||
|
=======
|
||||||
|
```csharp
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IHostingEnvironment env)
|
||||||
|
>>>>>>> develop
|
||||||
|
{
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(env.ContentRootPath)
|
||||||
|
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||||
|
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
|
||||||
|
.AddJsonFile("configuration.json")
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
|
||||||
|
Configuration = builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfigurationRoot Configuration { get; }
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
Action<ConfigurationBuilderCachePart> settings = (x) =>
|
||||||
|
{
|
||||||
|
x.WithMicrosoftLogging(log =>
|
||||||
|
{
|
||||||
|
<<<<<<< HEAD
|
||||||
|
x.WithMicrosoftLogging(log =>
|
||||||
|
{
|
||||||
|
log.AddConsole(LogLevel.Debug);
|
||||||
|
})
|
||||||
|
.WithDictionaryHandle();
|
||||||
|
};
|
||||||
|
|
||||||
|
services.AddOcelotOutputCaching(settings);
|
||||||
|
services.AddOcelot(Configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||||
|
|
||||||
|
await app.UseOcelot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Then in your Program.cs you will want to have the following. This can be changed if you
|
||||||
|
don't wan't to use the default url e.g. UseUrls(someUrls) and should work as long as you keep the WebHostBuilder registration.
|
||||||
|
|
||||||
|
IWebHostBuilder builder = new WebHostBuilder();
|
||||||
|
|
||||||
|
builder.ConfigureServices(s => {
|
||||||
|
s.AddSingleton(builder);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseStartup<Startup>();
|
||||||
|
|
||||||
|
var host = builder.Build();
|
||||||
|
|
||||||
|
host.Run();
|
||||||
|
|
||||||
|
Sadly we need to inject the IWebHostBuilder interface to get the applications scheme, url and port later. I cannot
|
||||||
|
find a better way of doing this at the moment without setting this in a static or some kind of config.
|
||||||
|
=======
|
||||||
|
log.AddConsole(LogLevel.Debug);
|
||||||
|
})
|
||||||
|
.WithDictionaryHandle();
|
||||||
|
};
|
||||||
|
|
||||||
|
services.AddOcelotOutputCaching(settings);
|
||||||
|
services.AddOcelotFileConfiguration(Configuration);
|
||||||
|
services.AddOcelot();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||||
|
|
||||||
|
app.UseOcelot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
>>>>>>> develop
|
||||||
|
|
||||||
|
This is pretty much all you need to get going.......more to come!
|
||||||
|
|
||||||
|
## Routing
|
||||||
|
|
||||||
|
Ocelot's primary functionality is to take incomeing http requests and forward them on
|
||||||
|
to a downstream service. At the moment in the form of another http request (in the future
|
||||||
|
this could be any transport mechanism.).
|
||||||
|
|
||||||
|
Ocelot always adds a trailing slash to an UpstreamPathTemplate.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to set up a ReRoute you need to add one to the json array called ReRoutes like
|
||||||
|
the following.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/posts/{postId}",
|
||||||
|
"DownstreamScheme": "https",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"DownstreamHost" "localhost"
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": "Put"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The DownstreamPathTemplate,Scheme, Port and Host make the URL that this request will be forwarded to.
|
||||||
|
The UpstreamPathTemplate is the URL that Ocelot will use to identity which
|
||||||
|
DownstreamPathTemplate to use for a given request. Finally the UpstreamHttpMethod is used so
|
||||||
|
Ocelot can distinguish between requests to the same URL and is obviously needed to work :)
|
||||||
|
In Ocelot you can add placeholders for variables to your Templates in the form of {something}.
|
||||||
|
The placeholder needs to be in both the DownstreamPathTemplate and UpstreamPathTemplate. If it is
|
||||||
|
Ocelot will attempt to replace the placeholder with the correct variable value from the
|
||||||
|
Upstream URL when the request comes in.
|
||||||
|
|
||||||
|
At the moment without any configuration Ocelot will default to all ReRoutes being case insensitive.
|
||||||
|
In order to change this you can specify on a per ReRoute basis the following setting.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"ReRouteIsCaseSensitive": true
|
||||||
|
```
|
||||||
|
|
||||||
|
This means that when Ocelot tries to match the incoming upstream url with an upstream template the
|
||||||
|
evaluation will be case sensitive. This setting defaults to false so only set it if you want
|
||||||
|
the ReRoute to be case sensitive is my advice!
|
||||||
|
|
||||||
|
## Administration
|
||||||
|
|
||||||
|
Ocelot supports changing configuration during runtime via an authenticated HTTP API. The API is authenticated
|
||||||
|
using bearer tokens that you request from iteself. This is provided by the amazing [IdentityServer](https://github.com/IdentityServer/IdentityServer4)
|
||||||
|
project that I have been using for a few years now. Check them out.
|
||||||
|
|
||||||
|
In order to enable the administration section you need to do a few things. First of all add this to your
|
||||||
|
initial configuration.json. The value can be anything you want and it is obviously reccomended don't use
|
||||||
|
a url you would like to route through with Ocelot as this will not work. The administration uses the
|
||||||
|
MapWhen functionality of asp.net core and all requests to root/administration will be sent there not
|
||||||
|
to the Ocelot middleware.
|
||||||
|
|
||||||
|
"GlobalConfiguration": {
|
||||||
|
"AdministrationPath": "/administration"
|
||||||
|
}
|
||||||
|
|
||||||
|
This will get the admin area set up but not the authentication. Please note that this is a very basic approach to
|
||||||
|
this problem and if needed we can obviously improve on this!
|
||||||
|
|
||||||
|
You need to set 3 environmental variables.
|
||||||
|
|
||||||
|
OCELOT_USERNAME
|
||||||
|
OCELOT_HASH
|
||||||
|
OCELOT_SALT
|
||||||
|
|
||||||
|
These need to be the admin username you want to use with Ocelot and the hash and salt of the password you want to
|
||||||
|
use given hashing algorythm. When requesting bearer tokens for use with the administration api you will need to
|
||||||
|
supply username and password.
|
||||||
|
|
||||||
|
In order to create a hash and salt of your password please check out HashCreationTests.should_create_hash_and_salt()
|
||||||
|
this technique is based on [this](https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing)
|
||||||
|
using SHA256 rather than SHA1.
|
||||||
|
|
||||||
|
Now if you went with the configuration options above and want to access the API you can use the postman scripts
|
||||||
|
called ocelot.postman_collection.json in the solution to change the Ocelot configuration. Obviously these
|
||||||
|
will need to be changed if you are running Ocelot on a different url to http://localhost:5000.
|
||||||
|
|
||||||
|
The scripts show you how to request a bearer token from ocelot and then use it to GET the existing configuration and POST
|
||||||
|
a configuration.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
In the future we can add a feature that allows ReRoute specfic configuration.
|
||||||
|
|
||||||
|
At the moment the only supported service discovery provider is Consul. The following is required in the
|
||||||
|
GlobalConfiguration. The Provider is required and if you do not specify a host and port the Consul default
|
||||||
|
will be used.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"ServiceDiscoveryProvider": {
|
||||||
|
"Provider":"Consul",
|
||||||
|
"Host":"localhost",
|
||||||
|
"Port":8500
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to tell Ocelot a ReRoute is to use the service discovery provider for its host and port you must add the
|
||||||
|
ServiceName and load balancer you wish to use when making requests downstream. At the moment Ocelot has a RoundRobin
|
||||||
|
and LeastConnection algorithm you can use. If no load balancer is specified Ocelot will not load balance requests.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/posts/{postId}",
|
||||||
|
"DownstreamScheme": "https",
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": "Put",
|
||||||
|
"ServiceName": "product",
|
||||||
|
"LoadBalancer": "LeastConnection"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When this is set up Ocelot will lookup the downstream host and port from the service discover provider and load balancer
|
||||||
|
requests across any available services.
|
||||||
|
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
Ocelot currently supports the use of bearer tokens with Identity Server (more providers to
|
||||||
|
come if required). In order to identity a ReRoute as authenticated it needs the following
|
||||||
|
configuration added.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"AuthenticationOptions": {
|
||||||
|
"Provider": "IdentityServer",
|
||||||
|
"ProviderRootUrl": "http://localhost:52888",
|
||||||
|
"ScopeName": "api",
|
||||||
|
"AdditionalScopes": [
|
||||||
|
"openid",
|
||||||
|
"offline_access"
|
||||||
|
],
|
||||||
|
"ScopeSecret": "secret"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example the Provider is specified as IdentityServer. This string is important
|
||||||
|
because it is used to identity the authentication provider (as previously mentioned in
|
||||||
|
the future there might be more providers). Identity server requires that the client
|
||||||
|
talk to it so we need to provide the root url of the IdentityServer as ProviderRootUrl.
|
||||||
|
IdentityServer requires at least one scope and you can also provider additional scopes.
|
||||||
|
Finally if you are using IdentityServer reference tokens you need to provide the scope
|
||||||
|
secret.
|
||||||
|
|
||||||
|
Ocelot will use this configuration to build an authentication handler and if
|
||||||
|
authentication is succefull the next middleware will be called else the response
|
||||||
|
is 401 unauthorised.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"RouteClaimsRequirement": {
|
||||||
|
"UserType": "registered"
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example when the authorisation middleware is called Ocelot will check to see
|
||||||
|
if the user has the claim type UserType and if the value of that claim is registered.
|
||||||
|
If it isn't then the user will not be authorised and the response will be 403 forbidden.
|
||||||
|
|
||||||
|
## Claims Tranformation
|
||||||
|
|
||||||
|
Ocelot allows the user to access claims and transform them into headers, query string
|
||||||
|
parameters and other claims. This is only available once a user has been authenticated.
|
||||||
|
|
||||||
|
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 and Finally
|
||||||
|
the claims to query strig parameters middleware.
|
||||||
|
|
||||||
|
The syntax for performing the transforms is the same for each proces. In the ReRoute
|
||||||
|
configuration a json dictionary is added with a specific name either AddClaimsToRequest,
|
||||||
|
AddHeadersToRequest, AddQueriesToRequest.
|
||||||
|
|
||||||
|
Note I'm not a hotshot programmer so have no idea if this syntax is good..
|
||||||
|
|
||||||
|
Within this dictionary the entries specify how Ocelot should transform things!
|
||||||
|
The key to the dictionary is going to become the key of either a claim, header
|
||||||
|
or query parameter.
|
||||||
|
|
||||||
|
The value of the entry is parsed to logic that will perform the transform. First of
|
||||||
|
all a dictionary accessor is specified e.g. Claims[CustomerId]. This means we want
|
||||||
|
to access the claims and get the CustomerId claim type. Next is a greater than (>)
|
||||||
|
symbol which is just used to split the string. The next entry is either value or value with
|
||||||
|
and indexer. If value is specifed Ocelot will just take the value and add it to the
|
||||||
|
transform. If the value has an indexer Ocelot will look for a delimiter which is provided
|
||||||
|
after another greater than symbol. Ocelot will then split the value on the delimiter
|
||||||
|
and add whatever was at the index requested to the transform.
|
||||||
|
|
||||||
|
#### Claims to Claims Tranformation
|
||||||
|
|
||||||
|
Below is an example configuration that will transforms claims to claims
|
||||||
|
|
||||||
|
```json
|
||||||
|
"AddClaimsToRequest": {
|
||||||
|
"UserType": "Claims[sub] > value[0] > |",
|
||||||
|
"UserId": "Claims[sub] > value[1] > |"
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
This shows a transforms where Ocelot looks at the users sub claim and transforms it into
|
||||||
|
UserType and UserId claims. Assuming the sub looks like this "usertypevalue|useridvalue".
|
||||||
|
|
||||||
|
#### Claims to Headers Tranformation
|
||||||
|
|
||||||
|
Below is an example configuration that will transforms claims to headers
|
||||||
|
|
||||||
|
```json
|
||||||
|
"AddHeadersToRequest": {
|
||||||
|
"CustomerId": "Claims[sub] > value[1] > |"
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
This shows a transform where Ocelot looks at the users sub claim and trasnforms it into a
|
||||||
|
CustomerId header. Assuming the sub looks like this "usertypevalue|useridvalue".
|
||||||
|
|
||||||
|
#### Claims to Query String Parameters Tranformation
|
||||||
|
|
||||||
|
Below is an example configuration that will transforms claims to query string parameters
|
||||||
|
|
||||||
|
```json
|
||||||
|
"AddQueriesToRequest": {
|
||||||
|
"LocationId": "Claims[LocationId] > value",
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
This shows a transform where Ocelot looks at the users LocationId claim and add its as
|
||||||
|
a query string parameter to be forwarded onto the downstream service.
|
||||||
|
|
||||||
|
## 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 the an awesome
|
||||||
|
.NET library called Polly check them out [here](https://github.com/App-vNext/Polly).
|
||||||
|
|
||||||
|
Add the following section to a ReRoute configuration.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking":3,
|
||||||
|
"DurationOfBreak":5,
|
||||||
|
"TimeoutValue":5000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You must set a number greater than 0 against ExceptionsAllowedBeforeBreaking for this rule to be
|
||||||
|
implemented. Duration of break is how long the circuit breaker will stay open for after it is tripped.
|
||||||
|
TimeoutValue means ff a request takes more than 5 seconds it will automatically be timed out.
|
||||||
|
|
||||||
|
If you do not add a QoS section QoS will not be used.
|
||||||
|
|
||||||
|
## RequestId / CorrelationId
|
||||||
|
|
||||||
|
Ocelot supports a client sending a request id in the form of a header. If set Ocelot will
|
||||||
|
use the requestid for logging as soon as it becomes available in the middleware pipeline.
|
||||||
|
Ocelot will also forward the request id with the specified header to the downstream service.
|
||||||
|
I'm not sure if have this spot on yet in terms of the pipeline order becasue there are a few logs
|
||||||
|
that don't get the users request id at the moment and ocelot just logs not set for request id
|
||||||
|
which sucks. You can still get the framework request id in the logs if you set
|
||||||
|
IncludeScopes true in your logging config. This can then be used to match up later logs that do
|
||||||
|
have an OcelotRequestId.
|
||||||
|
|
||||||
|
In order to use the requestid feature in your ReRoute configuration add this setting
|
||||||
|
|
||||||
|
```json
|
||||||
|
"RequestIdKey": "OcRequestId"
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example OcRequestId is the request header that contains the clients request id.
|
||||||
|
|
||||||
|
There is also a setting in the GlobalConfiguration section which will override whatever has been
|
||||||
|
set at ReRoute level for the request id. The setting is as fllows.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"RequestIdKey": "OcRequestId",
|
||||||
|
```
|
||||||
|
|
||||||
|
It behaves in exactly the same way as the ReRoute level RequestIdKey settings.
|
||||||
|
|
||||||
|
## Caching
|
||||||
|
|
||||||
|
Ocelot supports some very rudimentary caching at the moment provider by
|
||||||
|
the [CacheManager](http://cachemanager.net/) project. This is an amazing project
|
||||||
|
that is solving a lot of caching problems. I would reccomend using this package to
|
||||||
|
cache with Ocelot. If you look at the example [here](https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/Startup.cs)
|
||||||
|
you can see how the cache manager is setup and then passed into the Ocelot
|
||||||
|
AddOcelotOutputCaching configuration method. You can use any settings supported by
|
||||||
|
the CacheManager package and just pass them in.
|
||||||
|
|
||||||
|
Anyway Ocelot currently supports caching on the URL of the downstream service
|
||||||
|
and setting a TTL in seconds to expire the cache. More to come!
|
||||||
|
|
||||||
|
In orde to use caching on a route in your ReRoute configuration add this setting.
|
||||||
|
|
||||||
|
```json
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example ttl seconds is set to 15 which means the cache will expire after 15 seconds.
|
||||||
|
|
||||||
|
## Ocelot Middleware injection and overrides
|
||||||
|
|
||||||
|
Warning use with caution. If you are seeing any exceptions or strange behavior in your middleware
|
||||||
|
pipeline and you are using any of the following. Remove them and try again!
|
||||||
|
|
||||||
|
When setting up Ocelot in your Startup.cs you can provide some additonal middleware
|
||||||
|
and override middleware. This is done as follos.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var configuration = new OcelotMiddlewareConfiguration
|
||||||
|
{
|
||||||
|
PreErrorResponderMiddleware = async (ctx, next) =>
|
||||||
|
{
|
||||||
|
await next.Invoke();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
app.UseOcelot(configuration);
|
||||||
|
```
|
||||||
|
|
||||||
|
In the example above the provided function will run before the first piece of Ocelot middleware.
|
||||||
|
This allows a user to supply any behaviours they want before and after the Ocelot pipeline has run.
|
||||||
|
This means you can break everything so use at your own pleasure!
|
||||||
|
|
||||||
|
The user can set functions against the following.
|
||||||
|
|
||||||
|
+ PreErrorResponderMiddleware - Already explained above.
|
||||||
|
|
||||||
|
+ PreAuthenticationMiddleware - This allows the user to run pre authentication logic and then call
|
||||||
|
Ocelot's authentication middleware.
|
||||||
|
|
||||||
|
+ AuthenticationMiddleware - This overrides Ocelots authentication middleware.
|
||||||
|
|
||||||
|
+ PreAuthorisationMiddleware - This allows the user to run pre authorisation logic and then call
|
||||||
|
Ocelot's authorisation middleware.
|
||||||
|
|
||||||
|
+ AuthorisationMiddleware - This overrides Ocelots authorisation middleware.
|
||||||
|
|
||||||
|
+ PreQueryStringBuilderMiddleware - This alows the user to manipulate the query string on the
|
||||||
|
http request before it is passed to Ocelots request creator.
|
||||||
|
|
||||||
|
Obviously you can just add middleware as normal before the call to app.UseOcelot() It cannot be added
|
||||||
|
after as Ocelot does not call the next middleware.
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
Ocelot uses the standard logging interfaces ILoggerFactory / ILogger<T> at the moment.
|
||||||
|
This is encapsulated in IOcelotLogger / IOcelotLoggerFactory with an implementation
|
||||||
|
for the standard asp.net core logging stuff at the moment.
|
||||||
|
|
||||||
|
There are a bunch of debugging logs in the ocelot middlewares however I think the
|
||||||
|
system probably needs more logging in the code it calls into. Other than the debugging
|
||||||
|
there is a global error handler that should catch any errors thrown and log them as errors.
|
||||||
|
|
||||||
|
The reason for not just using bog standard framework logging is that I could not
|
||||||
|
work out how to override the request id that get's logged when setting IncludeScopes
|
||||||
|
to true for logging settings. Nicely onto the next feature.
|
||||||
|
|
||||||
|
## Not supported
|
||||||
|
|
||||||
|
Ocelot does not support...
|
||||||
|
|
||||||
|
+ Chunked Encoding - Ocelot will always get the body size and return Content-Length
|
||||||
|
header. Sorry if this doesn't work for your use case!
|
||||||
|
|
||||||
|
+ Fowarding a host header - The host header that you send to Ocelot will not be
|
||||||
|
forwarded to the downstream service. Obviously this would break everything :(
|
||||||
|
|
||||||
|
## Things that are currently annoying me
|
||||||
|
|
||||||
|
+ The ReRoute configuration object is too large.
|
||||||
|
|
||||||
|
+ The base OcelotMiddleware lets you access things that are going to be null
|
||||||
|
and doesnt check the response is OK. I think the fact you can even call stuff
|
||||||
|
that isnt available is annoying. Let alone it be null.
|
||||||
|
|
||||||
|
## Coming up
|
||||||
|
|
||||||
|
You can see what we are working on [here](https://github.com/TomPallister/Ocelot/projects/1)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
|||||||
version: 1.0.{build}
|
|
||||||
configuration:
|
|
||||||
- Release
|
|
||||||
platform: Any CPU
|
|
||||||
build_script:
|
|
||||||
- build.ps1
|
|
||||||
cache:
|
|
||||||
- '%USERPROFILE%\.nuget\packages'
|
|
55
build.cake
55
build.cake
@ -18,7 +18,9 @@ var unitTestAssemblies = @"./test/Ocelot.UnitTests";
|
|||||||
|
|
||||||
// acceptance testing
|
// acceptance testing
|
||||||
var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests");
|
var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests");
|
||||||
var acceptanceTestAssemblies = @"./test/Ocelot.AcceptanceTests";
|
|
||||||
|
// integration testing
|
||||||
|
var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests");
|
||||||
|
|
||||||
// benchmark testing
|
// benchmark testing
|
||||||
var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests");
|
var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests");
|
||||||
@ -44,9 +46,12 @@ var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package";
|
|||||||
var releaseTag = "";
|
var releaseTag = "";
|
||||||
string committedVersion = "0.0.0-dev";
|
string committedVersion = "0.0.0-dev";
|
||||||
var buildVersion = committedVersion;
|
var buildVersion = committedVersion;
|
||||||
|
GitVersion versioning = null;
|
||||||
|
var nugetFeedUnstableBranchFilter = "^(develop)$|^(PullRequest/)";
|
||||||
|
|
||||||
var target = Argument("target", "Default");
|
var target = Argument("target", "Default");
|
||||||
|
|
||||||
|
|
||||||
Information("target is " +target);
|
Information("target is " +target);
|
||||||
Information("Build configuration is " + compileConfig);
|
Information("Build configuration is " + compileConfig);
|
||||||
|
|
||||||
@ -74,7 +79,9 @@ Task("Clean")
|
|||||||
Task("Version")
|
Task("Version")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
var nugetVersion = GetNuGetVersionForCommit();
|
versioning = GetNuGetVersionForCommit();
|
||||||
|
var nugetVersion = versioning.NuGetVersion;
|
||||||
|
|
||||||
Information("SemVer version number: " + nugetVersion);
|
Information("SemVer version number: " + nugetVersion);
|
||||||
|
|
||||||
if (AppVeyor.IsRunningOnAppVeyor)
|
if (AppVeyor.IsRunningOnAppVeyor)
|
||||||
@ -129,6 +136,24 @@ Task("RunAcceptanceTests")
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Task("RunIntegrationTests")
|
||||||
|
.IsDependentOn("Restore")
|
||||||
|
.Does(() =>
|
||||||
|
{
|
||||||
|
var buildSettings = new DotNetCoreTestSettings
|
||||||
|
{
|
||||||
|
Configuration = "Debug", //int test config is hard-coded for debug
|
||||||
|
};
|
||||||
|
|
||||||
|
EnsureDirectoryExists(artifactsForIntegrationTestsDir);
|
||||||
|
|
||||||
|
DoInDirectory("test/Ocelot.IntegrationTests", () =>
|
||||||
|
{
|
||||||
|
DotNetCoreTest(".", buildSettings);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
Task("RunBenchmarkTests")
|
Task("RunBenchmarkTests")
|
||||||
.IsDependentOn("Restore")
|
.IsDependentOn("Restore")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
@ -149,6 +174,7 @@ Task("RunBenchmarkTests")
|
|||||||
Task("RunTests")
|
Task("RunTests")
|
||||||
.IsDependentOn("RunUnitTests")
|
.IsDependentOn("RunUnitTests")
|
||||||
.IsDependentOn("RunAcceptanceTests")
|
.IsDependentOn("RunAcceptanceTests")
|
||||||
|
.IsDependentOn("RunIntegrationTests")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
});
|
});
|
||||||
@ -189,7 +215,10 @@ Task("ReleasePackagesToUnstableFeed")
|
|||||||
.IsDependentOn("CreatePackages")
|
.IsDependentOn("CreatePackages")
|
||||||
.Does(() =>
|
.Does(() =>
|
||||||
{
|
{
|
||||||
PublishPackages(packagesDir, artifactsFile, nugetFeedUnstableKey, nugetFeedUnstableUploadUrl, nugetFeedUnstableSymbolsUploadUrl);
|
if (ShouldPublishToUnstableFeed(nugetFeedUnstableBranchFilter, versioning.BranchName))
|
||||||
|
{
|
||||||
|
PublishPackages(packagesDir, artifactsFile, nugetFeedUnstableKey, nugetFeedUnstableUploadUrl, nugetFeedUnstableSymbolsUploadUrl);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Task("EnsureStableReleaseRequirements")
|
Task("EnsureStableReleaseRequirements")
|
||||||
@ -250,15 +279,14 @@ Task("Release")
|
|||||||
RunTarget(target);
|
RunTarget(target);
|
||||||
|
|
||||||
/// Gets nuique nuget version for this commit
|
/// Gets nuique nuget version for this commit
|
||||||
private string GetNuGetVersionForCommit()
|
private GitVersion GetNuGetVersionForCommit()
|
||||||
{
|
{
|
||||||
GitVersion(new GitVersionSettings{
|
GitVersion(new GitVersionSettings{
|
||||||
UpdateAssemblyInfo = false,
|
UpdateAssemblyInfo = false,
|
||||||
OutputType = GitVersionOutput.BuildServer
|
OutputType = GitVersionOutput.BuildServer
|
||||||
});
|
});
|
||||||
|
|
||||||
var versionInfo = GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json });
|
return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json });
|
||||||
return versionInfo.NuGetVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates project version in all of our projects
|
/// Updates project version in all of our projects
|
||||||
@ -343,3 +371,18 @@ private string GetResource(string url)
|
|||||||
return assetsReader.ReadToEnd();
|
return assetsReader.ReadToEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool ShouldPublishToUnstableFeed(string filter, string branchName)
|
||||||
|
{
|
||||||
|
var regex = new System.Text.RegularExpressions.Regex(filter);
|
||||||
|
var publish = regex.IsMatch(branchName);
|
||||||
|
if (publish)
|
||||||
|
{
|
||||||
|
Information("Branch " + branchName + " will be published to the unstable feed");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Information("Branch " + branchName + " will not be published to the unstable feed");
|
||||||
|
}
|
||||||
|
return publish;
|
||||||
|
}
|
1
configuration.json
Executable file
1
configuration.json
Executable file
@ -0,0 +1 @@
|
|||||||
|
{"ReRoutes":[],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0},"AdministrationPath":"/administration"}}
|
117
ocelot.postman_collection.json
Normal file
117
ocelot.postman_collection.json
Normal file
File diff suppressed because one or more lines are too long
@ -2,7 +2,6 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
22
src/Ocelot/Configuration/Authentication/HashMatcher.cs
Normal file
22
src/Ocelot/Configuration/Authentication/HashMatcher.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Authentication
|
||||||
|
{
|
||||||
|
public class HashMatcher : IHashMatcher
|
||||||
|
{
|
||||||
|
public bool Match(string password, string salt, string hash)
|
||||||
|
{
|
||||||
|
byte[] s = Convert.FromBase64String(salt);
|
||||||
|
|
||||||
|
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
|
||||||
|
password: password,
|
||||||
|
salt: s,
|
||||||
|
prf: KeyDerivationPrf.HMACSHA256,
|
||||||
|
iterationCount: 10000,
|
||||||
|
numBytesRequested: 256 / 8));
|
||||||
|
|
||||||
|
return hashed == hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/Ocelot/Configuration/Authentication/IHashMatcher.cs
Normal file
7
src/Ocelot/Configuration/Authentication/IHashMatcher.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.Configuration.Authentication
|
||||||
|
{
|
||||||
|
public interface IHashMatcher
|
||||||
|
{
|
||||||
|
bool Match(string password, string salt, string hash);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
using IdentityServer4.Validation;
|
||||||
|
using Ocelot.Configuration.Provider;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Authentication
|
||||||
|
{
|
||||||
|
public class OcelotResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
|
||||||
|
{
|
||||||
|
private readonly IHashMatcher _matcher;
|
||||||
|
private readonly IIdentityServerConfiguration _identityServerConfiguration;
|
||||||
|
|
||||||
|
public OcelotResourceOwnerPasswordValidator(IHashMatcher matcher, IIdentityServerConfiguration identityServerConfiguration)
|
||||||
|
{
|
||||||
|
_identityServerConfiguration = identityServerConfiguration;
|
||||||
|
_matcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var user = _identityServerConfiguration.Users.FirstOrDefault(u => u.UserName == context.UserName);
|
||||||
|
|
||||||
|
if(user == null)
|
||||||
|
{
|
||||||
|
context.Result = new GrantValidationResult(
|
||||||
|
TokenRequestErrors.InvalidGrant,
|
||||||
|
"invalid custom credential");
|
||||||
|
}
|
||||||
|
else if(_matcher.Match(context.Password, user.Salt, user.Hash))
|
||||||
|
{
|
||||||
|
context.Result = new GrantValidationResult(
|
||||||
|
subject: "admin",
|
||||||
|
authenticationMethod: "custom");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Result = new GrantValidationResult(
|
||||||
|
TokenRequestErrors.InvalidGrant,
|
||||||
|
"invalid custom credential");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
|
@ -56,14 +56,21 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
public async Task<Response<IOcelotConfiguration>> Create()
|
public async Task<Response<IOcelotConfiguration>> Create()
|
||||||
{
|
{
|
||||||
var config = await SetUpConfiguration();
|
var config = await SetUpConfiguration(_options.Value);
|
||||||
|
|
||||||
return new OkResponse<IOcelotConfiguration>(config);
|
return new OkResponse<IOcelotConfiguration>(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IOcelotConfiguration> SetUpConfiguration()
|
public async Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration)
|
||||||
{
|
{
|
||||||
var response = _configurationValidator.IsValid(_options.Value);
|
var config = await SetUpConfiguration(fileConfiguration);
|
||||||
|
|
||||||
|
return new OkResponse<IOcelotConfiguration>(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IOcelotConfiguration> SetUpConfiguration(FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
var response = _configurationValidator.IsValid(fileConfiguration);
|
||||||
|
|
||||||
if (response.Data.IsError)
|
if (response.Data.IsError)
|
||||||
{
|
{
|
||||||
@ -79,13 +86,13 @@ namespace Ocelot.Configuration.Creator
|
|||||||
|
|
||||||
var reRoutes = new List<ReRoute>();
|
var reRoutes = new List<ReRoute>();
|
||||||
|
|
||||||
foreach (var reRoute in _options.Value.ReRoutes)
|
foreach (var reRoute in fileConfiguration.ReRoutes)
|
||||||
{
|
{
|
||||||
var ocelotReRoute = await SetUpReRoute(reRoute, _options.Value.GlobalConfiguration);
|
var ocelotReRoute = await SetUpReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
||||||
reRoutes.Add(ocelotReRoute);
|
reRoutes.Add(ocelotReRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OcelotConfiguration(reRoutes);
|
return new OcelotConfiguration(reRoutes, fileConfiguration.GlobalConfiguration.AdministrationPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
private async Task<ReRoute> SetUpReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
@ -6,5 +7,6 @@ namespace Ocelot.Configuration.Creator
|
|||||||
public interface IOcelotConfigurationCreator
|
public interface IOcelotConfigurationCreator
|
||||||
{
|
{
|
||||||
Task<Response<IOcelotConfiguration>> Create();
|
Task<Response<IOcelotConfiguration>> Create();
|
||||||
|
Task<Response<IOcelotConfiguration>> Create(FileConfiguration fileConfiguration);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using IdentityServer4.AccessTokenValidation;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
using Ocelot.Configuration.Provider;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Creator
|
||||||
|
{
|
||||||
|
public static class IdentityServerConfigurationCreator
|
||||||
|
{
|
||||||
|
public static IdentityServerConfiguration GetIdentityServerConfiguration()
|
||||||
|
{
|
||||||
|
var username = Environment.GetEnvironmentVariable("OCELOT_USERNAME");
|
||||||
|
var hash = Environment.GetEnvironmentVariable("OCELOT_HASH");
|
||||||
|
var salt = Environment.GetEnvironmentVariable("OCELOT_SALT");
|
||||||
|
|
||||||
|
return new IdentityServerConfiguration(
|
||||||
|
"admin",
|
||||||
|
false,
|
||||||
|
SupportedTokens.Both,
|
||||||
|
"secret",
|
||||||
|
new List<string> { "admin", "openid", "offline_access" },
|
||||||
|
"Ocelot Administration",
|
||||||
|
true,
|
||||||
|
GrantTypes.ResourceOwnerPassword,
|
||||||
|
AccessTokenType.Jwt,
|
||||||
|
false,
|
||||||
|
new List<User>
|
||||||
|
{
|
||||||
|
new User("admin", username, hash, salt)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
namespace Ocelot.Configuration.File
|
|
||||||
|
namespace Ocelot.Configuration.File
|
||||||
{
|
{
|
||||||
public class FileGlobalConfiguration
|
public class FileGlobalConfiguration
|
||||||
{
|
{
|
||||||
@ -7,9 +8,11 @@
|
|||||||
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
|
||||||
RateLimitOptions = new FileRateLimitOptions();
|
RateLimitOptions = new FileRateLimitOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string RequestIdKey { get; set; }
|
public string RequestIdKey { get; set; }
|
||||||
|
|
||||||
public FileServiceDiscoveryProvider ServiceDiscoveryProvider {get;set;}
|
public FileServiceDiscoveryProvider ServiceDiscoveryProvider {get;set;}
|
||||||
|
public string AdministrationPath {get;set;}
|
||||||
|
|
||||||
public FileRateLimitOptions RateLimitOptions { get; set; }
|
public FileRateLimitOptions RateLimitOptions { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
namespace Ocelot.Configuration.File
|
||||||
|
{
|
||||||
|
public class FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
public FileGlobalConfiguration()
|
||||||
|
{
|
||||||
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider();
|
||||||
|
RateLimitOptions = new FileRateLimitOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RequestIdKey { get; set; }
|
||||||
|
|
||||||
|
public FileServiceDiscoveryProvider ServiceDiscoveryProvider {get;set;}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
public string AdministrationPath {get;set;}
|
||||||
|
=======
|
||||||
|
|
||||||
|
public FileRateLimitOptions RateLimitOptions { get; set; }
|
||||||
|
>>>>>>> develop
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace Ocelot.Configuration.File
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.File
|
|
||||||
{
|
{
|
||||||
public class FileQoSOptions
|
public class FileQoSOptions
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@ namespace Ocelot.Configuration.File
|
|||||||
{
|
{
|
||||||
public class FileServiceDiscoveryProvider
|
public class FileServiceDiscoveryProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
public string Provider {get;set;}
|
public string Provider {get;set;}
|
||||||
public string Host {get;set;}
|
public string Host {get;set;}
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
@ -5,5 +5,6 @@ namespace Ocelot.Configuration
|
|||||||
public interface IOcelotConfiguration
|
public interface IOcelotConfiguration
|
||||||
{
|
{
|
||||||
List<ReRoute> ReRoutes { get; }
|
List<ReRoute> ReRoutes { get; }
|
||||||
|
string AdministrationPath {get;}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,11 +4,13 @@ namespace Ocelot.Configuration
|
|||||||
{
|
{
|
||||||
public class OcelotConfiguration : IOcelotConfiguration
|
public class OcelotConfiguration : IOcelotConfiguration
|
||||||
{
|
{
|
||||||
public OcelotConfiguration(List<ReRoute> reRoutes)
|
public OcelotConfiguration(List<ReRoute> reRoutes, string administrationPath)
|
||||||
{
|
{
|
||||||
ReRoutes = reRoutes;
|
ReRoutes = reRoutes;
|
||||||
|
AdministrationPath = administrationPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ReRoute> ReRoutes { get; }
|
public List<ReRoute> ReRoutes { get; }
|
||||||
|
public string AdministrationPath {get;}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Provider
|
||||||
|
{
|
||||||
|
public class FileConfigurationProvider : IFileConfigurationProvider
|
||||||
|
{
|
||||||
|
private IFileConfigurationRepository _repo;
|
||||||
|
|
||||||
|
public FileConfigurationProvider(IFileConfigurationRepository repo)
|
||||||
|
{
|
||||||
|
_repo = repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response<FileConfiguration> Get()
|
||||||
|
{
|
||||||
|
var fileConfig = _repo.Get();
|
||||||
|
return new OkResponse<FileConfiguration>(fileConfig.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Provider
|
||||||
|
{
|
||||||
|
public interface IFileConfigurationProvider
|
||||||
|
{
|
||||||
|
Response<FileConfiguration> Get();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using IdentityServer4.AccessTokenValidation;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Provider
|
||||||
|
{
|
||||||
|
public interface IIdentityServerConfiguration
|
||||||
|
{
|
||||||
|
string ApiName { get; }
|
||||||
|
bool RequireHttps { get; }
|
||||||
|
List<string> AllowedScopes { get; }
|
||||||
|
SupportedTokens SupportedTokens { get; }
|
||||||
|
string ApiSecret { get; }
|
||||||
|
string Description {get;}
|
||||||
|
bool Enabled {get;}
|
||||||
|
IEnumerable<string> AllowedGrantTypes {get;}
|
||||||
|
AccessTokenType AccessTokenType {get;}
|
||||||
|
bool RequireClientSecret {get;}
|
||||||
|
List<User> Users {get;}
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,9 @@
|
|||||||
using System.Threading.Tasks;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
namespace Ocelot.Configuration.Provider
|
||||||
{
|
{
|
||||||
public interface IOcelotConfigurationProvider
|
public interface IOcelotConfigurationProvider
|
||||||
{
|
{
|
||||||
Task<Response<IOcelotConfiguration>> Get();
|
Response<IOcelotConfiguration> Get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using IdentityServer4.AccessTokenValidation;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Provider
|
||||||
|
{
|
||||||
|
public class IdentityServerConfiguration : IIdentityServerConfiguration
|
||||||
|
{
|
||||||
|
public IdentityServerConfiguration(
|
||||||
|
string apiName,
|
||||||
|
bool requireHttps,
|
||||||
|
SupportedTokens supportedTokens,
|
||||||
|
string apiSecret,
|
||||||
|
List<string> allowedScopes,
|
||||||
|
string description,
|
||||||
|
bool enabled,
|
||||||
|
IEnumerable<string> grantType,
|
||||||
|
AccessTokenType accessTokenType,
|
||||||
|
bool requireClientSecret,
|
||||||
|
List<User> users)
|
||||||
|
{
|
||||||
|
ApiName = apiName;
|
||||||
|
RequireHttps = requireHttps;
|
||||||
|
SupportedTokens = supportedTokens;
|
||||||
|
ApiSecret = apiSecret;
|
||||||
|
AllowedScopes = allowedScopes;
|
||||||
|
Description = description;
|
||||||
|
Enabled = enabled;
|
||||||
|
AllowedGrantTypes = grantType;
|
||||||
|
AccessTokenType = accessTokenType;
|
||||||
|
RequireClientSecret = requireClientSecret;
|
||||||
|
Users = users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ApiName { get; private set; }
|
||||||
|
public bool RequireHttps { get; private set; }
|
||||||
|
public List<string> AllowedScopes { get; private set; }
|
||||||
|
public SupportedTokens SupportedTokens { get; private set; }
|
||||||
|
public string ApiSecret { get; private set; }
|
||||||
|
public string Description {get;private set;}
|
||||||
|
public bool Enabled {get;private set;}
|
||||||
|
public IEnumerable<string> AllowedGrantTypes {get;private set;}
|
||||||
|
public AccessTokenType AccessTokenType {get;private set;}
|
||||||
|
public bool RequireClientSecret {get;private set;}
|
||||||
|
public List<User> Users {get;private set;}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using Ocelot.Configuration.Repository;
|
||||||
using Ocelot.Configuration.Creator;
|
|
||||||
using Ocelot.Configuration.Repository;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Provider
|
namespace Ocelot.Configuration.Provider
|
||||||
@ -11,16 +9,13 @@ namespace Ocelot.Configuration.Provider
|
|||||||
public class OcelotConfigurationProvider : IOcelotConfigurationProvider
|
public class OcelotConfigurationProvider : IOcelotConfigurationProvider
|
||||||
{
|
{
|
||||||
private readonly IOcelotConfigurationRepository _repo;
|
private readonly IOcelotConfigurationRepository _repo;
|
||||||
private readonly IOcelotConfigurationCreator _creator;
|
|
||||||
|
|
||||||
public OcelotConfigurationProvider(IOcelotConfigurationRepository repo,
|
public OcelotConfigurationProvider(IOcelotConfigurationRepository repo)
|
||||||
IOcelotConfigurationCreator creator)
|
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
_creator = creator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<IOcelotConfiguration>> Get()
|
public Response<IOcelotConfiguration> Get()
|
||||||
{
|
{
|
||||||
var repoConfig = _repo.Get();
|
var repoConfig = _repo.Get();
|
||||||
|
|
||||||
@ -29,20 +24,6 @@ namespace Ocelot.Configuration.Provider
|
|||||||
return new ErrorResponse<IOcelotConfiguration>(repoConfig.Errors);
|
return new ErrorResponse<IOcelotConfiguration>(repoConfig.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (repoConfig.Data == null)
|
|
||||||
{
|
|
||||||
var creatorConfig = await _creator.Create();
|
|
||||||
|
|
||||||
if (creatorConfig.IsError)
|
|
||||||
{
|
|
||||||
return new ErrorResponse<IOcelotConfiguration>(creatorConfig.Errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
_repo.AddOrReplace(creatorConfig.Data);
|
|
||||||
|
|
||||||
return new OkResponse<IOcelotConfiguration>(creatorConfig.Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OkResponse<IOcelotConfiguration>(repoConfig.Data);
|
return new OkResponse<IOcelotConfiguration>(repoConfig.Data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
src/Ocelot/Configuration/Provider/User.cs
Normal file
17
src/Ocelot/Configuration/Provider/User.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
namespace Ocelot.Configuration.Provider
|
||||||
|
{
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
public User(string subject, string userName, string hash, string salt)
|
||||||
|
{
|
||||||
|
Subject = subject;
|
||||||
|
UserName = userName;
|
||||||
|
Hash = hash;
|
||||||
|
Salt = salt;
|
||||||
|
}
|
||||||
|
public string Subject { get; private set; }
|
||||||
|
public string UserName { get; private set; }
|
||||||
|
public string Hash { get; private set; }
|
||||||
|
public string Salt { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Repository
|
||||||
|
{
|
||||||
|
public class FileConfigurationRepository : IFileConfigurationRepository
|
||||||
|
{
|
||||||
|
private static readonly object _lock = new object();
|
||||||
|
public Response<FileConfiguration> Get()
|
||||||
|
{
|
||||||
|
var configFilePath = $"{AppContext.BaseDirectory}/configuration.json";
|
||||||
|
string json = string.Empty;
|
||||||
|
lock(_lock)
|
||||||
|
{
|
||||||
|
json = System.IO.File.ReadAllText(configFilePath);
|
||||||
|
}
|
||||||
|
var fileConfiguration = JsonConvert.DeserializeObject<FileConfiguration>(json);
|
||||||
|
return new OkResponse<FileConfiguration>(fileConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response Set(FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
var configurationPath = $"{AppContext.BaseDirectory}/configuration.json";
|
||||||
|
|
||||||
|
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||||
|
|
||||||
|
lock(_lock)
|
||||||
|
{
|
||||||
|
if (System.IO.File.Exists(configurationPath))
|
||||||
|
{
|
||||||
|
System.IO.File.Delete(configurationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.IO.File.WriteAllText(configurationPath, jsonConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OkResponse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Repository
|
||||||
|
{
|
||||||
|
public interface IFileConfigurationRepository
|
||||||
|
{
|
||||||
|
Response<FileConfiguration> Get();
|
||||||
|
Response Set(FileConfiguration fileConfiguration);
|
||||||
|
}
|
||||||
|
}
|
42
src/Ocelot/Configuration/Setter/FileConfigurationSetter.cs
Normal file
42
src/Ocelot/Configuration/Setter/FileConfigurationSetter.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Setter
|
||||||
|
{
|
||||||
|
public class FileConfigurationSetter : IFileConfigurationSetter
|
||||||
|
{
|
||||||
|
private readonly IOcelotConfigurationRepository _configRepo;
|
||||||
|
private readonly IOcelotConfigurationCreator _configCreator;
|
||||||
|
private readonly IFileConfigurationRepository _repo;
|
||||||
|
|
||||||
|
public FileConfigurationSetter(IOcelotConfigurationRepository configRepo,
|
||||||
|
IOcelotConfigurationCreator configCreator, IFileConfigurationRepository repo)
|
||||||
|
{
|
||||||
|
_configRepo = configRepo;
|
||||||
|
_configCreator = configCreator;
|
||||||
|
_repo = repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Response> Set(FileConfiguration fileConfig)
|
||||||
|
{
|
||||||
|
var response = _repo.Set(fileConfig);
|
||||||
|
|
||||||
|
if(response.IsError)
|
||||||
|
{
|
||||||
|
return new ErrorResponse(response.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = await _configCreator.Create(fileConfig);
|
||||||
|
|
||||||
|
if(!config.IsError)
|
||||||
|
{
|
||||||
|
_configRepo.AddOrReplace(config.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ErrorResponse(config.Errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
src/Ocelot/Configuration/Setter/IFileConfigurationSetter.cs
Normal file
11
src/Ocelot/Configuration/Setter/IFileConfigurationSetter.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
|
namespace Ocelot.Configuration.Setter
|
||||||
|
{
|
||||||
|
public interface IFileConfigurationSetter
|
||||||
|
{
|
||||||
|
Task<Response> Set(FileConfiguration config);
|
||||||
|
}
|
||||||
|
}
|
49
src/Ocelot/Controllers/FileConfigurationController.cs
Normal file
49
src/Ocelot/Controllers/FileConfigurationController.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Configuration.Provider;
|
||||||
|
using Ocelot.Configuration.Setter;
|
||||||
|
|
||||||
|
namespace Ocelot.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
[Route("configuration")]
|
||||||
|
public class FileConfigurationController : Controller
|
||||||
|
{
|
||||||
|
private readonly IFileConfigurationProvider _configGetter;
|
||||||
|
private readonly IFileConfigurationSetter _configSetter;
|
||||||
|
|
||||||
|
public FileConfigurationController(IFileConfigurationProvider getFileConfig, IFileConfigurationSetter configSetter)
|
||||||
|
{
|
||||||
|
_configGetter = getFileConfig;
|
||||||
|
_configSetter = configSetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
var response = _configGetter.Get();
|
||||||
|
|
||||||
|
if(response.IsError)
|
||||||
|
{
|
||||||
|
return new BadRequestObjectResult(response.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OkObjectResult(response.Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Post([FromBody]FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
var response = await _configSetter.Set(fileConfiguration);
|
||||||
|
|
||||||
|
if(response.IsError)
|
||||||
|
{
|
||||||
|
return new BadRequestObjectResult(response.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OkObjectResult(fileConfiguration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using CacheManager.Core;
|
using CacheManager.Core;
|
||||||
|
using IdentityServer4.Models;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Authentication.Handler.Creator;
|
using Ocelot.Authentication.Handler.Creator;
|
||||||
using Ocelot.Authentication.Handler.Factory;
|
using Ocelot.Authentication.Handler.Factory;
|
||||||
using Ocelot.Authorisation;
|
using Ocelot.Authorisation;
|
||||||
using Ocelot.Cache;
|
using Ocelot.Cache;
|
||||||
using Ocelot.Claims;
|
using Ocelot.Claims;
|
||||||
|
using Ocelot.Configuration.Authentication;
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Parser;
|
using Ocelot.Configuration.Parser;
|
||||||
using Ocelot.Configuration.Provider;
|
using Ocelot.Configuration.Provider;
|
||||||
using Ocelot.Configuration.Repository;
|
using Ocelot.Configuration.Repository;
|
||||||
|
using Ocelot.Configuration.Setter;
|
||||||
using Ocelot.Configuration.Validator;
|
using Ocelot.Configuration.Validator;
|
||||||
using Ocelot.DownstreamRouteFinder.Finder;
|
using Ocelot.DownstreamRouteFinder.Finder;
|
||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
@ -25,44 +29,86 @@ using Ocelot.Infrastructure.Claims.Parser;
|
|||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.LoadBalancer.LoadBalancers;
|
using Ocelot.LoadBalancer.LoadBalancers;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Middleware;
|
||||||
using Ocelot.QueryStrings;
|
using Ocelot.QueryStrings;
|
||||||
using Ocelot.Request.Builder;
|
using Ocelot.Request.Builder;
|
||||||
using Ocelot.Requester;
|
using Ocelot.Requester;
|
||||||
using Ocelot.Requester.QoS;
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Responder;
|
using Ocelot.Responder;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
|
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
||||||
using Ocelot.RateLimit;
|
using Ocelot.RateLimit;
|
||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
public static class ServiceCollectionExtensions
|
public static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
|
|
||||||
public static IServiceCollection AddOcelotOutputCaching(this IServiceCollection services, Action<ConfigurationBuilderCachePart> settings)
|
public static IServiceCollection AddOcelotOutputCaching(this IServiceCollection services, Action<ConfigurationBuilderCachePart> settings)
|
||||||
{
|
{
|
||||||
var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
|
var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
|
||||||
var ocelotCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
|
var ocelotCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
|
||||||
|
|
||||||
services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
|
services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
|
||||||
services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotCacheManager);
|
services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotCacheManager);
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
public static IServiceCollection AddOcelotFileConfiguration(this IServiceCollection services, IConfigurationRoot configurationRoot)
|
|
||||||
|
public static IServiceCollection AddOcelot(this IServiceCollection services, IConfigurationRoot configurationRoot)
|
||||||
{
|
{
|
||||||
services.Configure<FileConfiguration>(configurationRoot);
|
services.Configure<FileConfiguration>(configurationRoot);
|
||||||
|
|
||||||
services.AddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
services.AddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
||||||
services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
||||||
services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
|
services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
|
||||||
|
services.AddSingleton<IBaseUrlFinder, BaseUrlFinder>();
|
||||||
|
|
||||||
return services;
|
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
|
||||||
}
|
|
||||||
|
|
||||||
public static IServiceCollection AddOcelot(this IServiceCollection services)
|
if(identityServerConfiguration != null)
|
||||||
{
|
{
|
||||||
services.AddMvcCore().AddJsonFormatters();
|
services.AddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
|
||||||
|
services.AddSingleton<IHashMatcher, HashMatcher>();
|
||||||
|
services.AddIdentityServer()
|
||||||
|
.AddTemporarySigningCredential()
|
||||||
|
.AddInMemoryApiResources(new List<ApiResource>
|
||||||
|
{
|
||||||
|
new ApiResource
|
||||||
|
{
|
||||||
|
Name = identityServerConfiguration.ApiName,
|
||||||
|
Description = identityServerConfiguration.Description,
|
||||||
|
Enabled = identityServerConfiguration.Enabled,
|
||||||
|
DisplayName = identityServerConfiguration.ApiName,
|
||||||
|
Scopes = identityServerConfiguration.AllowedScopes.Select(x => new Scope(x)).ToList(),
|
||||||
|
ApiSecrets = new List<Secret>
|
||||||
|
{
|
||||||
|
new Secret
|
||||||
|
{
|
||||||
|
Value = identityServerConfiguration.ApiSecret.Sha256()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.AddInMemoryClients(new List<Client>
|
||||||
|
{
|
||||||
|
new Client
|
||||||
|
{
|
||||||
|
ClientId = identityServerConfiguration.ApiName,
|
||||||
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
|
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
|
||||||
|
AllowedScopes = identityServerConfiguration.AllowedScopes,
|
||||||
|
AccessTokenType = identityServerConfiguration.AccessTokenType,
|
||||||
|
Enabled = identityServerConfiguration.Enabled,
|
||||||
|
RequireClientSecret = identityServerConfiguration.RequireClientSecret
|
||||||
|
}
|
||||||
|
}).AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
|
||||||
|
}
|
||||||
|
|
||||||
|
services.AddMvcCore()
|
||||||
|
.AddAuthorization()
|
||||||
|
.AddJsonFormatters();
|
||||||
services.AddLogging();
|
services.AddLogging();
|
||||||
|
services.AddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
|
||||||
|
services.AddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
|
||||||
|
services.AddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
|
||||||
services.AddSingleton<IQosProviderHouse, QosProviderHouse>();
|
services.AddSingleton<IQosProviderHouse, QosProviderHouse>();
|
||||||
services.AddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
services.AddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
||||||
services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
||||||
|
@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using CacheManager.Core;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Ocelot.Authentication.Handler.Creator;
|
||||||
|
using Ocelot.Authentication.Handler.Factory;
|
||||||
|
using Ocelot.Authorisation;
|
||||||
|
using Ocelot.Cache;
|
||||||
|
using Ocelot.Claims;
|
||||||
|
using Ocelot.Configuration.Authentication;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Configuration.Parser;
|
||||||
|
using Ocelot.Configuration.Provider;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
|
using Ocelot.Configuration.Setter;
|
||||||
|
using Ocelot.Configuration.Validator;
|
||||||
|
using Ocelot.DownstreamRouteFinder.Finder;
|
||||||
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
|
using Ocelot.DownstreamUrlCreator;
|
||||||
|
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||||
|
using Ocelot.Headers;
|
||||||
|
using Ocelot.Infrastructure.Claims.Parser;
|
||||||
|
using Ocelot.Infrastructure.RequestData;
|
||||||
|
using Ocelot.LoadBalancer.LoadBalancers;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.QueryStrings;
|
||||||
|
using Ocelot.Request.Builder;
|
||||||
|
using Ocelot.Requester;
|
||||||
|
using Ocelot.Requester.QoS;
|
||||||
|
using Ocelot.Responder;
|
||||||
|
using Ocelot.ServiceDiscovery;
|
||||||
|
<<<<<<< HEAD
|
||||||
|
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
||||||
|
=======
|
||||||
|
using Ocelot.RateLimit;
|
||||||
|
>>>>>>> develop
|
||||||
|
|
||||||
|
namespace Ocelot.DependencyInjection
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddOcelotOutputCaching(this IServiceCollection services, Action<ConfigurationBuilderCachePart> settings)
|
||||||
|
{
|
||||||
|
var cacheManagerOutputCache = CacheFactory.Build<HttpResponseMessage>("OcelotOutputCache", settings);
|
||||||
|
var ocelotCacheManager = new OcelotCacheManagerCache<HttpResponseMessage>(cacheManagerOutputCache);
|
||||||
|
services.AddSingleton<ICacheManager<HttpResponseMessage>>(cacheManagerOutputCache);
|
||||||
|
services.AddSingleton<IOcelotCache<HttpResponseMessage>>(ocelotCacheManager);
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IServiceCollection AddOcelot(this IServiceCollection services, IConfigurationRoot configurationRoot)
|
||||||
|
{
|
||||||
|
services.Configure<FileConfiguration>(configurationRoot);
|
||||||
|
services.AddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
||||||
|
services.AddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
||||||
|
services.AddSingleton<IConfigurationValidator, FileConfigurationValidator>();
|
||||||
|
services.AddSingleton<IBaseUrlFinder, BaseUrlFinder>();
|
||||||
|
|
||||||
|
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
|
||||||
|
|
||||||
|
if(identityServerConfiguration != null)
|
||||||
|
{
|
||||||
|
services.AddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
|
||||||
|
services.AddSingleton<IHashMatcher, HashMatcher>();
|
||||||
|
services.AddIdentityServer()
|
||||||
|
.AddTemporarySigningCredential()
|
||||||
|
.AddInMemoryApiResources(new List<ApiResource>
|
||||||
|
{
|
||||||
|
new ApiResource
|
||||||
|
{
|
||||||
|
Name = identityServerConfiguration.ApiName,
|
||||||
|
Description = identityServerConfiguration.Description,
|
||||||
|
Enabled = identityServerConfiguration.Enabled,
|
||||||
|
DisplayName = identityServerConfiguration.ApiName,
|
||||||
|
Scopes = identityServerConfiguration.AllowedScopes.Select(x => new Scope(x)).ToList(),
|
||||||
|
ApiSecrets = new List<Secret>
|
||||||
|
{
|
||||||
|
new Secret
|
||||||
|
{
|
||||||
|
Value = identityServerConfiguration.ApiSecret.Sha256()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.AddInMemoryClients(new List<Client>
|
||||||
|
{
|
||||||
|
new Client
|
||||||
|
{
|
||||||
|
ClientId = identityServerConfiguration.ApiName,
|
||||||
|
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
|
||||||
|
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
|
||||||
|
AllowedScopes = identityServerConfiguration.AllowedScopes,
|
||||||
|
AccessTokenType = identityServerConfiguration.AccessTokenType,
|
||||||
|
Enabled = identityServerConfiguration.Enabled,
|
||||||
|
RequireClientSecret = identityServerConfiguration.RequireClientSecret
|
||||||
|
}
|
||||||
|
}).AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
|
||||||
|
}
|
||||||
|
|
||||||
|
services.AddMvcCore()
|
||||||
|
.AddAuthorization()
|
||||||
|
.AddJsonFormatters();
|
||||||
|
services.AddLogging();
|
||||||
|
services.AddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
|
||||||
|
services.AddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
|
||||||
|
services.AddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
|
||||||
|
services.AddSingleton<IQosProviderHouse, QosProviderHouse>();
|
||||||
|
services.AddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
||||||
|
services.AddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
||||||
|
services.AddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
|
||||||
|
services.AddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
|
||||||
|
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||||
|
services.AddSingleton<IUrlBuilder, UrlBuilder>();
|
||||||
|
services.AddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
||||||
|
services.AddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
|
||||||
|
services.AddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
||||||
|
services.AddSingleton<IAuthoriser, ClaimsAuthoriser>();
|
||||||
|
services.AddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
|
||||||
|
services.AddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
|
||||||
|
services.AddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
|
||||||
|
services.AddSingleton<IClaimsParser, ClaimsParser>();
|
||||||
|
services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
||||||
|
services.AddSingleton<IUrlPathPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
||||||
|
services.AddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
|
||||||
|
services.AddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
|
||||||
|
services.AddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
||||||
|
services.AddSingleton<IHttpResponder, HttpContextResponder>();
|
||||||
|
services.AddSingleton<IRequestCreator, HttpRequestCreator>();
|
||||||
|
services.AddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
|
||||||
|
services.AddSingleton<IAuthenticationHandlerFactory, AuthenticationHandlerFactory>();
|
||||||
|
services.AddSingleton<IAuthenticationHandlerCreator, AuthenticationHandlerCreator>();
|
||||||
|
services.AddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
|
||||||
|
|
||||||
|
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
|
||||||
|
// could maybe use a scoped data repository
|
||||||
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
|
services.AddScoped<IRequestScopedDataRepository, HttpDataRepository>();
|
||||||
|
services.AddMemoryCache();
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Configuration.Provider;
|
using Ocelot.Configuration.Provider;
|
||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
@ -22,9 +21,9 @@ namespace Ocelot.DownstreamRouteFinder.Finder
|
|||||||
_urlPathPlaceholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
|
_urlPathPlaceholderNameAndValueFinder = urlPathPlaceholderNameAndValueFinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
|
public Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod)
|
||||||
{
|
{
|
||||||
var configuration = await _configProvider.Get();
|
var configuration = _configProvider.Get();
|
||||||
|
|
||||||
var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod.Method, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase));
|
var applicableReRoutes = configuration.Data.ReRoutes.Where(r => string.Equals(r.UpstreamHttpMethod.Method, upstreamHttpMethod, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
using System.Threading.Tasks;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Responses;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamRouteFinder.Finder
|
namespace Ocelot.DownstreamRouteFinder.Finder
|
||||||
{
|
{
|
||||||
public interface IDownstreamRouteFinder
|
public interface IDownstreamRouteFinder
|
||||||
{
|
{
|
||||||
Task<Response<DownstreamRoute>> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod);
|
Response<DownstreamRoute> FindDownstreamRoute(string upstreamUrlPath, string upstreamHttpMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.DownstreamRouteFinder.Finder;
|
using Ocelot.DownstreamRouteFinder.Finder;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
@ -34,7 +33,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
|||||||
|
|
||||||
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
|
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
|
||||||
|
|
||||||
var downstreamRoute = await _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method);
|
var downstreamRoute = _downstreamRouteFinder.FindDownstreamRoute(upstreamUrlPath, context.Request.Method);
|
||||||
|
|
||||||
if (downstreamRoute.IsError)
|
if (downstreamRoute.IsError)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using Ocelot.Configuration;
|
using Ocelot.Responses;
|
||||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
|
||||||
using Ocelot.Responses;
|
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator
|
namespace Ocelot.DownstreamUrlCreator
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Configuration;
|
|
||||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Values;
|
|
||||||
|
|
||||||
namespace Ocelot.DownstreamUrlCreator.Middleware
|
namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ocelot.Configuration;
|
|
||||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
@ -3,8 +3,6 @@ using Ocelot.Responses;
|
|||||||
|
|
||||||
namespace Ocelot.Headers
|
namespace Ocelot.Headers
|
||||||
{
|
{
|
||||||
using System;
|
|
||||||
|
|
||||||
public class RemoveOutputHeaders : IRemoveOutputHeaders
|
public class RemoveOutputHeaders : IRemoveOutputHeaders
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.LoadBalancer.LoadBalancers
|
namespace Ocelot.LoadBalancer.LoadBalancers
|
||||||
|
21
src/Ocelot/Middleware/BaseUrlFinder.cs
Normal file
21
src/Ocelot/Middleware/BaseUrlFinder.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
||||||
|
namespace Ocelot.Middleware
|
||||||
|
{
|
||||||
|
public class BaseUrlFinder : IBaseUrlFinder
|
||||||
|
{
|
||||||
|
private readonly IWebHostBuilder _webHostBuilder;
|
||||||
|
|
||||||
|
public BaseUrlFinder(IWebHostBuilder webHostBuilder)
|
||||||
|
{
|
||||||
|
_webHostBuilder = webHostBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Find()
|
||||||
|
{
|
||||||
|
var baseSchemeUrlAndPort = _webHostBuilder.GetSetting(WebHostDefaults.ServerUrlsKey);
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(baseSchemeUrlAndPort) ? "http://localhost:5000" : baseSchemeUrlAndPort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
src/Ocelot/Middleware/IBaseUrlFinder.cs
Normal file
7
src/Ocelot/Middleware/IBaseUrlFinder.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.Middleware
|
||||||
|
{
|
||||||
|
public interface IBaseUrlFinder
|
||||||
|
{
|
||||||
|
string Find();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using System.Collections.Generic;
|
||||||
|
using IdentityServer4.AccessTokenValidation;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Ocelot.Authentication.Middleware;
|
using Ocelot.Authentication.Middleware;
|
||||||
using Ocelot.Cache.Middleware;
|
using Ocelot.Cache.Middleware;
|
||||||
using Ocelot.Claims.Middleware;
|
using Ocelot.Claims.Middleware;
|
||||||
@ -18,8 +20,13 @@ namespace Ocelot.Middleware
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Authorisation.Middleware;
|
using Authorisation.Middleware;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Provider;
|
using Ocelot.Configuration.Provider;
|
||||||
|
using Ocelot.Configuration.Setter;
|
||||||
using Ocelot.LoadBalancer.Middleware;
|
using Ocelot.LoadBalancer.Middleware;
|
||||||
|
|
||||||
public static class OcelotMiddlewareExtensions
|
public static class OcelotMiddlewareExtensions
|
||||||
@ -29,10 +36,10 @@ namespace Ocelot.Middleware
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="builder"></param>
|
/// <param name="builder"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IApplicationBuilder UseOcelot(this IApplicationBuilder builder)
|
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
CreateConfiguration(builder);
|
await builder.UseOcelot(new OcelotMiddlewareConfiguration());
|
||||||
builder.UseOcelot(new OcelotMiddlewareConfiguration());
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,9 +49,9 @@ namespace Ocelot.Middleware
|
|||||||
/// <param name="builder"></param>
|
/// <param name="builder"></param>
|
||||||
/// <param name="middlewareConfiguration"></param>
|
/// <param name="middlewareConfiguration"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IApplicationBuilder UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration)
|
public static async Task<IApplicationBuilder> UseOcelot(this IApplicationBuilder builder, OcelotMiddlewareConfiguration middlewareConfiguration)
|
||||||
{
|
{
|
||||||
CreateConfiguration(builder);
|
await CreateAdministrationArea(builder);
|
||||||
|
|
||||||
// This is registered to catch any global exceptions that are not handled
|
// This is registered to catch any global exceptions that are not handled
|
||||||
builder.UseExceptionHandlerMiddleware();
|
builder.UseExceptionHandlerMiddleware();
|
||||||
@ -126,15 +133,61 @@ namespace Ocelot.Middleware
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CreateConfiguration(IApplicationBuilder builder)
|
private static async Task<IOcelotConfiguration> CreateConfiguration(IApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
|
var fileConfig = (IOptions<FileConfiguration>)builder.ApplicationServices.GetService(typeof(IOptions<FileConfiguration>));
|
||||||
|
|
||||||
|
var configSetter = (IFileConfigurationSetter)builder.ApplicationServices.GetService(typeof(IFileConfigurationSetter));
|
||||||
|
|
||||||
var configProvider = (IOcelotConfigurationProvider)builder.ApplicationServices.GetService(typeof(IOcelotConfigurationProvider));
|
var configProvider = (IOcelotConfigurationProvider)builder.ApplicationServices.GetService(typeof(IOcelotConfigurationProvider));
|
||||||
|
|
||||||
var config = configProvider.Get();
|
var config = await configSetter.Set(fileConfig.Value);
|
||||||
|
|
||||||
if(config == null)
|
if(config == null || config.IsError)
|
||||||
{
|
{
|
||||||
throw new Exception("Unable to start Ocelot: configuration was null");
|
throw new Exception("Unable to start Ocelot: configuration was not set up correctly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var ocelotConfiguration = configProvider.Get();
|
||||||
|
|
||||||
|
if(ocelotConfiguration == null || ocelotConfiguration.Data == null || ocelotConfiguration.IsError)
|
||||||
|
{
|
||||||
|
throw new Exception("Unable to start Ocelot: ocelot configuration was not returned by provider.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ocelotConfiguration.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task CreateAdministrationArea(IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
var configuration = await CreateConfiguration(builder);
|
||||||
|
|
||||||
|
var identityServerConfiguration = (IIdentityServerConfiguration)builder.ApplicationServices.GetService(typeof(IIdentityServerConfiguration));
|
||||||
|
|
||||||
|
if(!string.IsNullOrEmpty(configuration.AdministrationPath) && identityServerConfiguration != null)
|
||||||
|
{
|
||||||
|
var urlFinder = (IBaseUrlFinder)builder.ApplicationServices.GetService(typeof(IBaseUrlFinder));
|
||||||
|
|
||||||
|
var baseSchemeUrlAndPort = urlFinder.Find();
|
||||||
|
|
||||||
|
builder.Map(configuration.AdministrationPath, app =>
|
||||||
|
{
|
||||||
|
var identityServerUrl = $"{baseSchemeUrlAndPort}/{configuration.AdministrationPath.Remove(0,1)}";
|
||||||
|
|
||||||
|
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
|
||||||
|
{
|
||||||
|
Authority = identityServerUrl,
|
||||||
|
ApiName = identityServerConfiguration.ApiName,
|
||||||
|
RequireHttpsMetadata = identityServerConfiguration.RequireHttps,
|
||||||
|
AllowedScopes = identityServerConfiguration.AllowedScopes,
|
||||||
|
SupportedTokens = SupportedTokens.Both,
|
||||||
|
ApiSecret = identityServerConfiguration.ApiSecret
|
||||||
|
});
|
||||||
|
|
||||||
|
app.UseIdentityServer();
|
||||||
|
|
||||||
|
app.UseMvc();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
// General Information about an assembly is controlled through the following
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@ -54,7 +55,10 @@ namespace Ocelot.Responder
|
|||||||
|
|
||||||
using (Stream stream = new MemoryStream(content))
|
using (Stream stream = new MemoryStream(content))
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(context.Response.Body);
|
if (response.StatusCode != HttpStatusCode.NotModified)
|
||||||
|
{
|
||||||
|
await stream.CopyToAsync(context.Response.Body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ namespace Ocelot.Responses
|
|||||||
{
|
{
|
||||||
public class ErrorResponse : Response
|
public class ErrorResponse : Response
|
||||||
{
|
{
|
||||||
|
public ErrorResponse(Error error) : base(new List<Error>{error})
|
||||||
|
{
|
||||||
|
}
|
||||||
public ErrorResponse(List<Error> errors) : base(errors)
|
public ErrorResponse(List<Error> errors) : base(errors)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,14 @@
|
|||||||
"CacheManager.Microsoft.Extensions.Configuration": "0.9.2",
|
"CacheManager.Microsoft.Extensions.Configuration": "0.9.2",
|
||||||
"CacheManager.Microsoft.Extensions.Logging": "0.9.2",
|
"CacheManager.Microsoft.Extensions.Logging": "0.9.2",
|
||||||
"Consul": "0.7.2.1",
|
"Consul": "0.7.2.1",
|
||||||
"Polly": "5.0.3"
|
"Polly": "5.0.3",
|
||||||
|
"IdentityServer4": "1.0.1",
|
||||||
|
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "1.1.0"
|
||||||
},
|
},
|
||||||
"runtimes": {
|
"runtimes": {
|
||||||
"win10-x64": {},
|
"win10-x64": {},
|
||||||
"osx.10.11-x64": {},
|
"osx.10.11-x64": {},
|
||||||
|
"osx.10.12-x64": {},
|
||||||
"win7-x64": {}
|
"win7-x64": {}
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
88
src/Ocelot/project.json.orig
Normal file
88
src/Ocelot/project.json.orig
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.0-dev",
|
||||||
|
<<<<<<< HEAD
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.Json": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Console": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Http": "1.1.0",
|
||||||
|
"System.Text.RegularExpressions": "4.3.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.OAuth": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Cookies": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Google": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Facebook": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Twitter": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication": "1.1.0",
|
||||||
|
"IdentityServer4.AccessTokenValidation": "1.0.2",
|
||||||
|
"Microsoft.AspNetCore.Mvc": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
||||||
|
"Microsoft.NETCore.App": "1.1.0",
|
||||||
|
"CacheManager.Core": "0.9.2",
|
||||||
|
"CacheManager.Microsoft.Extensions.Configuration": "0.9.2",
|
||||||
|
"CacheManager.Microsoft.Extensions.Logging": "0.9.2",
|
||||||
|
"Consul": "0.7.2.1",
|
||||||
|
"Polly": "5.0.3",
|
||||||
|
"IdentityServer4": "1.0.1",
|
||||||
|
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "1.1.0"
|
||||||
|
},
|
||||||
|
=======
|
||||||
|
"title": "Ocelot",
|
||||||
|
"summary": "API Gateway created using .NET core.",
|
||||||
|
"projectUrl": "https://github.com/TomPallister/Ocelot",
|
||||||
|
"description": "This project is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system. In particular I want easy integration with IdentityServer reference and bearer tokens. We have been unable to find this in my current workplace without having to write our own Javascript middlewares to handle the IdentityServer reference tokens. We would rather use the IdentityServer code that already exists to do this. Ocelot is a bunch of middlewares in a specific order. Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is stored in a per request scoped repository and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features.",
|
||||||
|
"tags": [
|
||||||
|
"API Gateway",
|
||||||
|
".NET core"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.Json": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Console": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Http": "1.1.0",
|
||||||
|
"System.Text.RegularExpressions": "4.3.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.OAuth": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Cookies": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Google": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Facebook": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.Twitter": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication.MicrosoftAccount": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Authentication": "1.1.0",
|
||||||
|
"IdentityServer4.AccessTokenValidation": "1.0.2",
|
||||||
|
"Microsoft.AspNetCore.Mvc": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
||||||
|
"Microsoft.NETCore.App": "1.1.0",
|
||||||
|
"CacheManager.Core": "0.9.2",
|
||||||
|
"CacheManager.Microsoft.Extensions.Configuration": "0.9.2",
|
||||||
|
"CacheManager.Microsoft.Extensions.Logging": "0.9.2",
|
||||||
|
"Consul": "0.7.2.1",
|
||||||
|
"Polly": "5.0.3"
|
||||||
|
},
|
||||||
|
>>>>>>> develop
|
||||||
|
"runtimes": {
|
||||||
|
"win10-x64": {},
|
||||||
|
"osx.10.11-x64": {},
|
||||||
|
"osx.10.12-x64": {},
|
||||||
|
"win7-x64": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netcoreapp1.1": {
|
||||||
|
"imports": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_return_response_200_and_foward_claim_as_header()
|
public void should_return_internal_server_error_if_downstream_service_returns_internal_server_error()
|
||||||
{
|
{
|
||||||
var configuration = new FileConfiguration
|
var configuration = new FileConfiguration
|
||||||
{
|
{
|
||||||
@ -29,9 +29,12 @@ namespace Ocelot.AcceptanceTests
|
|||||||
{
|
{
|
||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamPathTemplate = "http://localhost:53876/",
|
DownstreamPathTemplate = "/",
|
||||||
UpstreamPathTemplate = "/",
|
UpstreamPathTemplate = "/",
|
||||||
UpstreamHttpMethod = "Get"
|
UpstreamHttpMethod = "Get",
|
||||||
|
DownstreamPort = 53876,
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHost = "localhost"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,8 @@ namespace Ocelot.AcceptanceTests
|
|||||||
private BearerToken _token;
|
private BearerToken _token;
|
||||||
public HttpClient OcelotClient => _ocelotClient;
|
public HttpClient OcelotClient => _ocelotClient;
|
||||||
public string RequestIdKey = "OcRequestId";
|
public string RequestIdKey = "OcRequestId";
|
||||||
private Random _random;
|
private readonly Random _random;
|
||||||
|
private IWebHostBuilder _webHostBuilder;
|
||||||
|
|
||||||
public Steps()
|
public Steps()
|
||||||
{
|
{
|
||||||
@ -70,12 +71,40 @@ namespace Ocelot.AcceptanceTests
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void GivenOcelotIsRunning()
|
public void GivenOcelotIsRunning()
|
||||||
{
|
{
|
||||||
_ocelotServer = new TestServer(new WebHostBuilder()
|
_webHostBuilder = new WebHostBuilder();
|
||||||
|
|
||||||
|
_webHostBuilder.ConfigureServices(s =>
|
||||||
|
{
|
||||||
|
s.AddSingleton(_webHostBuilder);
|
||||||
|
});
|
||||||
|
|
||||||
|
_ocelotServer = new TestServer(_webHostBuilder
|
||||||
.UseStartup<Startup>());
|
.UseStartup<Startup>());
|
||||||
|
|
||||||
_ocelotClient = _ocelotServer.CreateClient();
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ThenTheResponseShouldBe(FileConfiguration expected)
|
||||||
|
{
|
||||||
|
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
|
||||||
|
|
||||||
|
response.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||||
|
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
|
||||||
|
|
||||||
|
for(var i = 0; i < response.ReRoutes.Count; i++)
|
||||||
|
{
|
||||||
|
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
|
||||||
|
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
|
||||||
|
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
|
||||||
|
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
|
||||||
|
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.ReRoutes[i].UpstreamPathTemplate);
|
||||||
|
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.ReRoutes[i].UpstreamHttpMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -89,7 +118,14 @@ namespace Ocelot.AcceptanceTests
|
|||||||
|
|
||||||
var configuration = builder.Build();
|
var configuration = builder.Build();
|
||||||
|
|
||||||
_ocelotServer = new TestServer(new WebHostBuilder()
|
_webHostBuilder = new WebHostBuilder();
|
||||||
|
|
||||||
|
_webHostBuilder.ConfigureServices(s =>
|
||||||
|
{
|
||||||
|
s.AddSingleton(_webHostBuilder);
|
||||||
|
});
|
||||||
|
|
||||||
|
_ocelotServer = new TestServer(_webHostBuilder
|
||||||
.UseConfiguration(configuration)
|
.UseConfiguration(configuration)
|
||||||
.ConfigureServices(s =>
|
.ConfigureServices(s =>
|
||||||
{
|
{
|
||||||
@ -101,9 +137,9 @@ namespace Ocelot.AcceptanceTests
|
|||||||
})
|
})
|
||||||
.WithDictionaryHandle();
|
.WithDictionaryHandle();
|
||||||
};
|
};
|
||||||
|
|
||||||
s.AddOcelotOutputCaching(settings);
|
s.AddOcelotOutputCaching(settings);
|
||||||
s.AddOcelotFileConfiguration(configuration);
|
s.AddOcelot(configuration);
|
||||||
s.AddOcelot();
|
|
||||||
})
|
})
|
||||||
.ConfigureLogging(l =>
|
.ConfigureLogging(l =>
|
||||||
{
|
{
|
||||||
@ -112,7 +148,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
})
|
})
|
||||||
.Configure(a =>
|
.Configure(a =>
|
||||||
{
|
{
|
||||||
a.UseOcelot(ocelotMiddlewareConfig);
|
a.UseOcelot(ocelotMiddlewareConfig).Wait();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
_ocelotClient = _ocelotServer.CreateClient();
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
@ -146,6 +182,26 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GivenIHaveAnOcelotToken(string adminPath)
|
||||||
|
{
|
||||||
|
var tokenUrl = $"{adminPath}/connect/token";
|
||||||
|
var formData = new List<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("client_id", "admin"),
|
||||||
|
new KeyValuePair<string, string>("client_secret", "secret"),
|
||||||
|
new KeyValuePair<string, string>("scope", "admin"),
|
||||||
|
new KeyValuePair<string, string>("username", "admin"),
|
||||||
|
new KeyValuePair<string, string>("password", "admin"),
|
||||||
|
new KeyValuePair<string, string>("grant_type", "password")
|
||||||
|
};
|
||||||
|
var content = new FormUrlEncodedContent(formData);
|
||||||
|
|
||||||
|
var response = _ocelotClient.PostAsync(tokenUrl, content).Result;
|
||||||
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
||||||
|
}
|
||||||
|
|
||||||
public void VerifyIdentiryServerStarted(string url)
|
public void VerifyIdentiryServerStarted(string url)
|
||||||
{
|
{
|
||||||
using (var httpClient = new HttpClient())
|
using (var httpClient = new HttpClient())
|
||||||
@ -155,7 +211,6 @@ namespace Ocelot.AcceptanceTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void WhenIGetUrlOnTheApiGateway(string url)
|
public void WhenIGetUrlOnTheApiGateway(string url)
|
||||||
{
|
{
|
||||||
_response = _ocelotClient.GetAsync(url).Result;
|
_response = _ocelotClient.GetAsync(url).Result;
|
||||||
|
305
test/Ocelot.AcceptanceTests/Steps.cs.orig
Normal file
305
test/Ocelot.AcceptanceTests/Steps.cs.orig
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CacheManager.Core;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.TestHost;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.DependencyInjection;
|
||||||
|
using Ocelot.ManualTest;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Shouldly;
|
||||||
|
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
||||||
|
|
||||||
|
namespace Ocelot.AcceptanceTests
|
||||||
|
{
|
||||||
|
public class Steps : IDisposable
|
||||||
|
{
|
||||||
|
private TestServer _ocelotServer;
|
||||||
|
private HttpClient _ocelotClient;
|
||||||
|
private HttpResponseMessage _response;
|
||||||
|
private HttpContent _postContent;
|
||||||
|
private BearerToken _token;
|
||||||
|
public HttpClient OcelotClient => _ocelotClient;
|
||||||
|
public string RequestIdKey = "OcRequestId";
|
||||||
|
private readonly Random _random;
|
||||||
|
private IWebHostBuilder _webHostBuilder;
|
||||||
|
|
||||||
|
public Steps()
|
||||||
|
{
|
||||||
|
_random = new Random();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
var configurationPath = TestConfiguration.ConfigurationPath;
|
||||||
|
|
||||||
|
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||||
|
|
||||||
|
if (File.Exists(configurationPath))
|
||||||
|
{
|
||||||
|
File.Delete(configurationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration, string configurationPath)
|
||||||
|
{
|
||||||
|
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||||
|
|
||||||
|
if (File.Exists(configurationPath))
|
||||||
|
{
|
||||||
|
File.Delete(configurationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public void GivenOcelotIsRunning()
|
||||||
|
{
|
||||||
|
_webHostBuilder = new WebHostBuilder();
|
||||||
|
|
||||||
|
_webHostBuilder.ConfigureServices(s =>
|
||||||
|
{
|
||||||
|
s.AddSingleton(_webHostBuilder);
|
||||||
|
});
|
||||||
|
|
||||||
|
_ocelotServer = new TestServer(_webHostBuilder
|
||||||
|
.UseStartup<Startup>());
|
||||||
|
|
||||||
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ThenTheResponseShouldBe(FileConfiguration expected)
|
||||||
|
{
|
||||||
|
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
|
||||||
|
|
||||||
|
response.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||||
|
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
|
||||||
|
|
||||||
|
for(var i = 0; i < response.ReRoutes.Count; i++)
|
||||||
|
{
|
||||||
|
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
|
||||||
|
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
|
||||||
|
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
|
||||||
|
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
|
||||||
|
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.ReRoutes[i].UpstreamPathTemplate);
|
||||||
|
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.ReRoutes[i].UpstreamHttpMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public void GivenOcelotIsRunning(OcelotMiddlewareConfiguration ocelotMiddlewareConfig)
|
||||||
|
{
|
||||||
|
var builder = new ConfigurationBuilder()
|
||||||
|
.SetBasePath(Directory.GetCurrentDirectory())
|
||||||
|
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||||
|
.AddJsonFile("configuration.json")
|
||||||
|
.AddEnvironmentVariables();
|
||||||
|
|
||||||
|
var configuration = builder.Build();
|
||||||
|
|
||||||
|
_webHostBuilder = new WebHostBuilder();
|
||||||
|
|
||||||
|
_webHostBuilder.ConfigureServices(s =>
|
||||||
|
{
|
||||||
|
s.AddSingleton(_webHostBuilder);
|
||||||
|
});
|
||||||
|
|
||||||
|
_ocelotServer = new TestServer(_webHostBuilder
|
||||||
|
.UseConfiguration(configuration)
|
||||||
|
.ConfigureServices(s =>
|
||||||
|
{
|
||||||
|
Action<ConfigurationBuilderCachePart> settings = (x) =>
|
||||||
|
{
|
||||||
|
x.WithMicrosoftLogging(log =>
|
||||||
|
{
|
||||||
|
log.AddConsole(LogLevel.Debug);
|
||||||
|
})
|
||||||
|
.WithDictionaryHandle();
|
||||||
|
};
|
||||||
|
<<<<<<< HEAD
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> develop
|
||||||
|
s.AddOcelotOutputCaching(settings);
|
||||||
|
s.AddOcelot(configuration);
|
||||||
|
})
|
||||||
|
.ConfigureLogging(l =>
|
||||||
|
{
|
||||||
|
l.AddConsole(configuration.GetSection("Logging"));
|
||||||
|
l.AddDebug();
|
||||||
|
})
|
||||||
|
.Configure(a =>
|
||||||
|
{
|
||||||
|
a.UseOcelot(ocelotMiddlewareConfig).Wait();
|
||||||
|
}));
|
||||||
|
|
||||||
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GivenIHaveAddedATokenToMyRequest()
|
||||||
|
{
|
||||||
|
_ocelotClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GivenIHaveAToken(string url)
|
||||||
|
{
|
||||||
|
var tokenUrl = $"{url}/connect/token";
|
||||||
|
var formData = new List<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("client_id", "client"),
|
||||||
|
new KeyValuePair<string, string>("client_secret", "secret"),
|
||||||
|
new KeyValuePair<string, string>("scope", "api"),
|
||||||
|
new KeyValuePair<string, string>("username", "test"),
|
||||||
|
new KeyValuePair<string, string>("password", "test"),
|
||||||
|
new KeyValuePair<string, string>("grant_type", "password")
|
||||||
|
};
|
||||||
|
var content = new FormUrlEncodedContent(formData);
|
||||||
|
|
||||||
|
using (var httpClient = new HttpClient())
|
||||||
|
{
|
||||||
|
var response = httpClient.PostAsync(tokenUrl, content).Result;
|
||||||
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GivenIHaveAnOcelotToken(string adminPath)
|
||||||
|
{
|
||||||
|
var tokenUrl = $"{adminPath}/connect/token";
|
||||||
|
var formData = new List<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("client_id", "admin"),
|
||||||
|
new KeyValuePair<string, string>("client_secret", "secret"),
|
||||||
|
new KeyValuePair<string, string>("scope", "admin"),
|
||||||
|
new KeyValuePair<string, string>("username", "admin"),
|
||||||
|
new KeyValuePair<string, string>("password", "admin"),
|
||||||
|
new KeyValuePair<string, string>("grant_type", "password")
|
||||||
|
};
|
||||||
|
var content = new FormUrlEncodedContent(formData);
|
||||||
|
|
||||||
|
var response = _ocelotClient.PostAsync(tokenUrl, content).Result;
|
||||||
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void VerifyIdentiryServerStarted(string url)
|
||||||
|
{
|
||||||
|
using (var httpClient = new HttpClient())
|
||||||
|
{
|
||||||
|
var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WhenIGetUrlOnTheApiGateway(string url)
|
||||||
|
{
|
||||||
|
_response = _ocelotClient.GetAsync(url).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WhenIGetUrlOnTheApiGatewayMultipleTimes(string url, int times)
|
||||||
|
{
|
||||||
|
var tasks = new Task[times];
|
||||||
|
|
||||||
|
for (int i = 0; i < times; i++)
|
||||||
|
{
|
||||||
|
var urlCopy = url;
|
||||||
|
tasks[i] = GetForServiceDiscoveryTest(urlCopy);
|
||||||
|
Thread.Sleep(_random.Next(40,60));
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.WaitAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GetForServiceDiscoveryTest(string url)
|
||||||
|
{
|
||||||
|
var response = await _ocelotClient.GetAsync(url);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
int count = int.Parse(content);
|
||||||
|
count.ShouldBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit(string url, int times)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < times; i++)
|
||||||
|
{
|
||||||
|
var clientId = "ocelotclient1";
|
||||||
|
var request = new HttpRequestMessage(new HttpMethod("GET"), url);
|
||||||
|
request.Headers.Add("ClientId", clientId);
|
||||||
|
_response = _ocelotClient.SendAsync(request).Result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WhenIGetUrlOnTheApiGateway(string url, string requestId)
|
||||||
|
{
|
||||||
|
_ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation(RequestIdKey, requestId);
|
||||||
|
|
||||||
|
_response = _ocelotClient.GetAsync(url).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WhenIPostUrlOnTheApiGateway(string url)
|
||||||
|
{
|
||||||
|
_response = _ocelotClient.PostAsync(url, _postContent).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GivenThePostHasContent(string postcontent)
|
||||||
|
{
|
||||||
|
_postContent = new StringContent(postcontent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ThenTheResponseBodyShouldBe(string expectedBody)
|
||||||
|
{
|
||||||
|
_response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
|
||||||
|
{
|
||||||
|
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void ThenTheStatusCodeShouldBe(int expectedHttpStatusCode)
|
||||||
|
{
|
||||||
|
var responseStatusCode = (int)_response.StatusCode;
|
||||||
|
responseStatusCode.ShouldBe(expectedHttpStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_ocelotClient?.Dispose();
|
||||||
|
_ocelotServer?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ThenTheRequestIdIsReturned()
|
||||||
|
{
|
||||||
|
_response.Headers.GetValues(RequestIdKey).First().ShouldNotBeNullOrEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ThenTheRequestIdIsReturned(string expected)
|
||||||
|
{
|
||||||
|
_response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
test/Ocelot.AcceptanceTests/TestConfiguration.cs.orig
Normal file
17
test/Ocelot.AcceptanceTests/TestConfiguration.cs.orig
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
using System.IO;
|
||||||
|
>>>>>>> develop
|
||||||
|
|
||||||
|
namespace Ocelot.AcceptanceTests
|
||||||
|
{
|
||||||
|
public static class TestConfiguration
|
||||||
|
{
|
||||||
|
<<<<<<< HEAD
|
||||||
|
public static string ConfigurationPath => $"{AppContext.BaseDirectory}/configuration.json";
|
||||||
|
=======
|
||||||
|
public static string ConfigurationPath => Path.Combine(AppContext.BaseDirectory, "configuration.json");
|
||||||
|
>>>>>>> develop
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
{"ReRoutes":[{"DownstreamPathTemplate":"41879/","UpstreamPathTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":41879,"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},"RateLimitOptions":{"ClientIdHeader":"ClientId","QuotaExceededMessage":null,"RateLimitCounterPrefix":"ocelot","DisableRateLimitHeaders":false,"HttpStatusCode":429}}}
|
{"ReRoutes":[{"DownstreamPathTemplate":"41879/","UpstreamPathTemplate":"/","UpstreamHttpMethod":"Get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"http","DownstreamHost":"localhost","DownstreamPort":41879,"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}}}
|
59
test/Ocelot.AcceptanceTests/project.json.orig
Normal file
59
test/Ocelot.AcceptanceTests/project.json.orig
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.0-dev",
|
||||||
|
|
||||||
|
"buildOptions": {
|
||||||
|
"copyToOutput": {
|
||||||
|
"include": [
|
||||||
|
"configuration.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"testRunner": "xunit",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.Json": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Console": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Http": "1.1.0",
|
||||||
|
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
|
||||||
|
"Ocelot": "0.0.0-dev",
|
||||||
|
"dotnet-test-xunit": "2.2.0-preview2-build1029",
|
||||||
|
"Ocelot.ManualTest": "0.0.0-dev",
|
||||||
|
"Microsoft.AspNetCore.TestHost": "1.1.0",
|
||||||
|
"IdentityServer4": "1.0.1",
|
||||||
|
"Microsoft.AspNetCore.Mvc": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Server.Kestrel.Https": "1.1.0",
|
||||||
|
"Microsoft.NETCore.App": "1.1.0",
|
||||||
|
"Shouldly": "2.8.2",
|
||||||
|
"TestStack.BDDfy": "4.3.2",
|
||||||
|
"Consul": "0.7.2.1",
|
||||||
|
"Microsoft.Extensions.Caching.Memory": "1.1.0",
|
||||||
|
"xunit": "2.2.0-rc1-build3507"
|
||||||
|
},
|
||||||
|
"runtimes": {
|
||||||
|
<<<<<<< HEAD
|
||||||
|
"win10-x64": {},
|
||||||
|
"osx.10.11-x64": {},
|
||||||
|
"osx.10.12-x64": {},
|
||||||
|
"win7-x64": {}
|
||||||
|
=======
|
||||||
|
"osx.10.11-x64":{},
|
||||||
|
"osx.10.12-x64":{},
|
||||||
|
"win7-x64": {},
|
||||||
|
"win10-x64": {}
|
||||||
|
>>>>>>> develop
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netcoreapp1.1": {
|
||||||
|
"imports": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
test/Ocelot.Benchmarks/project.json.orig
Normal file
28
test/Ocelot.Benchmarks/project.json.orig
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.0-dev",
|
||||||
|
"buildOptions": {
|
||||||
|
"emitEntryPoint": true
|
||||||
|
},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"Ocelot": "0.0.0-dev",
|
||||||
|
"BenchmarkDotNet": "0.10.2"
|
||||||
|
},
|
||||||
|
"runtimes": {
|
||||||
|
"osx.10.11-x64":{},
|
||||||
|
<<<<<<< HEAD
|
||||||
|
"osx.10.12-x64": {},
|
||||||
|
"win7-x64": {}
|
||||||
|
=======
|
||||||
|
"osx.10.12-x64":{},
|
||||||
|
"win7-x64": {},
|
||||||
|
"win10-x64": {}
|
||||||
|
>>>>>>> develop
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netcoreapp1.1": {
|
||||||
|
"imports": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
308
test/Ocelot.IntegrationTests/AdministrationTests.cs
Normal file
308
test/Ocelot.IntegrationTests/AdministrationTests.cs
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.ManualTest;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.IntegrationTests
|
||||||
|
{
|
||||||
|
public class AdministrationTests : IDisposable
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private HttpResponseMessage _response;
|
||||||
|
private IWebHost _builder;
|
||||||
|
private IWebHostBuilder _webHostBuilder;
|
||||||
|
private readonly string _ocelotBaseUrl;
|
||||||
|
private BearerToken _token;
|
||||||
|
|
||||||
|
public AdministrationTests()
|
||||||
|
{
|
||||||
|
_httpClient = 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
|
||||||
|
{
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
AdministrationPath = "/administration"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
AdministrationPath = "/administration"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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_file_configuration()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
AdministrationPath = "/administration",
|
||||||
|
RequestIdKey = "RequestId",
|
||||||
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||||
|
{
|
||||||
|
Host = "127.0.0.1",
|
||||||
|
Provider = "test"
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
ReRoutes = new List<FileReRoute>()
|
||||||
|
{
|
||||||
|
new FileReRoute()
|
||||||
|
{
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 80,
|
||||||
|
DownstreamScheme = "https",
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "get",
|
||||||
|
UpstreamPathTemplate = "/"
|
||||||
|
},
|
||||||
|
new FileReRoute()
|
||||||
|
{
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 80,
|
||||||
|
DownstreamScheme = "https",
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "get",
|
||||||
|
UpstreamPathTemplate = "/test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
AdministrationPath = "/administration"
|
||||||
|
},
|
||||||
|
ReRoutes = new List<FileReRoute>()
|
||||||
|
{
|
||||||
|
new FileReRoute()
|
||||||
|
{
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 80,
|
||||||
|
DownstreamScheme = "https",
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "get",
|
||||||
|
UpstreamPathTemplate = "/"
|
||||||
|
},
|
||||||
|
new FileReRoute()
|
||||||
|
{
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 80,
|
||||||
|
DownstreamScheme = "https",
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = "get",
|
||||||
|
UpstreamPathTemplate = "/test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var updatedConfiguration = new FileConfiguration
|
||||||
|
{
|
||||||
|
GlobalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
AdministrationPath = "/administration"
|
||||||
|
},
|
||||||
|
ReRoutes = new List<FileReRoute>()
|
||||||
|
{
|
||||||
|
new FileReRoute()
|
||||||
|
{
|
||||||
|
DownstreamHost = "127.0.0.1",
|
||||||
|
DownstreamPort = 80,
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamPathTemplate = "/geoffrey",
|
||||||
|
UpstreamHttpMethod = "get",
|
||||||
|
UpstreamPathTemplate = "/"
|
||||||
|
},
|
||||||
|
new FileReRoute()
|
||||||
|
{
|
||||||
|
DownstreamHost = "123.123.123",
|
||||||
|
DownstreamPort = 443,
|
||||||
|
DownstreamScheme = "https",
|
||||||
|
DownstreamPathTemplate = "/blooper/{productId}",
|
||||||
|
UpstreamHttpMethod = "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))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
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(FileConfiguration expected)
|
||||||
|
{
|
||||||
|
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
|
||||||
|
|
||||||
|
response.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||||
|
response.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||||
|
response.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
|
||||||
|
|
||||||
|
for (var i = 0; i < response.ReRoutes.Count; i++)
|
||||||
|
{
|
||||||
|
response.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
|
||||||
|
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
|
||||||
|
response.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
|
||||||
|
response.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
|
||||||
|
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expected.ReRoutes[i].UpstreamPathTemplate);
|
||||||
|
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expected.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<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("client_id", "admin"),
|
||||||
|
new KeyValuePair<string, string>("client_secret", "secret"),
|
||||||
|
new KeyValuePair<string, string>("scope", "admin"),
|
||||||
|
new KeyValuePair<string, string>("username", "admin"),
|
||||||
|
new KeyValuePair<string, string>("password", "secret"),
|
||||||
|
new KeyValuePair<string, string>("grant_type", "password")
|
||||||
|
};
|
||||||
|
var content = new FormUrlEncodedContent(formData);
|
||||||
|
|
||||||
|
var response = _httpClient.PostAsync(tokenUrl, content).Result;
|
||||||
|
var responseContent = response.Content.ReadAsStringAsync().Result;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenOcelotIsRunning()
|
||||||
|
{
|
||||||
|
_webHostBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(_ocelotBaseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.ConfigureServices(x => {
|
||||||
|
x.AddSingleton(_webHostBuilder);
|
||||||
|
})
|
||||||
|
.UseStartup<Startup>();
|
||||||
|
|
||||||
|
_builder = _webHostBuilder.Build();
|
||||||
|
|
||||||
|
_builder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
var configurationPath = $"{Directory.GetCurrentDirectory()}/configuration.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}/configuration.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 ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
|
||||||
|
{
|
||||||
|
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_builder?.Dispose();
|
||||||
|
_httpClient?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
test/Ocelot.IntegrationTests/BearerToken.cs
Normal file
16
test/Ocelot.IntegrationTests/BearerToken.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Ocelot.IntegrationTests
|
||||||
|
{
|
||||||
|
class BearerToken
|
||||||
|
{
|
||||||
|
[JsonProperty("access_token")]
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("expires_in")]
|
||||||
|
public int ExpiresIn { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("token_type")]
|
||||||
|
public string TokenType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
22
test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.xproj
Normal file
22
test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.xproj
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>d4575572-99ca-4530-8737-c296eda326f8</ProjectGuid>
|
||||||
|
<RootNamespace>Ocelot.IntegrationTests</RootNamespace>
|
||||||
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||||
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
|
</Project>
|
19
test/Ocelot.IntegrationTests/Properties/AssemblyInfo.cs
Normal file
19
test/Ocelot.IntegrationTests/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("Ocelot.IntegrationTests")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("d4575572-99ca-4530-8737-c296eda326f8")]
|
10
test/Ocelot.IntegrationTests/appsettings.json
Normal file
10
test/Ocelot.IntegrationTests/appsettings.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"IncludeScopes": true,
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
test/Ocelot.IntegrationTests/configuration.json
Executable file
1
test/Ocelot.IntegrationTests/configuration.json
Executable file
@ -0,0 +1 @@
|
|||||||
|
{"ReRoutes":[],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":null,"Host":null,"Port":0},"AdministrationPath":"/administration","RateLimitOptions":{"ClientIdHeader":"ClientId","QuotaExceededMessage":null,"RateLimitCounterPrefix":"ocelot","DisableRateLimitHeaders":false,"HttpStatusCode":429}}}
|
51
test/Ocelot.IntegrationTests/project.json
Normal file
51
test/Ocelot.IntegrationTests/project.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.0-dev",
|
||||||
|
|
||||||
|
"buildOptions": {
|
||||||
|
"copyToOutput": {
|
||||||
|
"include": [
|
||||||
|
"configuration.json",
|
||||||
|
"appsettings.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"testRunner": "xunit",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.Json": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Console": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Http": "1.1.0",
|
||||||
|
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
|
||||||
|
"Ocelot": "0.0.0-dev",
|
||||||
|
"xunit": "2.2.0-beta2-build3300",
|
||||||
|
"dotnet-test-xunit": "2.2.0-preview2-build1029",
|
||||||
|
"Ocelot.ManualTest": "0.0.0-dev",
|
||||||
|
"Microsoft.AspNetCore.TestHost": "1.1.0",
|
||||||
|
"IdentityServer4": "1.0.1",
|
||||||
|
"Microsoft.AspNetCore.Mvc": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
||||||
|
"Microsoft.NETCore.App": "1.1.0",
|
||||||
|
"Shouldly": "2.8.2",
|
||||||
|
"TestStack.BDDfy": "4.3.2",
|
||||||
|
"Consul": "0.7.2.1"
|
||||||
|
},
|
||||||
|
"runtimes": {
|
||||||
|
"win10-x64": {},
|
||||||
|
"osx.10.11-x64": {},
|
||||||
|
"osx.10.12-x64": {},
|
||||||
|
"win7-x64": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netcoreapp1.1": {
|
||||||
|
"imports": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Ocelot.ManualTest
|
namespace Ocelot.ManualTest
|
||||||
{
|
{
|
||||||
@ -7,11 +8,17 @@ namespace Ocelot.ManualTest
|
|||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var host = new WebHostBuilder()
|
IWebHostBuilder builder = new WebHostBuilder();
|
||||||
.UseKestrel()
|
|
||||||
|
builder.ConfigureServices(s => {
|
||||||
|
s.AddSingleton(builder);
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.UseKestrel()
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>();
|
||||||
.Build();
|
|
||||||
|
var host = builder.Build();
|
||||||
|
|
||||||
host.Run();
|
host.Run();
|
||||||
}
|
}
|
||||||
|
@ -38,15 +38,14 @@ namespace Ocelot.ManualTest
|
|||||||
.WithDictionaryHandle();
|
.WithDictionaryHandle();
|
||||||
};
|
};
|
||||||
services.AddOcelotOutputCaching(settings);
|
services.AddOcelotOutputCaching(settings);
|
||||||
services.AddOcelotFileConfiguration(Configuration);
|
services.AddOcelot(Configuration);
|
||||||
services.AddOcelot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||||
|
|
||||||
app.UseOcelot();
|
await app.UseOcelot();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,6 +300,7 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"GlobalConfiguration": {
|
"GlobalConfiguration": {
|
||||||
"RequestIdKey": "OcRequestId"
|
"RequestIdKey": "OcRequestId",
|
||||||
|
"AdministrationPath": "/admin"
|
||||||
}
|
}
|
||||||
}
|
}
|
310
test/Ocelot.ManualTest/configuration.json.orig
Normal file
310
test/Ocelot.ManualTest/configuration.json.orig
Normal file
@ -0,0 +1,310 @@
|
|||||||
|
{
|
||||||
|
"ReRoutes": [
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "localhost",
|
||||||
|
"DownstreamPort": 52876,
|
||||||
|
"UpstreamPathTemplate": "/identityserverexample",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"AuthenticationOptions": {
|
||||||
|
"Provider": "IdentityServer",
|
||||||
|
"ProviderRootUrl": "http://localhost:52888",
|
||||||
|
"ScopeName": "api",
|
||||||
|
"AdditionalScopes": [
|
||||||
|
"openid",
|
||||||
|
"offline_access"
|
||||||
|
],
|
||||||
|
"ScopeSecret": "secret"
|
||||||
|
},
|
||||||
|
"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",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
<<<<<<< HEAD
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
=======
|
||||||
|
"DownstreamPort": 443,
|
||||||
|
>>>>>>> develop
|
||||||
|
"UpstreamPathTemplate": "/posts",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/comments",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/comments",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/posts",
|
||||||
|
"UpstreamHttpMethod": "Post",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": "Put",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": "Patch",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/posts/{postId}",
|
||||||
|
"UpstreamHttpMethod": "Delete",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/products",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/products/{productId}",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "products20161126090340.azurewebsites.net",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/products",
|
||||||
|
"UpstreamHttpMethod": "Post",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "products20161126090340.azurewebsites.net",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/products/{productId}",
|
||||||
|
"UpstreamHttpMethod": "Put",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "products20161126090340.azurewebsites.net",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/products/{productId}",
|
||||||
|
"UpstreamHttpMethod": "Delete",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/customers",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/customers",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/customers/{customerId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/customers/{customerId}",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/customers",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/customers",
|
||||||
|
"UpstreamHttpMethod": "Post",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/customers/{customerId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/customers/{customerId}",
|
||||||
|
"UpstreamHttpMethod": "Put",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/api/customers/{customerId}",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "customers20161126090811.azurewebsites.net",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/customers/{customerId}",
|
||||||
|
"UpstreamHttpMethod": "Delete",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DownstreamPathTemplate": "/posts",
|
||||||
|
"DownstreamScheme": "http",
|
||||||
|
"DownstreamHost": "jsonplaceholder.typicode.com",
|
||||||
|
"DownstreamPort": 80,
|
||||||
|
"UpstreamPathTemplate": "/posts/",
|
||||||
|
"UpstreamHttpMethod": "Get",
|
||||||
|
"QoSOptions": {
|
||||||
|
"ExceptionsAllowedBeforeBreaking": 3,
|
||||||
|
"DurationOfBreak": 10,
|
||||||
|
"TimeoutValue": 5000
|
||||||
|
},
|
||||||
|
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"GlobalConfiguration": {
|
||||||
|
"RequestIdKey": "OcRequestId",
|
||||||
|
"AdministrationPath": "/admin"
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,10 @@
|
|||||||
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||||
"Ocelot": "0.0.0-dev",
|
"Ocelot": "0.0.0-dev",
|
||||||
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
||||||
"Microsoft.NETCore.App": "1.1.0"
|
"Microsoft.NETCore.App": "1.1.0",
|
||||||
|
"Consul": "0.7.2.1",
|
||||||
|
"Polly": "5.0.3",
|
||||||
|
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "1.1.0"
|
||||||
},
|
},
|
||||||
|
|
||||||
"tools": {
|
"tools": {
|
||||||
|
72
test/Ocelot.ManualTest/project.json.orig
Normal file
72
test/Ocelot.ManualTest/project.json.orig
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.0-dev",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNetCore.Http": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.Json": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Console": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||||
|
"Ocelot": "0.0.0-dev",
|
||||||
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
||||||
|
"Microsoft.NETCore.App": "1.1.0",
|
||||||
|
"Consul": "0.7.2.1",
|
||||||
|
"Polly": "5.0.3",
|
||||||
|
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "1.1.0"
|
||||||
|
},
|
||||||
|
|
||||||
|
"tools": {
|
||||||
|
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
|
||||||
|
},
|
||||||
|
"runtimes": {
|
||||||
|
"osx.10.11-x64":{},
|
||||||
|
<<<<<<< HEAD
|
||||||
|
"osx.10.12-x64": {},
|
||||||
|
"win7-x64": {}
|
||||||
|
=======
|
||||||
|
"osx.10.12-x64":{},
|
||||||
|
"win7-x64": {},
|
||||||
|
"win10-x64": {}
|
||||||
|
>>>>>>> develop
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netcoreapp1.1": {
|
||||||
|
"imports": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"buildOptions": {
|
||||||
|
"emitEntryPoint": true,
|
||||||
|
"preserveCompilationContext": true,
|
||||||
|
"copyToOutput": {
|
||||||
|
"include": [
|
||||||
|
"configuration.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"runtimeOptions": {
|
||||||
|
"configProperties": {
|
||||||
|
"System.GC.Server": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"publishOptions": {
|
||||||
|
"include": [
|
||||||
|
"wwwroot",
|
||||||
|
"Views",
|
||||||
|
"Areas/**/Views",
|
||||||
|
"appsettings.json",
|
||||||
|
"web.config",
|
||||||
|
"configuration.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"scripts": {
|
||||||
|
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
|
||||||
|
}
|
||||||
|
}
|
@ -1,112 +1,62 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Provider;
|
|
||||||
using Ocelot.Configuration.Repository;
|
|
||||||
using Ocelot.Errors;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.IO;
|
||||||
|
using Ocelot.Configuration.Provider;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Configuration
|
namespace Ocelot.UnitTests.Configuration
|
||||||
{
|
{
|
||||||
public class FileConfigurationProviderTests
|
public class FileConfigurationProviderTests
|
||||||
{
|
{
|
||||||
private readonly IOcelotConfigurationProvider _ocelotConfigurationProvider;
|
private readonly IFileConfigurationProvider _provider;
|
||||||
private readonly Mock<IOcelotConfigurationRepository> _configurationRepository;
|
private Mock<IFileConfigurationRepository> _repo;
|
||||||
private readonly Mock<IOcelotConfigurationCreator> _creator;
|
private FileConfiguration _result;
|
||||||
private Response<IOcelotConfiguration> _result;
|
private FileConfiguration _fileConfiguration;
|
||||||
|
|
||||||
public FileConfigurationProviderTests()
|
public FileConfigurationProviderTests()
|
||||||
{
|
{
|
||||||
_creator = new Mock<IOcelotConfigurationCreator>();
|
_repo = new Mock<IFileConfigurationRepository>();
|
||||||
_configurationRepository = new Mock<IOcelotConfigurationRepository>();
|
_provider = new FileConfigurationProvider(_repo.Object);
|
||||||
_ocelotConfigurationProvider = new OcelotConfigurationProvider(_configurationRepository.Object, _creator.Object);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_get_config()
|
public void should_return_file_configuration()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>()))))
|
var config = new FileConfiguration();
|
||||||
.When(x => x.WhenIGetTheConfig())
|
|
||||||
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>()))))
|
this.Given(x => x.GivenTheConfigurationIs(config))
|
||||||
|
.When(x => x.WhenIGetTheReRoutes())
|
||||||
|
.Then(x => x.ThenTheRepoIsCalledCorrectly())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void should_create_config_if_it_doesnt_exist()
|
|
||||||
{
|
|
||||||
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(null)))
|
|
||||||
.And(x => x.GivenTheCreatorReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>()))))
|
|
||||||
.When(x => x.WhenIGetTheConfig())
|
|
||||||
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>()))))
|
|
||||||
.BDDfy();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void should_return_error()
|
|
||||||
{
|
|
||||||
this.Given(x => x.GivenTheRepoReturns(new ErrorResponse<IOcelotConfiguration>(new List<Error>
|
|
||||||
{
|
|
||||||
new AnyError()
|
|
||||||
})))
|
|
||||||
.When(x => x.WhenIGetTheConfig())
|
|
||||||
.Then(x => x.TheFollowingIsReturned(
|
|
||||||
new ErrorResponse<IOcelotConfiguration>(new List<Error>
|
|
||||||
{
|
|
||||||
new AnyError()
|
|
||||||
})))
|
|
||||||
.BDDfy();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
private void GivenTheConfigurationIs(FileConfiguration fileConfiguration)
|
||||||
public void should_return_error_if_creator_errors()
|
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(null)))
|
_fileConfiguration = fileConfiguration;
|
||||||
.And(x => x.GivenTheCreatorReturns(new ErrorResponse<IOcelotConfiguration>(new List<Error>
|
_repo
|
||||||
{
|
|
||||||
new AnyError()
|
|
||||||
})))
|
|
||||||
.When(x => x.WhenIGetTheConfig())
|
|
||||||
.Then(x => x.TheFollowingIsReturned(new ErrorResponse<IOcelotConfiguration>(new List<Error>
|
|
||||||
{
|
|
||||||
new AnyError()
|
|
||||||
})))
|
|
||||||
.BDDfy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GivenTheCreatorReturns(Response<IOcelotConfiguration> config)
|
|
||||||
{
|
|
||||||
_creator
|
|
||||||
.Setup(x => x.Create())
|
|
||||||
.ReturnsAsync(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GivenTheRepoReturns(Response<IOcelotConfiguration> config)
|
|
||||||
{
|
|
||||||
_configurationRepository
|
|
||||||
.Setup(x => x.Get())
|
.Setup(x => x.Get())
|
||||||
.Returns(config);
|
.Returns(new OkResponse<FileConfiguration>(fileConfiguration));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WhenIGetTheConfig()
|
private void WhenIGetTheReRoutes()
|
||||||
{
|
{
|
||||||
_result = _ocelotConfigurationProvider.Get().Result;
|
_result = _provider.Get().Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TheFollowingIsReturned(Response<IOcelotConfiguration> expected)
|
private void ThenTheRepoIsCalledCorrectly()
|
||||||
{
|
{
|
||||||
_result.IsError.ShouldBe(expected.IsError);
|
_repo
|
||||||
}
|
.Verify(x => x.Get(), Times.Once);
|
||||||
|
|
||||||
class AnyError : Error
|
|
||||||
{
|
|
||||||
public AnyError()
|
|
||||||
: base("blamo", OcelotErrorCode.UnknownError)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,161 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.IO;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Configuration
|
||||||
|
{
|
||||||
|
public class FileConfigurationRepositoryTests
|
||||||
|
{
|
||||||
|
private readonly IFileConfigurationRepository _repo;
|
||||||
|
private FileConfiguration _result;
|
||||||
|
private FileConfiguration _fileConfiguration;
|
||||||
|
|
||||||
|
public FileConfigurationRepositoryTests()
|
||||||
|
{
|
||||||
|
_repo = new FileConfigurationRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_file_configuration()
|
||||||
|
{
|
||||||
|
var reRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamHost = "localhost",
|
||||||
|
DownstreamPort = 80,
|
||||||
|
DownstreamScheme = "https",
|
||||||
|
DownstreamPathTemplate = "/test/test/{test}"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var globalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
AdministrationPath = "testy",
|
||||||
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||||
|
{
|
||||||
|
Provider = "consul",
|
||||||
|
Port = 198,
|
||||||
|
Host = "blah"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var config = new FileConfiguration();
|
||||||
|
config.GlobalConfiguration = globalConfiguration;
|
||||||
|
config.ReRoutes.AddRange(reRoutes);
|
||||||
|
|
||||||
|
this.Given(x => x.GivenTheConfigurationIs(config))
|
||||||
|
.When(x => x.WhenIGetTheReRoutes())
|
||||||
|
.Then(x => x.ThenTheFollowingIsReturned(config))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_set_file_configuration()
|
||||||
|
{
|
||||||
|
var reRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamHost = "123.12.12.12",
|
||||||
|
DownstreamPort = 80,
|
||||||
|
DownstreamScheme = "https",
|
||||||
|
DownstreamPathTemplate = "/asdfs/test/{test}"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var globalConfiguration = new FileGlobalConfiguration
|
||||||
|
{
|
||||||
|
AdministrationPath = "asdas",
|
||||||
|
ServiceDiscoveryProvider = new FileServiceDiscoveryProvider
|
||||||
|
{
|
||||||
|
Provider = "consul",
|
||||||
|
Port = 198,
|
||||||
|
Host = "blah"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var config = new FileConfiguration();
|
||||||
|
config.GlobalConfiguration = globalConfiguration;
|
||||||
|
config.ReRoutes.AddRange(reRoutes);
|
||||||
|
|
||||||
|
this.Given(x => GivenIHaveAConfiguration(config))
|
||||||
|
.When(x => WhenISetTheConfiguration())
|
||||||
|
.Then(x => ThenTheConfigurationIsStoredAs(config))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenIHaveAConfiguration(FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
_fileConfiguration = fileConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenISetTheConfiguration()
|
||||||
|
{
|
||||||
|
_repo.Set(_fileConfiguration);
|
||||||
|
_result = _repo.Get().Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheConfigurationIsStoredAs(FileConfiguration expected)
|
||||||
|
{
|
||||||
|
_result.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||||
|
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||||
|
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||||
|
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||||
|
_result.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
|
||||||
|
|
||||||
|
for(var i = 0; i < _result.ReRoutes.Count; i++)
|
||||||
|
{
|
||||||
|
_result.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
|
||||||
|
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
|
||||||
|
_result.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
|
||||||
|
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheConfigurationIs(FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
var configurationPath = $"{AppContext.BaseDirectory}/configuration.json";
|
||||||
|
|
||||||
|
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||||
|
|
||||||
|
if (File.Exists(configurationPath))
|
||||||
|
{
|
||||||
|
File.Delete(configurationPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetTheReRoutes()
|
||||||
|
{
|
||||||
|
_result = _repo.Get().Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheFollowingIsReturned(FileConfiguration expected)
|
||||||
|
{
|
||||||
|
_result.GlobalConfiguration.AdministrationPath.ShouldBe(expected.GlobalConfiguration.AdministrationPath);
|
||||||
|
_result.GlobalConfiguration.RequestIdKey.ShouldBe(expected.GlobalConfiguration.RequestIdKey);
|
||||||
|
_result.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Host);
|
||||||
|
_result.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Port);
|
||||||
|
_result.GlobalConfiguration.ServiceDiscoveryProvider.Provider.ShouldBe(expected.GlobalConfiguration.ServiceDiscoveryProvider.Provider);
|
||||||
|
|
||||||
|
for(var i = 0; i < _result.ReRoutes.Count; i++)
|
||||||
|
{
|
||||||
|
_result.ReRoutes[i].DownstreamHost.ShouldBe(expected.ReRoutes[i].DownstreamHost);
|
||||||
|
_result.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expected.ReRoutes[i].DownstreamPathTemplate);
|
||||||
|
_result.ReRoutes[i].DownstreamPort.ShouldBe(expected.ReRoutes[i].DownstreamPort);
|
||||||
|
_result.ReRoutes[i].DownstreamScheme.ShouldBe(expected.ReRoutes[i].DownstreamScheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
|
using Ocelot.Configuration.Setter;
|
||||||
|
using Ocelot.Errors;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Configuration
|
||||||
|
{
|
||||||
|
public class FileConfigurationSetterTests
|
||||||
|
{
|
||||||
|
private FileConfiguration _fileConfiguration;
|
||||||
|
private FileConfigurationSetter _configSetter;
|
||||||
|
private Mock<IOcelotConfigurationRepository> _configRepo;
|
||||||
|
private Mock<IOcelotConfigurationCreator> _configCreator;
|
||||||
|
private Response<IOcelotConfiguration> _configuration;
|
||||||
|
private object _result;
|
||||||
|
private Mock<IFileConfigurationRepository> _repo;
|
||||||
|
|
||||||
|
public FileConfigurationSetterTests()
|
||||||
|
{
|
||||||
|
_repo = new Mock<IFileConfigurationRepository>();
|
||||||
|
_configRepo = new Mock<IOcelotConfigurationRepository>();
|
||||||
|
_configCreator = new Mock<IOcelotConfigurationCreator>();
|
||||||
|
_configSetter = new FileConfigurationSetter(_configRepo.Object, _configCreator.Object, _repo.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_set_configuration()
|
||||||
|
{
|
||||||
|
var fileConfig = new FileConfiguration();
|
||||||
|
var config = new OcelotConfiguration(new List<ReRoute>(), string.Empty);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFollowingConfiguration(fileConfig))
|
||||||
|
.And(x => GivenTheRepoReturns(new OkResponse()))
|
||||||
|
.And(x => GivenTheCreatorReturns(new OkResponse<IOcelotConfiguration>(config)))
|
||||||
|
.When(x => WhenISetTheConfiguration())
|
||||||
|
.Then(x => ThenTheConfigurationRepositoryIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_error_if_unable_to_set_file_configuration()
|
||||||
|
{
|
||||||
|
var fileConfig = new FileConfiguration();
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFollowingConfiguration(fileConfig))
|
||||||
|
.And(x => GivenTheRepoReturns(new ErrorResponse(It.IsAny<Error>())))
|
||||||
|
.When(x => WhenISetTheConfiguration())
|
||||||
|
.And(x => ThenAnErrorResponseIsReturned())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_error_if_unable_to_set_ocelot_configuration()
|
||||||
|
{
|
||||||
|
var fileConfig = new FileConfiguration();
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFollowingConfiguration(fileConfig))
|
||||||
|
.And(x => GivenTheRepoReturns(new OkResponse()))
|
||||||
|
.And(x => GivenTheCreatorReturns(new ErrorResponse<IOcelotConfiguration>(It.IsAny<Error>())))
|
||||||
|
.When(x => WhenISetTheConfiguration())
|
||||||
|
.And(x => ThenAnErrorResponseIsReturned())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheRepoReturns(Response response)
|
||||||
|
{
|
||||||
|
_repo
|
||||||
|
.Setup(x => x.Set(It.IsAny<FileConfiguration>()))
|
||||||
|
.Returns(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenAnErrorResponseIsReturned()
|
||||||
|
{
|
||||||
|
_result.ShouldBeOfType<ErrorResponse>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheCreatorReturns(Response<IOcelotConfiguration> configuration)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_configCreator
|
||||||
|
.Setup(x => x.Create(_fileConfiguration))
|
||||||
|
.ReturnsAsync(_configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheFollowingConfiguration(FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
_fileConfiguration = fileConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenISetTheConfiguration()
|
||||||
|
{
|
||||||
|
_result = _configSetter.Set(_fileConfiguration).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheConfigurationRepositoryIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_configRepo
|
||||||
|
.Verify(x => x.AddOrReplace(_configuration.Data), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
test/Ocelot.UnitTests/Configuration/HashCreationTests.cs
Normal file
33
test/Ocelot.UnitTests/Configuration/HashCreationTests.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Configuration
|
||||||
|
{
|
||||||
|
public class HashCreationTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void should_create_hash_and_salt()
|
||||||
|
{
|
||||||
|
var password = "secret";
|
||||||
|
|
||||||
|
var salt = new byte[128 / 8];
|
||||||
|
|
||||||
|
using (var rng = RandomNumberGenerator.Create())
|
||||||
|
{
|
||||||
|
rng.GetBytes(salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
var storedSalt = Convert.ToBase64String(salt);
|
||||||
|
|
||||||
|
var storedHash = Convert.ToBase64String(KeyDerivation.Pbkdf2(
|
||||||
|
password: password,
|
||||||
|
salt: salt,
|
||||||
|
prf: KeyDerivationPrf.HMACSHA256,
|
||||||
|
iterationCount: 10000,
|
||||||
|
numBytesRequested: 256 / 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
test/Ocelot.UnitTests/Configuration/HashMatcherTests.cs
Normal file
76
test/Ocelot.UnitTests/Configuration/HashMatcherTests.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
using Ocelot.Configuration.Authentication;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Configuration
|
||||||
|
{
|
||||||
|
public class HashMatcherTests
|
||||||
|
{
|
||||||
|
private string _password;
|
||||||
|
private string _hash;
|
||||||
|
private string _salt;
|
||||||
|
private bool _result;
|
||||||
|
private HashMatcher _hashMatcher;
|
||||||
|
|
||||||
|
public HashMatcherTests()
|
||||||
|
{
|
||||||
|
_hashMatcher = new HashMatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_match_hash()
|
||||||
|
{
|
||||||
|
var hash = "kE/mxd1hO9h9Sl2VhGhwJUd9xZEv4NP6qXoN39nIqM4=";
|
||||||
|
var salt = "zzWITpnDximUNKYLiUam/w==";
|
||||||
|
var password = "secret";
|
||||||
|
|
||||||
|
this.Given(x => GivenThePassword(password))
|
||||||
|
.And(x => GivenTheHash(hash))
|
||||||
|
.And(x => GivenTheSalt(salt))
|
||||||
|
.When(x => WhenIMatch())
|
||||||
|
.Then(x => ThenTheResultIs(true))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_not_match_hash()
|
||||||
|
{
|
||||||
|
var hash = "kE/mxd1hO9h9Sl2VhGhwJUd9xZEv4NP6qXoN39nIqM4=";
|
||||||
|
var salt = "zzWITpnDximUNKYLiUam/w==";
|
||||||
|
var password = "secret1";
|
||||||
|
|
||||||
|
this.Given(x => GivenThePassword(password))
|
||||||
|
.And(x => GivenTheHash(hash))
|
||||||
|
.And(x => GivenTheSalt(salt))
|
||||||
|
.When(x => WhenIMatch())
|
||||||
|
.Then(x => ThenTheResultIs(false))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThePassword(string password)
|
||||||
|
{
|
||||||
|
_password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheHash(string hash)
|
||||||
|
{
|
||||||
|
_hash = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheSalt(string salt)
|
||||||
|
{
|
||||||
|
_salt = salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIMatch()
|
||||||
|
{
|
||||||
|
_result = _hashMatcher.Match(_password, _salt, _hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheResultIs(bool expected)
|
||||||
|
{
|
||||||
|
_result.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void can_add_config()
|
public void can_add_config()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheConfigurationIs(new FakeConfig("initial")))
|
this.Given(x => x.GivenTheConfigurationIs(new FakeConfig("initial", "adminath")))
|
||||||
.When(x => x.WhenIAddOrReplaceTheConfig())
|
.When(x => x.WhenIAddOrReplaceTheConfig())
|
||||||
.Then(x => x.ThenNoErrorsAreReturned())
|
.Then(x => x.ThenNoErrorsAreReturned())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -54,7 +54,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
private void GivenThereIsASavedConfiguration()
|
private void GivenThereIsASavedConfiguration()
|
||||||
{
|
{
|
||||||
GivenTheConfigurationIs(new FakeConfig("initial"));
|
GivenTheConfigurationIs(new FakeConfig("initial", "adminath"));
|
||||||
WhenIAddOrReplaceTheConfig();
|
WhenIAddOrReplaceTheConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,9 +77,10 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
{
|
{
|
||||||
private readonly string _downstreamTemplatePath;
|
private readonly string _downstreamTemplatePath;
|
||||||
|
|
||||||
public FakeConfig(string downstreamTemplatePath)
|
public FakeConfig(string downstreamTemplatePath, string administrationPath)
|
||||||
{
|
{
|
||||||
_downstreamTemplatePath = downstreamTemplatePath;
|
_downstreamTemplatePath = downstreamTemplatePath;
|
||||||
|
AdministrationPath = administrationPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ReRoute> ReRoutes => new List<ReRoute>
|
public List<ReRoute> ReRoutes => new List<ReRoute>
|
||||||
@ -89,6 +90,8 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.Build()
|
.Build()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public string AdministrationPath {get;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Configuration.Provider;
|
||||||
|
using Ocelot.Configuration.Repository;
|
||||||
|
using Ocelot.Errors;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Configuration
|
||||||
|
{
|
||||||
|
public class OcelotConfigurationProviderTests
|
||||||
|
{
|
||||||
|
private readonly IOcelotConfigurationProvider _ocelotConfigurationProvider;
|
||||||
|
private readonly Mock<IOcelotConfigurationRepository> _configurationRepository;
|
||||||
|
private Response<IOcelotConfiguration> _result;
|
||||||
|
|
||||||
|
public OcelotConfigurationProviderTests()
|
||||||
|
{
|
||||||
|
_configurationRepository = new Mock<IOcelotConfigurationRepository>();
|
||||||
|
_ocelotConfigurationProvider = new OcelotConfigurationProvider(_configurationRepository.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_get_config()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheRepoReturns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty))))
|
||||||
|
.When(x => x.WhenIGetTheConfig())
|
||||||
|
.Then(x => x.TheFollowingIsReturned(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(new List<ReRoute>(), string.Empty))))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_error()
|
||||||
|
{
|
||||||
|
this.Given(x => x.GivenTheRepoReturns(new ErrorResponse<IOcelotConfiguration>(new List<Error>
|
||||||
|
{
|
||||||
|
new AnyError()
|
||||||
|
})))
|
||||||
|
.When(x => x.WhenIGetTheConfig())
|
||||||
|
.Then(x => x.TheFollowingIsReturned(
|
||||||
|
new ErrorResponse<IOcelotConfiguration>(new List<Error>
|
||||||
|
{
|
||||||
|
new AnyError()
|
||||||
|
})))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheRepoReturns(Response<IOcelotConfiguration> config)
|
||||||
|
{
|
||||||
|
_configurationRepository
|
||||||
|
.Setup(x => x.Get())
|
||||||
|
.Returns(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetTheConfig()
|
||||||
|
{
|
||||||
|
_result = _ocelotConfigurationProvider.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TheFollowingIsReturned(Response<IOcelotConfiguration> expected)
|
||||||
|
{
|
||||||
|
_result.IsError.ShouldBe(expected.IsError);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnyError : Error
|
||||||
|
{
|
||||||
|
public AnyError()
|
||||||
|
: base("blamo", OcelotErrorCode.UnknownError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
using Ocelot.Configuration.Authentication;
|
||||||
|
using Xunit;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Moq;
|
||||||
|
using IdentityServer4.Validation;
|
||||||
|
using Ocelot.Configuration.Provider;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Configuration
|
||||||
|
{
|
||||||
|
public class OcelotResourceOwnerPasswordValidatorTests
|
||||||
|
{
|
||||||
|
private OcelotResourceOwnerPasswordValidator _validator;
|
||||||
|
private Mock<IHashMatcher> _matcher;
|
||||||
|
private string _userName;
|
||||||
|
private string _password;
|
||||||
|
private ResourceOwnerPasswordValidationContext _context;
|
||||||
|
private Mock<IIdentityServerConfiguration> _config;
|
||||||
|
private User _user;
|
||||||
|
|
||||||
|
public OcelotResourceOwnerPasswordValidatorTests()
|
||||||
|
{
|
||||||
|
_matcher = new Mock<IHashMatcher>();
|
||||||
|
_config = new Mock<IIdentityServerConfiguration>();
|
||||||
|
_validator = new OcelotResourceOwnerPasswordValidator(_matcher.Object, _config.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_success()
|
||||||
|
{
|
||||||
|
this.Given(x => GivenTheUserName("tom"))
|
||||||
|
.And(x => GivenThePassword("password"))
|
||||||
|
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
|
||||||
|
.And(x => GivenTheMatcherReturns(true))
|
||||||
|
.When(x => WhenIValidate())
|
||||||
|
.Then(x => ThenTheUserIsValidated())
|
||||||
|
.And(x => ThenTheMatcherIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_fail_when_no_user()
|
||||||
|
{
|
||||||
|
this.Given(x => GivenTheUserName("bob"))
|
||||||
|
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
|
||||||
|
.And(x => GivenTheMatcherReturns(true))
|
||||||
|
.When(x => WhenIValidate())
|
||||||
|
.Then(x => ThenTheUserIsNotValidated())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_fail_when_password_doesnt_match()
|
||||||
|
{
|
||||||
|
this.Given(x => GivenTheUserName("tom"))
|
||||||
|
.And(x => GivenThePassword("password"))
|
||||||
|
.And(x => GivenTheUserIs(new User("sub", "tom", "xxx", "xxx")))
|
||||||
|
.And(x => GivenTheMatcherReturns(false))
|
||||||
|
.When(x => WhenIValidate())
|
||||||
|
.Then(x => ThenTheUserIsNotValidated())
|
||||||
|
.And(x => ThenTheMatcherIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheMatcherIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_matcher
|
||||||
|
.Verify(x => x.Match(_password, _user.Salt, _user.Hash), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThePassword(string password)
|
||||||
|
{
|
||||||
|
_password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheUserIs(User user)
|
||||||
|
{
|
||||||
|
_user = user;
|
||||||
|
_config
|
||||||
|
.Setup(x => x.Users)
|
||||||
|
.Returns(new List<User>{_user});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheMatcherReturns(bool expected)
|
||||||
|
{
|
||||||
|
_matcher
|
||||||
|
.Setup(x => x.Match(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||||
|
.Returns(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheUserName(string userName)
|
||||||
|
{
|
||||||
|
_userName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIValidate()
|
||||||
|
{
|
||||||
|
_context = new ResourceOwnerPasswordValidationContext
|
||||||
|
{
|
||||||
|
UserName = _userName,
|
||||||
|
Password = _password
|
||||||
|
};
|
||||||
|
_validator.ValidateAsync(_context).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheUserIsValidated()
|
||||||
|
{
|
||||||
|
_context.Result.IsError.ShouldBe(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheUserIsNotValidated()
|
||||||
|
{
|
||||||
|
_context.Result.IsError.ShouldBe(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Ocelot.Configuration.Setter;
|
||||||
|
using Ocelot.Controllers;
|
||||||
|
using Ocelot.Errors;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
using Shouldly;
|
||||||
|
using Ocelot.Configuration.Provider;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Controllers
|
||||||
|
{
|
||||||
|
public class FileConfigurationControllerTests
|
||||||
|
{
|
||||||
|
private FileConfigurationController _controller;
|
||||||
|
private Mock<IFileConfigurationProvider> _configGetter;
|
||||||
|
private Mock<IFileConfigurationSetter> _configSetter;
|
||||||
|
private IActionResult _result;
|
||||||
|
private FileConfiguration _fileConfiguration;
|
||||||
|
|
||||||
|
public FileConfigurationControllerTests()
|
||||||
|
{
|
||||||
|
_configGetter = new Mock<IFileConfigurationProvider>();
|
||||||
|
_configSetter = new Mock<IFileConfigurationSetter>();
|
||||||
|
_controller = new FileConfigurationController(_configGetter.Object, _configSetter.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_get_file_configuration()
|
||||||
|
{
|
||||||
|
var expected = new OkResponse<FileConfiguration>(new FileConfiguration());
|
||||||
|
|
||||||
|
this.Given(x => x.GivenTheGetConfigurationReturns(expected))
|
||||||
|
.When(x => x.WhenIGetTheFileConfiguration())
|
||||||
|
.Then(x => x.TheTheGetFileConfigurationIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_error_when_cannot_get_config()
|
||||||
|
{
|
||||||
|
var expected = new ErrorResponse<FileConfiguration>(It.IsAny<Error>());
|
||||||
|
|
||||||
|
this.Given(x => x.GivenTheGetConfigurationReturns(expected))
|
||||||
|
.When(x => x.WhenIGetTheFileConfiguration())
|
||||||
|
.Then(x => x.TheTheGetFileConfigurationIsCalledCorrectly())
|
||||||
|
.And(x => x.ThenTheResponseIs<BadRequestObjectResult>())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_post_file_configuration()
|
||||||
|
{
|
||||||
|
var expected = new FileConfiguration();
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFileConfiguration(expected))
|
||||||
|
.And(x => GivenTheConfigSetterReturnsAnError(new OkResponse()))
|
||||||
|
.When(x => WhenIPostTheFileConfiguration())
|
||||||
|
.Then(x => x.ThenTheConfigrationSetterIsCalledCorrectly())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_error_when_cannot_set_config()
|
||||||
|
{
|
||||||
|
var expected = new FileConfiguration();
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFileConfiguration(expected))
|
||||||
|
.And(x => GivenTheConfigSetterReturnsAnError(new ErrorResponse(new FakeError())))
|
||||||
|
.When(x => WhenIPostTheFileConfiguration())
|
||||||
|
.Then(x => x.ThenTheConfigrationSetterIsCalledCorrectly())
|
||||||
|
.And(x => ThenTheResponseIs<BadRequestObjectResult>())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheConfigSetterReturnsAnError(Response response)
|
||||||
|
{
|
||||||
|
_configSetter
|
||||||
|
.Setup(x => x.Set(It.IsAny<FileConfiguration>()))
|
||||||
|
.ReturnsAsync(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheConfigrationSetterIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_configSetter
|
||||||
|
.Verify(x => x.Set(_fileConfiguration), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIPostTheFileConfiguration()
|
||||||
|
{
|
||||||
|
_result = _controller.Post(_fileConfiguration).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheFileConfiguration(FileConfiguration fileConfiguration)
|
||||||
|
{
|
||||||
|
_fileConfiguration = fileConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheResponseIs<T>()
|
||||||
|
{
|
||||||
|
_result.ShouldBeOfType<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheGetConfigurationReturns(Response<FileConfiguration> fileConfiguration)
|
||||||
|
{
|
||||||
|
_configGetter
|
||||||
|
.Setup(x => x.Get())
|
||||||
|
.Returns(fileConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetTheFileConfiguration()
|
||||||
|
{
|
||||||
|
_result = _controller.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TheTheGetFileConfigurationIsCalledCorrectly()
|
||||||
|
{
|
||||||
|
_configGetter
|
||||||
|
.Verify(x => x.Get(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeError : Error
|
||||||
|
{
|
||||||
|
public FakeError() : base(string.Empty, OcelotErrorCode.CannotAddDataError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -90,7 +90,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
|
||||||
_downstreamRouteFinder
|
_downstreamRouteFinder
|
||||||
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>()))
|
.Setup(x => x.FindDownstreamRoute(It.IsAny<string>(), It.IsAny<string>()))
|
||||||
.ReturnsAsync(_downstreamRoute);
|
.Returns(_downstreamRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -48,7 +48,8 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("someUpstreamPath")
|
.WithUpstreamTemplatePattern("someUpstreamPath")
|
||||||
.Build()
|
.Build()
|
||||||
}))
|
}, string.Empty
|
||||||
|
))
|
||||||
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
.When(x => x.WhenICallTheFinder())
|
.When(x => x.WhenICallTheFinder())
|
||||||
@ -80,7 +81,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("someUpstreamPath")
|
.WithUpstreamTemplatePattern("someUpstreamPath")
|
||||||
.Build()
|
.Build()
|
||||||
}
|
}, string.Empty
|
||||||
))
|
))
|
||||||
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
@ -118,7 +119,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithUpstreamHttpMethod("Post")
|
.WithUpstreamHttpMethod("Post")
|
||||||
.WithUpstreamTemplatePattern("")
|
.WithUpstreamTemplatePattern("")
|
||||||
.Build()
|
.Build()
|
||||||
}
|
}, string.Empty
|
||||||
))
|
))
|
||||||
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
|
||||||
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
|
||||||
@ -145,7 +146,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithUpstreamHttpMethod("Get")
|
.WithUpstreamHttpMethod("Get")
|
||||||
.WithUpstreamTemplatePattern("somePath")
|
.WithUpstreamTemplatePattern("somePath")
|
||||||
.Build(),
|
.Build(),
|
||||||
}
|
}, string.Empty
|
||||||
))
|
))
|
||||||
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(false))))
|
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(false))))
|
||||||
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
.And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
|
||||||
@ -193,12 +194,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.Returns(_match);
|
.Returns(_match);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig)
|
private void GivenTheConfigurationIs(List<ReRoute> reRoutesConfig, string adminPath)
|
||||||
{
|
{
|
||||||
_reRoutesConfig = reRoutesConfig;
|
_reRoutesConfig = reRoutesConfig;
|
||||||
_mockConfig
|
_mockConfig
|
||||||
.Setup(x => x.Get())
|
.Setup(x => x.Get())
|
||||||
.ReturnsAsync(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(_reRoutesConfig)));
|
.Returns(new OkResponse<IOcelotConfiguration>(new OcelotConfiguration(_reRoutesConfig, adminPath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)
|
private void GivenThereIsAnUpstreamUrlPath(string upstreamUrlPath)
|
||||||
@ -208,7 +209,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
|
|
||||||
private void WhenICallTheFinder()
|
private void WhenICallTheFinder()
|
||||||
{
|
{
|
||||||
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod).Result;
|
_result = _downstreamRouteFinder.FindDownstreamRoute(_upstreamUrlPath, _upstreamHttpMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
||||||
|
61
test/Ocelot.UnitTests/Middleware/BaseUrlFinderTests.cs
Normal file
61
test/Ocelot.UnitTests/Middleware/BaseUrlFinderTests.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Middleware
|
||||||
|
{
|
||||||
|
public class BaseUrlFinderTests
|
||||||
|
{
|
||||||
|
private readonly BaseUrlFinder _baseUrlFinder;
|
||||||
|
private readonly Mock<IWebHostBuilder> _webHostBuilder;
|
||||||
|
private string _result;
|
||||||
|
|
||||||
|
public BaseUrlFinderTests()
|
||||||
|
{
|
||||||
|
_webHostBuilder = new Mock<IWebHostBuilder>();
|
||||||
|
_baseUrlFinder = new BaseUrlFinder(_webHostBuilder.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_find_base_url_based_on_webhostbuilder()
|
||||||
|
{
|
||||||
|
this.Given(x => GivenTheWebHostBuilderReturns("http://localhost:7000"))
|
||||||
|
.When(x => WhenIFindTheUrl())
|
||||||
|
.Then(x => ThenTheUrlIs("http://localhost:7000"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_use_default_base_url()
|
||||||
|
{
|
||||||
|
this.Given(x => GivenTheWebHostBuilderReturns(""))
|
||||||
|
.When(x => WhenIFindTheUrl())
|
||||||
|
.Then(x => ThenTheUrlIs("http://localhost:5000"))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheWebHostBuilderReturns(string url)
|
||||||
|
{
|
||||||
|
_webHostBuilder
|
||||||
|
.Setup(x => x.GetSetting(WebHostDefaults.ServerUrlsKey))
|
||||||
|
.Returns(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIFindTheUrl()
|
||||||
|
{
|
||||||
|
_result = _baseUrlFinder.Find();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheUrlIs(string expected)
|
||||||
|
{
|
||||||
|
_result.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
test/Ocelot.UnitTests/configuration.json
Executable file
1
test/Ocelot.UnitTests/configuration.json
Executable file
@ -0,0 +1 @@
|
|||||||
|
{"ReRoutes":[{"DownstreamPathTemplate":"/test/test/{test}","UpstreamPathTemplate":null,"UpstreamHttpMethod":null,"AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"https","DownstreamHost":"localhost","DownstreamPort":80,"QoSOptions":{"ExceptionsAllowedBeforeBreaking":0,"DurationOfBreak":0,"TimeoutValue":0},"LoadBalancer":null}],"GlobalConfiguration":{"RequestIdKey":null,"ServiceDiscoveryProvider":{"Provider":"consul","Host":"blah","Port":198},"AdministrationPath":"testy"}}
|
@ -3,29 +3,30 @@
|
|||||||
|
|
||||||
"testRunner": "xunit",
|
"testRunner": "xunit",
|
||||||
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
|
"dotnet-test-xunit": "2.2.0-preview2-build1029",
|
||||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
|
"Microsoft.AspNetCore.Authentication.OAuth": "1.1.0",
|
||||||
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
|
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "1.1.0",
|
||||||
"Microsoft.Extensions.Configuration.Json": "1.1.0",
|
"Microsoft.AspNetCore.Http": "1.1.0",
|
||||||
"Microsoft.Extensions.Logging": "1.1.0",
|
"Microsoft.AspNetCore.Mvc": "1.1.0",
|
||||||
"Microsoft.Extensions.Logging.Console": "1.1.0",
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
|
||||||
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
||||||
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
"Microsoft.AspNetCore.TestHost": "1.1.0",
|
||||||
"Microsoft.AspNetCore.Http": "1.1.0",
|
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
|
||||||
"Ocelot": "0.0.0-dev",
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
|
||||||
"dotnet-test-xunit": "2.2.0-preview2-build1029",
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
|
||||||
"Moq": "4.6.38-alpha",
|
"Microsoft.Extensions.Configuration.Json": "1.1.0",
|
||||||
"Microsoft.AspNetCore.TestHost": "1.1.0",
|
"Microsoft.Extensions.Logging": "1.1.0",
|
||||||
"Microsoft.AspNetCore.Mvc": "1.1.0",
|
"Microsoft.Extensions.Logging.Console": "1.1.0",
|
||||||
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
||||||
"Microsoft.NETCore.App": "1.1.0",
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||||
"Shouldly": "2.8.2",
|
"Microsoft.NETCore.App": "1.1.0",
|
||||||
"TestStack.BDDfy": "4.3.2",
|
"Moq": "4.6.38-alpha",
|
||||||
"Microsoft.AspNetCore.Authentication.OAuth": "1.1.0",
|
"Ocelot": "0.0.0-dev",
|
||||||
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
|
"Shouldly": "2.8.2",
|
||||||
"xunit": "2.2.0-rc1-build3507"
|
"TestStack.BDDfy": "4.3.2",
|
||||||
},
|
"xunit": "2.2.0-rc1-build3507"
|
||||||
|
},
|
||||||
"runtimes": {
|
"runtimes": {
|
||||||
"osx.10.11-x64":{},
|
"osx.10.11-x64":{},
|
||||||
"osx.10.12-x64":{},
|
"osx.10.12-x64":{},
|
||||||
|
50
test/Ocelot.UnitTests/project.json.orig
Normal file
50
test/Ocelot.UnitTests/project.json.orig
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.0-dev",
|
||||||
|
|
||||||
|
"testRunner": "xunit",
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Configuration.Json": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Console": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Logging.Debug": "1.1.0",
|
||||||
|
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Http": "1.1.0",
|
||||||
|
"Ocelot": "0.0.0-dev",
|
||||||
|
"dotnet-test-xunit": "2.2.0-preview2-build1029",
|
||||||
|
"Moq": "4.6.38-alpha",
|
||||||
|
"Microsoft.AspNetCore.TestHost": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Mvc": "1.1.0",
|
||||||
|
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0",
|
||||||
|
"Microsoft.NETCore.App": "1.1.0",
|
||||||
|
"Shouldly": "2.8.2",
|
||||||
|
"TestStack.BDDfy": "4.3.2",
|
||||||
|
"Microsoft.AspNetCore.Authentication.OAuth": "1.1.0",
|
||||||
|
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
|
||||||
|
<<<<<<< HEAD
|
||||||
|
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "1.1.0"
|
||||||
|
=======
|
||||||
|
"xunit": "2.2.0-rc1-build3507"
|
||||||
|
>>>>>>> develop
|
||||||
|
},
|
||||||
|
"runtimes": {
|
||||||
|
"osx.10.11-x64":{},
|
||||||
|
<<<<<<< HEAD
|
||||||
|
"osx.10.12-x64": {},
|
||||||
|
"win7-x64": {}
|
||||||
|
=======
|
||||||
|
"osx.10.12-x64":{},
|
||||||
|
"win7-x64": {},
|
||||||
|
"win10-x64": {}
|
||||||
|
>>>>>>> develop
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"netcoreapp1.1": {
|
||||||
|
"imports": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user