Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 2 properties to OcelotPipelineConfiguration class for more flexibility in pipeline #1497

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Ocelot/Middleware/OcelotPipelineConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ public class OcelotPipelineConfiguration
/// </value>
public Func<HttpContext, Func<Task>, Task> AuthenticationMiddleware { get; set; }

/// <summary>
/// This is to allow the user to run any extra authentication after the Ocelot authentication kicks in.
/// </summary>
/// <value>
/// A <see cref="Func{HttpContext, TFunc, Task}"/> delegate object.
/// </value>
public Func<HttpContext, Func<Task>, Task> AfterAuthenticationMiddleware { get; set; }

/// <summary>
/// This is to allow the user to run any extra authorization before the Ocelot authentication kicks in.
/// </summary>
Expand All @@ -47,6 +55,14 @@ public class OcelotPipelineConfiguration
/// </value>
public Func<HttpContext, Func<Task>, Task> AuthorizationMiddleware { get; set; }

/// <summary>
/// This is to allow the user to run any extra authorization after the Ocelot authorization kicks in.
/// </summary>
/// <value>
/// A <see cref="Func{HttpContext, TFunc, Task}"/> delegate object.
/// </value>
public Func<HttpContext, Func<Task>, Task> AfterAuthorizationMiddleware { get; set; }

/// <summary>
/// This allows the user to implement there own query string manipulation logic.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Ocelot/Middleware/OcelotPipelineExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ public static RequestDelegate BuildOcelotPipeline(this IApplicationBuilder app,
app.Use(pipelineConfiguration.AuthenticationMiddleware);
}

// Allow After authentication logic. The idea being people might want to run something custom after what is built in.
app.UseIfNotNull(pipelineConfiguration.AfterAuthenticationMiddleware);

// The next thing we do is look at any claims transforms in case this is important for authorization
app.UseClaimsToClaimsMiddleware();

Expand All @@ -119,6 +122,9 @@ public static RequestDelegate BuildOcelotPipeline(this IApplicationBuilder app,
app.Use(pipelineConfiguration.AuthorizationMiddleware);
}

// Allow after authorization logic. The idea being people might want to run something custom after what is built in.
app.UseIfNotNull(pipelineConfiguration.AfterAuthorizationMiddleware);

// Now we can run the claims to headers transformation middleware
app.UseClaimsToHeadersMiddleware();

Expand Down
155 changes: 123 additions & 32 deletions test/Ocelot.AcceptanceTests/CustomMiddlewareTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Ocelot.Configuration.File;
using Ocelot.Middleware;
using System.Diagnostics;

namespace Ocelot.AcceptanceTests
{
public class CustomMiddlewareTests : IDisposable
Expand All @@ -19,7 +19,7 @@ public CustomMiddlewareTests()
}

[Fact]
public void should_call_pre_query_string_builder_middleware()
public void Should_call_pre_query_string_builder_middleware()
{
var configuration = new OcelotPipelineConfiguration
{
Expand All @@ -28,8 +28,8 @@ public void should_call_pre_query_string_builder_middleware()
_counter++;
await next.Invoke();
},
};

};
var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
Expand Down Expand Up @@ -64,7 +64,7 @@ public void should_call_pre_query_string_builder_middleware()
}

[Fact]
public void should_call_authorization_middleware()
public void Should_call_authorization_middleware()
{
var configuration = new OcelotPipelineConfiguration
{
Expand All @@ -73,8 +73,8 @@ public void should_call_authorization_middleware()
_counter++;
await next.Invoke();
},
};

};
var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
Expand Down Expand Up @@ -109,7 +109,7 @@ public void should_call_authorization_middleware()
}

[Fact]
public void should_call_authentication_middleware()
public void Should_call_authentication_middleware()
{
var configuration = new OcelotPipelineConfiguration
{
Expand All @@ -118,8 +118,8 @@ public void should_call_authentication_middleware()
_counter++;
await next.Invoke();
},
};

};
var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
Expand Down Expand Up @@ -154,7 +154,7 @@ public void should_call_authentication_middleware()
}

[Fact]
public void should_call_pre_error_middleware()
public void Should_call_pre_error_middleware()
{
var configuration = new OcelotPipelineConfiguration
{
Expand All @@ -163,8 +163,8 @@ public void should_call_pre_error_middleware()
_counter++;
await next.Invoke();
},
};

};
var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
Expand Down Expand Up @@ -199,7 +199,7 @@ public void should_call_pre_error_middleware()
}

[Fact]
public void should_call_pre_authorization_middleware()
public void Should_call_pre_authorization_middleware()
{
var configuration = new OcelotPipelineConfiguration
{
Expand All @@ -208,8 +208,8 @@ public void should_call_pre_authorization_middleware()
_counter++;
await next.Invoke();
},
};

};
var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
Expand Down Expand Up @@ -244,7 +244,52 @@ public void should_call_pre_authorization_middleware()
}

[Fact]
public void should_call_pre_http_authentication_middleware()
public void Should_call_after_authorization_middleware()
{
var configuration = new OcelotPipelineConfiguration
{
AfterAuthorizationMiddleware = async (ctx, next) =>
{
_counter++;
await next.Invoke();
},
};

var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
{
Routes = new List<FileRoute>
{
new()
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new()
{
Host = "localhost",
Port = port,
},
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
},
},
};

this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, ""))
.And(x => _steps.GivenThereIsAConfiguration(fileConfiguration))
.And(x => _steps.GivenOcelotIsRunning(configuration))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => x.ThenTheCounterIs(1))
.BDDfy();
}

[Fact]
public void Should_call_pre_http_authentication_middleware()
{
var configuration = new OcelotPipelineConfiguration
{
Expand All @@ -253,8 +298,8 @@ public void should_call_pre_http_authentication_middleware()
_counter++;
await next.Invoke();
},
};

};
var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
Expand Down Expand Up @@ -332,24 +377,69 @@ public void should_not_throw_when_pipeline_terminates_early()
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => x.ThenTheCounterIs(1))
.BDDfy();
}

}

[Fact]
public void Should_call_after_http_authentication_middleware()
{
var configuration = new OcelotPipelineConfiguration
{
AfterAuthenticationMiddleware = async (ctx, next) =>
{
_counter++;
await next.Invoke();
},
};

var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
{
Routes = new List<FileRoute>
{
new()
{
DownstreamPathTemplate = "/",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new()
{
Host = "localhost",
Port = port,
},
},
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
},
},
};

this.Given(x => x.GivenThereIsAServiceRunningOn($"http://localhost:{port}", 200, ""))
.And(x => _steps.GivenThereIsAConfiguration(fileConfiguration))
.And(x => _steps.GivenOcelotIsRunning(configuration))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => x.ThenTheCounterIs(1))
.BDDfy();
}

[Fact(Skip = "This is just an example to show how you could hook into Ocelot pipeline with your own middleware. At the moment you must use Response.OnCompleted callback and cannot change the response :( I will see if this can be changed one day!")]
public void should_fix_issue_237()
public void Should_fix_issue_237()
{
Func<object, Task> callback = state =>
{
var httpContext = (HttpContext)state;

if (httpContext.Response.StatusCode > 400)
{
{
Debug.WriteLine("COUNT CALLED");
Console.WriteLine("COUNT CALLED");
}

return Task.CompletedTask;
};

};
var port = PortFinder.GetRandomPort();

var fileConfiguration = new FileConfiguration
Expand Down Expand Up @@ -406,15 +496,16 @@ private void GivenThereIsAServiceRunningOn(string url, int statusCode, string ba

public void Dispose()
{
_serviceHandler?.Dispose();
_steps.Dispose();
_serviceHandler.Dispose();
_steps.Dispose();
GC.SuppressFinalize(this);
}

public class FakeMiddleware
{
private readonly RequestDelegate _next;
private readonly Func<object, Task> _callback;

private readonly Func<object, Task> _callback;
public FakeMiddleware(RequestDelegate next, Func<object, Task> callback)
{
_next = next;
Expand All @@ -423,10 +514,10 @@ public FakeMiddleware(RequestDelegate next, Func<object, Task> callback)

public async Task Invoke(HttpContext context)
{
await _next(context);

await _next(context);
context.Response.OnCompleted(_callback, context);
}
}
}
}
}