HTTP WebHook validation
Signed-off-by: clemensv <clemensv@microsoft.com>
This commit is contained in:
parent
2e88ade403
commit
b9f10957bf
|
@ -77,6 +77,127 @@ namespace CloudNative.CloudEvents
|
|||
await stream.CopyToAsync(httpWebRequest.GetRequestStream());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the request as WebHook validation request
|
||||
/// </summary>
|
||||
/// <param name="httpRequestMessage">Request</param>
|
||||
/// <param name="validateOrigin">Callback that returns whether the given origin may push events. If 'null', all origins are acceptable.</param>
|
||||
/// <param name="validateRate">Callback that returns the acceptable request rate. If 'null', the rate is not limited.</param>
|
||||
/// <returns>Response</returns>
|
||||
public static async Task<HttpResponseMessage> HandleAsWebHookValidationRequest(
|
||||
this HttpRequestMessage httpRequestMessage, Func<string, bool> validateOrigin,
|
||||
Func<string, string> validateRate)
|
||||
{
|
||||
if (IsWebHookValidationRequest(httpRequestMessage))
|
||||
{
|
||||
var origin = httpRequestMessage.Headers.GetValues("WebHook-Request-Origin").FirstOrDefault();
|
||||
var rate = httpRequestMessage.Headers.GetValues("WebHook-Request-Rate").FirstOrDefault();
|
||||
|
||||
if (origin != null && (validateOrigin == null || validateOrigin(origin)))
|
||||
{
|
||||
if (rate != null)
|
||||
{
|
||||
if (validateRate != null)
|
||||
{
|
||||
rate = validateRate(rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
rate = "*";
|
||||
}
|
||||
}
|
||||
|
||||
if (httpRequestMessage.Headers.Contains("WebHook-Request-Callback"))
|
||||
{
|
||||
var uri = httpRequestMessage.Headers.GetValues("WebHook-Request-Callback").FirstOrDefault();
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
var response = await client.GetAsync(new Uri(uri));
|
||||
return new HttpResponseMessage(response.StatusCode);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK);
|
||||
response.Headers.Add("Allow", "POST");
|
||||
response.Headers.Add("WebHook-Allowed-Origin", origin);
|
||||
response.Headers.Add("WebHook-Allowed-Rate", rate);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new HttpResponseMessage(HttpStatusCode.MethodNotAllowed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the request as WebHook validation request
|
||||
/// </summary>
|
||||
/// <param name="context">Request context</param>
|
||||
/// <param name="validateOrigin">Callback that returns whether the given origin may push events. If 'null', all origins are acceptable.</param>
|
||||
/// <param name="validateRate">Callback that returns the acceptable request rate. If 'null', the rate is not limited.</param>
|
||||
/// <returns>Task</returns>
|
||||
public static async Task HandleAsWebHookValidationRequest(this HttpListenerContext context,
|
||||
Func<string, bool> validateOrigin, Func<string, string> validateRate)
|
||||
{
|
||||
if (IsWebHookValidationRequest(context.Request))
|
||||
{
|
||||
var origin = context.Request.Headers.Get("WebHook-Request-Origin");
|
||||
var rate = context.Request.Headers.Get("WebHook-Request-Rate");
|
||||
|
||||
if (origin != null && (validateOrigin == null || validateOrigin(origin)))
|
||||
{
|
||||
if (rate != null)
|
||||
{
|
||||
if (validateRate != null)
|
||||
{
|
||||
rate = validateRate(rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
rate = "*";
|
||||
}
|
||||
}
|
||||
|
||||
if (context.Request.Headers["WebHook-Request-Callback"] != null)
|
||||
{
|
||||
var uri = context.Request.Headers.Get("WebHook-Request-Callback");
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
var response = await client.GetAsync(new Uri(uri));
|
||||
context.Response.StatusCode = (int)response.StatusCode;
|
||||
context.Response.Close();
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
|
||||
context.Response.Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
||||
context.Response.Headers.Add("Allow", "POST");
|
||||
context.Response.Headers.Add("WebHook-Allowed-Origin", origin);
|
||||
context.Response.Headers.Add("WebHook-Allowed-Rate", rate);
|
||||
context.Response.Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
|
||||
context.Response.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this HttpResponseMessage holds a CloudEvent
|
||||
/// </summary>
|
||||
|
@ -101,6 +222,24 @@ namespace CloudNative.CloudEvents
|
|||
httpListenerRequest.Headers[SpecVersionHttpHeader2] != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this HttpListenerRequest is a web hook validation request
|
||||
/// </summary>
|
||||
public static bool IsWebHookValidationRequest(this HttpRequestMessage httpRequestMessage)
|
||||
{
|
||||
return (httpRequestMessage.Method.Method.Equals("options", StringComparison.InvariantCultureIgnoreCase) &&
|
||||
httpRequestMessage.Headers.Contains("WebHook-Request-Origin"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this HttpListenerRequest is a web hook validation request
|
||||
/// </summary>
|
||||
public static bool IsWebHookValidationRequest(this HttpListenerRequest httpRequestMessage)
|
||||
{
|
||||
return (httpRequestMessage.HttpMethod.Equals("options", StringComparison.InvariantCultureIgnoreCase) &&
|
||||
httpRequestMessage.Headers["WebHook-Request-Origin"] != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts this response message into a CloudEvent object, with the given extensions.
|
||||
/// </summary>
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
|
@ -49,6 +50,13 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
async Task HandleContext(HttpListenerContext requestContext)
|
||||
{
|
||||
var ctxHeaderValue = requestContext.Request.Headers[testContextHeader];
|
||||
|
||||
if (requestContext.Request.IsWebHookValidationRequest())
|
||||
{
|
||||
await requestContext.HandleAsWebHookValidationRequest(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingRequests.TryRemove(ctxHeaderValue, out var pending))
|
||||
{
|
||||
await pending(requestContext);
|
||||
|
@ -64,6 +72,18 @@ namespace CloudNative.CloudEvents.UnitTests
|
|||
#pragma warning restore 4014
|
||||
}
|
||||
|
||||
[Fact]
|
||||
async Task HttpWebHookValidation()
|
||||
{
|
||||
var httpClient = new HttpClient();
|
||||
var req = new HttpRequestMessage(HttpMethod.Options, new Uri(listenerAddress + "ep"));
|
||||
req.Headers.Add("WebHook-Request-Origin", "example.com");
|
||||
req.Headers.Add("WebHook-Request-Rate", "120");
|
||||
var result = await httpClient.SendAsync( req );
|
||||
Assert.Equal("example.com", result.Headers.GetValues("WebHook-Allowed-Origin").First());
|
||||
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
async Task HttpBinaryClientReceiveTest()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue