diff --git a/samples/OcelotEureka/ApiGateway/ApiGateway.csproj b/samples/OcelotEureka/ApiGateway/ApiGateway.csproj
new file mode 100644
index 00000000..d2ec8f48
--- /dev/null
+++ b/samples/OcelotEureka/ApiGateway/ApiGateway.csproj
@@ -0,0 +1,22 @@
+
+
+
+ netcoreapp2.0
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotEureka/ApiGateway/Program.cs b/samples/OcelotEureka/ApiGateway/Program.cs
new file mode 100644
index 00000000..28d344c9
--- /dev/null
+++ b/samples/OcelotEureka/ApiGateway/Program.cs
@@ -0,0 +1,38 @@
+namespace ApiGateway
+{
+ using Microsoft.AspNetCore;
+ using Microsoft.AspNetCore.Hosting;
+ using Microsoft.Extensions.Configuration;
+ using Ocelot.DependencyInjection;
+ using Ocelot.Middleware;
+
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ BuildWebHost(args).Run();
+ }
+
+ public static IWebHost BuildWebHost(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .UseUrls("http://localhost:5000")
+ .ConfigureAppConfiguration((hostingContext, config) =>
+ {
+ config
+ .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
+ .AddJsonFile("appsettings.json", true, true)
+ .AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
+ .AddJsonFile("ocelot.json")
+ .AddEnvironmentVariables();
+ })
+ .ConfigureServices(s =>
+ {
+ s.AddOcelot();
+ })
+ .Configure(a =>
+ {
+ a.UseOcelot().Wait();
+ })
+ .Build();
+ }
+}
diff --git a/samples/OcelotEureka/ApiGateway/Properties/launchSettings.json b/samples/OcelotEureka/ApiGateway/Properties/launchSettings.json
new file mode 100644
index 00000000..c1b4df87
--- /dev/null
+++ b/samples/OcelotEureka/ApiGateway/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:54060/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "ApiGateway": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:54061/"
+ }
+ }
+}
diff --git a/samples/OcelotEureka/ApiGateway/appsettings.json b/samples/OcelotEureka/ApiGateway/appsettings.json
new file mode 100644
index 00000000..4a098bc4
--- /dev/null
+++ b/samples/OcelotEureka/ApiGateway/appsettings.json
@@ -0,0 +1,26 @@
+{
+ "Logging": {
+ "IncludeScopes": true,
+ "LogLevel": {
+ "Default": "Trace",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ },
+ "spring": {
+ "application": { "name": "Ocelot-Gateway" },
+ "cloud": {
+ "config": {
+ "uri": "http://localhost:5000",
+ "validateCertificates": false
+ }
+ }
+ },
+ "eureka": {
+ "client": {
+ "serviceUrl": "http://localhost:8761/eureka/",
+ "shouldRegisterWithEureka": false,
+ "validateCertificates": false
+ }
+ }
+}
diff --git a/samples/OcelotEureka/ApiGateway/ocelot.json b/samples/OcelotEureka/ApiGateway/ocelot.json
new file mode 100644
index 00000000..b8694ddd
--- /dev/null
+++ b/samples/OcelotEureka/ApiGateway/ocelot.json
@@ -0,0 +1,23 @@
+{
+ "ReRoutes": [
+ {
+ "DownstreamPathTemplate": "/api/Category",
+ "DownstreamScheme": "http",
+ "UpstreamPathTemplate": "/Category",
+ "UseServiceDiscovery": true,
+ "ServiceName": "ncore-rat",
+ "UpstreamHttpMethod": [ "Get" ],
+ "QoSOptions": {
+ "ExceptionsAllowedBeforeBreaking": 3,
+ "DurationOfBreak": 10000,
+ "TimeoutValue": 5000
+ },
+ "FileCacheOptions": { "TtlSeconds": 15 }
+ }
+ ],
+ "GlobalConfiguration": {
+ "RequestIdKey": "OcRequestId",
+ "AdministrationPath": "/administration",
+ "ServiceDiscoveryProvider": { "Type": "Eureka" }
+ }
+}
diff --git a/samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs b/samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs
new file mode 100644
index 00000000..53350be7
--- /dev/null
+++ b/samples/OcelotEureka/DownstreamService/Controllers/CategoryController.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Mvc;
+
+namespace DownstreamService.Controllers
+{
+ [Route("api/[controller]")]
+ public class CategoryController : Controller
+ {
+ // GET api/values
+ [HttpGet]
+ public IEnumerable Get()
+ {
+ return new[] { "category1", "category2" };
+ }
+ }
+}
diff --git a/samples/OcelotEureka/DownstreamService/DownstreamService.csproj b/samples/OcelotEureka/DownstreamService/DownstreamService.csproj
new file mode 100644
index 00000000..c1c23abd
--- /dev/null
+++ b/samples/OcelotEureka/DownstreamService/DownstreamService.csproj
@@ -0,0 +1,20 @@
+
+
+
+ netcoreapp2.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/OcelotEureka/DownstreamService/Program.cs b/samples/OcelotEureka/DownstreamService/Program.cs
new file mode 100644
index 00000000..ddce0c28
--- /dev/null
+++ b/samples/OcelotEureka/DownstreamService/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+
+namespace DownstreamService
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ BuildWebHost(args).Run();
+ }
+
+ public static IWebHost BuildWebHost(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .UseUrls($"http://{Environment.MachineName}:5001")
+ .UseStartup()
+ .Build();
+ }
+}
diff --git a/samples/OcelotEureka/DownstreamService/Properties/launchSettings.json b/samples/OcelotEureka/DownstreamService/Properties/launchSettings.json
new file mode 100644
index 00000000..0c084db8
--- /dev/null
+++ b/samples/OcelotEureka/DownstreamService/Properties/launchSettings.json
@@ -0,0 +1,29 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:53908/",
+ "sslPort": 0
+ }
+ },
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "api/values",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "DownstreamService": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "launchUrl": "api/values",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:53909/"
+ }
+ }
+}
diff --git a/samples/OcelotEureka/DownstreamService/Startup.cs b/samples/OcelotEureka/DownstreamService/Startup.cs
new file mode 100644
index 00000000..b3924be6
--- /dev/null
+++ b/samples/OcelotEureka/DownstreamService/Startup.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace DownstreamService
+{
+ using Steeltoe.Discovery.Client;
+
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddDiscoveryClient(Configuration);
+ services.AddMvc();
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseDiscoveryClient();
+ app.UseMvc();
+ }
+ }
+}
diff --git a/samples/OcelotEureka/DownstreamService/appsettings.Development.json b/samples/OcelotEureka/DownstreamService/appsettings.Development.json
new file mode 100644
index 00000000..fa8ce71a
--- /dev/null
+++ b/samples/OcelotEureka/DownstreamService/appsettings.Development.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ }
+}
diff --git a/samples/OcelotEureka/DownstreamService/appsettings.json b/samples/OcelotEureka/DownstreamService/appsettings.json
new file mode 100644
index 00000000..4bebaa50
--- /dev/null
+++ b/samples/OcelotEureka/DownstreamService/appsettings.json
@@ -0,0 +1,24 @@
+{
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": { "Default": "Warning" }
+ },
+ "spring": {
+ "application": { "name": "ncore-rat" },
+ "cloud": {
+ "config": {
+ "uri": "http://localhost:5001",
+ "validate_certificates": false
+ }
+ }
+ },
+ "eureka": {
+ "client": {
+ "serviceUrl": "http://localhost:8761/eureka/",
+ "shouldFetchRegistry": false,
+ "validateCertificates": false
+ },
+ "instance": { "port": 5001 }
+ }
+}
+
diff --git a/samples/OcelotEureka/OcelotEureka.sln b/samples/OcelotEureka/OcelotEureka.sln
new file mode 100644
index 00000000..62eb6ebc
--- /dev/null
+++ b/samples/OcelotEureka/OcelotEureka.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.27428.2027
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DownstreamService", "./DownstreamService/DownstreamService.csproj", "{2982C147-9446-47FE-862E-E689B64CC7E7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiGateway", "./ApiGateway/ApiGateway.csproj", "{006CF27E-5400-43E9-B511-C54EC1B9C546}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2982C147-9446-47FE-862E-E689B64CC7E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2982C147-9446-47FE-862E-E689B64CC7E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2982C147-9446-47FE-862E-E689B64CC7E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2982C147-9446-47FE-862E-E689B64CC7E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {006CF27E-5400-43E9-B511-C54EC1B9C546}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {006CF27E-5400-43E9-B511-C54EC1B9C546}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {006CF27E-5400-43E9-B511-C54EC1B9C546}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {006CF27E-5400-43E9-B511-C54EC1B9C546}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {2C604707-2EA1-4CCF-A89C-22B613052C8D}
+ EndGlobalSection
+EndGlobal
diff --git a/samples/OcelotEureka/README.md b/samples/OcelotEureka/README.md
new file mode 100644
index 00000000..aa803743
--- /dev/null
+++ b/samples/OcelotEureka/README.md
@@ -0,0 +1,39 @@
+#Example how to use Eureka service discovery
+
+I created this becasue users are having trouble getting Eureka to work with Ocelot, hopefully this helps.
+Please review the implementation of the individual servics to understand how everything fits together.
+
+##Instructions
+
+1. Get Eureka installed and running...
+
+ ```
+ $ git clone https://github.com/spring-cloud-samples/eureka.git
+ $ cd eureka
+ $ mvnw spring-boot:run
+ ```
+ Leave the service running
+
+2. Get Downstream service running and registered with Eureka
+
+ ```
+ cd ./DownstreamService/
+ dotnet run
+ ```
+
+ Leave the service running
+
+3. Get API Gateway running and collecting services from Eureka
+
+ ```
+ cd ./ApiGateway/
+ dotnet run
+ ```
+
+ Leave the service running
+
+4. Make a http request to http://localhost:5000/category you should get the following response
+
+ ```json
+ ["category1","category2"]
+ ```
\ No newline at end of file
diff --git a/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs b/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs
index 203d1b9d..69743c06 100644
--- a/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs
+++ b/src/Ocelot/LoadBalancer/LoadBalancers/NoLoadBalancer.cs
@@ -18,6 +18,11 @@ namespace Ocelot.LoadBalancer.LoadBalancers
public async Task> Lease()
{
//todo no point spinning a task up here, also first or default could be null..
+ if (_services == null || _services.Count == 0)
+ {
+ return new ErrorResponse(new ServicesAreEmptyError("There were no services in NoLoadBalancer"));
+ }
+
var service = await Task.FromResult(_services.FirstOrDefault());
return new OkResponse(service.HostAndPort);
}
diff --git a/test/Ocelot.Benchmarks/AllTheThingsBenchmarks.cs b/test/Ocelot.Benchmarks/AllTheThingsBenchmarks.cs
index 3e9e1a4a..55e7a435 100644
--- a/test/Ocelot.Benchmarks/AllTheThingsBenchmarks.cs
+++ b/test/Ocelot.Benchmarks/AllTheThingsBenchmarks.cs
@@ -76,16 +76,16 @@ namespace Ocelot.Benchmarks
response.EnsureSuccessStatusCode();
}
- // * Summary *
- // BenchmarkDotNet=v0.10.13, OS=macOS 10.12.6 (16G1212) [Darwin 16.7.0]
- // Intel Core i5-4278U CPU 2.60GHz (Haswell), 1 CPU, 4 logical cores and 2 physical cores
- // .NET Core SDK=2.1.4
- // [Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
- // DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
+ // * Summary*
+ // BenchmarkDotNet = v0.10.13, OS = macOS 10.12.6 (16G1212) [Darwin 16.7.0]
+ // Intel Core i5-4278U CPU 2.60GHz(Haswell), 1 CPU, 4 logical cores and 2 physical cores
+ //.NET Core SDK = 2.1.4
- // Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Scaled | Gen 0 | Gen 1 | Allocated |
- // --------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|--------:|-------:|----------:|
- // Baseline | 2.102 ms | 0.0292 ms | 0.0273 ms | 0.0070 ms | 2.063 ms | 2.080 ms | 2.093 ms | 2.122 ms | 2.152 ms | 475.8 | 1.00 | 31.2500 | 3.9063 | 1.63 KB |
+ // [Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
+ // DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
+ // Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Scaled | Gen 0 | Gen 1 | Allocated |
+ // --------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|-------:|--------:|-------:|----------:|
+ // Baseline | 2.102 ms | 0.0292 ms | 0.0273 ms | 0.0070 ms | 2.063 ms | 2.080 ms | 2.093 ms | 2.122 ms | 2.152 ms | 475.8 | 1.00 | 31.2500 | 3.9063 | 1.63 KB |
private void GivenOcelotIsRunning(string url)
{
@@ -121,7 +121,7 @@ namespace Ocelot.Benchmarks
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
- var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json");;
+ var configurationPath = Path.Combine(AppContext.BaseDirectory, "ocelot.json");
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
diff --git a/test/Ocelot.Benchmarks/UrlPathToUrlPathTemplateMatcherBenchmarks.cs b/test/Ocelot.Benchmarks/UrlPathToUrlPathTemplateMatcherBenchmarks.cs
index 3f02f48b..678c1934 100644
--- a/test/Ocelot.Benchmarks/UrlPathToUrlPathTemplateMatcherBenchmarks.cs
+++ b/test/Ocelot.Benchmarks/UrlPathToUrlPathTemplateMatcherBenchmarks.cs
@@ -43,8 +43,6 @@ namespace Ocelot.Benchmarks
// .NET Core SDK=2.1.4
// [Host] : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
// DefaultJob : .NET Core 2.0.6 (CoreCLR 4.6.0.0, CoreFX 4.6.26212.01), 64bit RyuJIT
-
-
// Method | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s |
// ----------- |---------:|----------:|----------:|----------:|---------:|---------:|---------:|---------:|---------:|----------:|
// Benchmark1 | 3.133 us | 0.0492 us | 0.0460 us | 0.0119 us | 3.082 us | 3.100 us | 3.122 us | 3.168 us | 3.233 us | 319,161.9 |
diff --git a/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerTests.cs b/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerTests.cs
index 92806cac..74b5c4b0 100644
--- a/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerTests.cs
+++ b/test/Ocelot.UnitTests/LoadBalancer/NoLoadBalancerTests.cs
@@ -29,6 +29,33 @@ namespace Ocelot.UnitTests.LoadBalancer
.BDDfy();
}
+ [Fact]
+ public void should_return_error_if_no_services()
+ {
+ var services = new List();
+
+ this.Given(x => x.GivenServices(services))
+ .When(x => x.WhenIGetTheNextHostAndPort())
+ .Then(x => x.ThenThereIsAnError())
+ .BDDfy();
+ }
+
+ [Fact]
+ public void should_return_error_if_null_services()
+ {
+ List services = null;
+
+ this.Given(x => x.GivenServices(services))
+ .When(x => x.WhenIGetTheNextHostAndPort())
+ .Then(x => x.ThenThereIsAnError())
+ .BDDfy();
+ }
+
+ private void ThenThereIsAnError()
+ {
+ _result.IsError.ShouldBeTrue();
+ }
+
private void GivenServices(List services)
{
_services = services;