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());
|
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>
|
/// <summary>
|
||||||
/// Indicates whether this HttpResponseMessage holds a CloudEvent
|
/// Indicates whether this HttpResponseMessage holds a CloudEvent
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -101,6 +222,24 @@ namespace CloudNative.CloudEvents
|
||||||
httpListenerRequest.Headers[SpecVersionHttpHeader2] != null;
|
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>
|
/// <summary>
|
||||||
/// Converts this response message into a CloudEvent object, with the given extensions.
|
/// Converts this response message into a CloudEvent object, with the given extensions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -7,6 +7,7 @@ namespace CloudNative.CloudEvents.UnitTests
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
|
@ -49,6 +50,13 @@ namespace CloudNative.CloudEvents.UnitTests
|
||||||
async Task HandleContext(HttpListenerContext requestContext)
|
async Task HandleContext(HttpListenerContext requestContext)
|
||||||
{
|
{
|
||||||
var ctxHeaderValue = requestContext.Request.Headers[testContextHeader];
|
var ctxHeaderValue = requestContext.Request.Headers[testContextHeader];
|
||||||
|
|
||||||
|
if (requestContext.Request.IsWebHookValidationRequest())
|
||||||
|
{
|
||||||
|
await requestContext.HandleAsWebHookValidationRequest(null, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (pendingRequests.TryRemove(ctxHeaderValue, out var pending))
|
if (pendingRequests.TryRemove(ctxHeaderValue, out var pending))
|
||||||
{
|
{
|
||||||
await pending(requestContext);
|
await pending(requestContext);
|
||||||
|
@ -64,6 +72,18 @@ namespace CloudNative.CloudEvents.UnitTests
|
||||||
#pragma warning restore 4014
|
#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]
|
[Fact]
|
||||||
async Task HttpBinaryClientReceiveTest()
|
async Task HttpBinaryClientReceiveTest()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue