Skip to content

Commit

Permalink
Merge pull request #3 from twitchax/context_fix
Browse files Browse the repository at this point in the history
Pass HttpContext into lambda.
  • Loading branch information
twitchax committed Jan 2, 2019
2 parents 974f771 + 202b2d7 commit 54933e4
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/Core/AspNetCore.Proxy.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.2.2</Version>
<Version>1.3.0</Version>
<AssemblyName>AspNetCore.Proxy</AssemblyName>
<PackageId>AspNetCore.Proxy</PackageId>
<DocumentationFile>bin\AspNetCore.Proxy.xml</DocumentationFile>
Expand Down
105 changes: 98 additions & 7 deletions src/Core/ProxyRouteExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public static void UseProxies(this IApplicationBuilder app)
var attribute = method.GetCustomAttributes(typeof(ProxyRouteAttribute), false).First() as ProxyRouteAttribute;
var parameters = method.GetParameters();

if(!(method.ReturnType == typeof(Task<string>)))
throw new InvalidOperationException($"Proxied generator method ({name}) must return a Task<string>.");
if(method.ReturnType != typeof(Task<string>) && method.ReturnType != typeof(string))
throw new InvalidOperationException($"Proxied generator method ({name}) must return a `Task<string>` or `string`.");

if(!method.IsStatic)
throw new InvalidOperationException($"Proxied generator method ({name}) must be static.");
Expand All @@ -49,7 +49,12 @@ public static void UseProxies(this IApplicationBuilder app)
}
});
return method.Invoke(null, castedArgs.ToArray()) as Task<string>;
// Make sure to always return a `Task<string>`, but allow methods that just return a `string`.
if(method.ReturnType == typeof(Task<string>))
return method.Invoke(null, castedArgs.ToArray()) as Task<string>;
return Task.FromResult(method.Invoke(null, castedArgs.ToArray()) as string);
});
}
}
Expand All @@ -59,16 +64,16 @@ public static void UseProxies(this IApplicationBuilder app)
/// </summary>
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
/// <param name="endpoint">The local route endpoint.</param>
/// <param name="getProxiedAddress">A functor which returns the address to which the request is proxied.</param>
/// <param name="onFailure">A catch all for failures.</param>
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<IDictionary<string, object>, Task<string>> getProxiedAddress, Func<HttpContext, Exception, Task> onFailure = null)
/// <param name="getProxiedAddress">A lambda { (context, args) => Task[string] } which returns the address to which the request is proxied.</param>
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => Task }.</param>
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<HttpContext, IDictionary<string, object>, Task<string>> getProxiedAddress, Func<HttpContext, Exception, Task> onFailure = null)
{
app.UseRouter(builder => {
builder.MapMiddlewareRoute(endpoint, proxyApp => {
proxyApp.Run(async context => {
try
{
var proxiedAddress = await getProxiedAddress(context.GetRouteData().Values.ToDictionary(v => v.Key, v => v.Value)).ConfigureAwait(false);
var proxiedAddress = await getProxiedAddress(context, context.GetRouteData().Values.ToDictionary(v => v.Key, v => v.Value)).ConfigureAwait(false);
var proxiedResponse = await context.SendProxyHttpRequest(proxiedAddress).ConfigureAwait(false);
await context.CopyProxyHttpResponse(proxiedResponse).ConfigureAwait(false);
Expand All @@ -89,5 +94,91 @@ public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<
});
});
}

#region UseProxy Overloads

/// <summary>
/// Middleware which creates an ad hoc proxy over a specified endpoint.
/// </summary>
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
/// <param name="endpoint">The local route endpoint.</param>
/// <param name="getProxiedAddress">A lambda { (args) => Task[string] } which returns the address to which the request is proxied.</param>
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => Task }.</param>
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<IDictionary<string, object>, Task<string>> getProxiedAddress, Func<HttpContext, Exception, Task> onFailure = null)
{
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => getProxiedAddress(args);

UseProxy(app, endpoint, gpa, onFailure);
}

/// <summary>
/// Middleware which creates an ad hoc proxy over a specified endpoint.
/// </summary>
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
/// <param name="endpoint">The local route endpoint.</param>
/// <param name="getProxiedAddress">A lambda { () => Task[string] } which returns the address to which the request is proxied.</param>
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => Task }.</param>
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<Task<string>> getProxiedAddress, Func<HttpContext, Exception, Task> onFailure = null)
{
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => getProxiedAddress();

UseProxy(app, endpoint, gpa, onFailure);
}

/// <summary>
/// Middleware which creates an ad hoc proxy over a specified endpoint.
/// </summary>
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
/// <param name="endpoint">The local route endpoint.</param>
/// <param name="getProxiedAddress">A lambda { (context, args) => string } which returns the address to which the request is proxied.</param>
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => void }.</param>
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<HttpContext, IDictionary<string, object>, string> getProxiedAddress, Action<HttpContext, Exception> onFailure = null)
{
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => Task.FromResult(getProxiedAddress(context, args));

Func<HttpContext, Exception, Task> of = null;
if(onFailure != null)
of = (context, e) => { onFailure(context, e); return Task.FromResult(0); };

UseProxy(app, endpoint, gpa, of);
}

/// <summary>
/// Middleware which creates an ad hoc proxy over a specified endpoint.
/// </summary>
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
/// <param name="endpoint">The local route endpoint.</param>
/// <param name="getProxiedAddress">A lambda { (args) => string } which returns the address to which the request is proxied.</param>
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => void }.</param>
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<IDictionary<string, object>, string> getProxiedAddress, Action<HttpContext, Exception> onFailure = null)
{
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => Task.FromResult(getProxiedAddress(args));

Func<HttpContext, Exception, Task> of = null;
if(onFailure != null)
of = (context, e) => { onFailure(context, e); return Task.FromResult(0); };

UseProxy(app, endpoint, gpa, of);
}

/// <summary>
/// Middleware which creates an ad hoc proxy over a specified endpoint.
/// </summary>
/// <param name="app">The ASP.NET <see cref="IApplicationBuilder"/>.</param>
/// <param name="endpoint">The local route endpoint.</param>
/// <param name="getProxiedAddress">A lambda { () => string } which returns the address to which the request is proxied.</param>
/// <param name="onFailure">A lambda to handle proxy failures { (context, exception) => void }.</param>
public static void UseProxy(this IApplicationBuilder app, string endpoint, Func<string> getProxiedAddress, Action<HttpContext, Exception> onFailure = null)
{
Func<HttpContext, IDictionary<string, object>, Task<string>> gpa = (context, args) => Task.FromResult(getProxiedAddress());

Func<HttpContext, Exception, Task> of = null;
if(onFailure != null)
of = (context, e) => { onFailure(context, e); return Task.FromResult(0); };

UseProxy(app, endpoint, gpa, of);
}

#endregion
}
}
102 changes: 95 additions & 7 deletions src/Test/UnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,79 @@ public UnitTests()
}

[Fact]
public async Task ProxyAttribute()
public async Task ProxyAttributeToTask()
{
var response = await _client.GetAsync("api/posts/1");
var response = await _client.GetAsync("api/posts/totask/1");
response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("sunt aut facere repellat provident occaecati excepturi optio reprehenderit", JObject.Parse(responseString).Value<string>("title"));
}

[Fact]
public async Task ProxyMiddleware()
public async Task ProxyAttributeToString()
{
var response = await _client.GetAsync("api/comments/1");
var response = await _client.GetAsync("api/posts/tostring/1");
response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("sunt aut facere repellat provident occaecati excepturi optio reprehenderit", JObject.Parse(responseString).Value<string>("title"));
}

[Fact]
public async Task ProxyMiddlewareWithContextAndArgsToTask()
{
var response = await _client.GetAsync("api/comments/contextandargstotask/1");
response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
}

[Fact]
public async Task ProxyMiddlewareWithArgsToTask()
{
var response = await _client.GetAsync("api/comments/argstotask/1");
response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
}

[Fact]
public async Task ProxyMiddlewareWithEmptyToTask()
{
var response = await _client.GetAsync("api/comments/emptytotask");
response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
}

[Fact]
public async Task ProxyMiddlewareWithContextAndArgsToString()
{
var response = await _client.GetAsync("api/comments/contextandargstostring/1");
response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
}

[Fact]
public async Task ProxyMiddlewareWithArgsToString()
{
var response = await _client.GetAsync("api/comments/argstostring/1");
response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("id labore ex et quam laborum", JObject.Parse(responseString).Value<string>("name"));
}

[Fact]
public async Task ProxyMiddlewareWithEmptyToString()
{
var response = await _client.GetAsync("api/comments/emptytostring");
response.EnsureSuccessStatusCode();

var responseString = await response.Content.ReadAsStringAsync();
Expand All @@ -54,18 +114,46 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseProxies();

app.UseProxy("api/comments/{postId}", (args) => {
app.UseProxy("api/comments/contextandargstotask/{postId}", (context, args) => {
context.GetHashCode();
return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/{args["postId"]}");
});

app.UseProxy("api/comments/argstotask/{postId}", (args) => {
return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/{args["postId"]}");
});

app.UseProxy("api/comments/emptytotask", () => {
return Task.FromResult($"https://jsonplaceholder.typicode.com/comments/1");
});

app.UseProxy("api/comments/contextandargstostring/{postId}", (context, args) => {
context.GetHashCode();
return $"https://jsonplaceholder.typicode.com/comments/{args["postId"]}";
});

app.UseProxy("api/comments/argstostring/{postId}", (args) => {
return $"https://jsonplaceholder.typicode.com/comments/{args["postId"]}";
});

app.UseProxy("api/comments/emptytostring", () => {
return $"https://jsonplaceholder.typicode.com/comments/1";
});
}
}

public static class UseProxies
{
[ProxyRoute("api/posts/{postId}")]
public static Task<string> ProxyGoogle(int postId)
[ProxyRoute("api/posts/totask/{postId}")]
public static Task<string> ProxyGoogleToTask(int postId)
{
return Task.FromResult($"https://jsonplaceholder.typicode.com/posts/{postId}");
}

[ProxyRoute("api/posts/tostring/{postId}")]
public static string ProxyGoogleToString(int postId)
{
return $"https://jsonplaceholder.typicode.com/posts/{postId}";
}
}
}

0 comments on commit 54933e4

Please sign in to comment.