Skip to content

Instantly share code, notes, and snippets.

@jfversluis
Created May 29, 2018 06:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jfversluis/c5f1a268f75f1269ed78d43acf368dbf to your computer and use it in GitHub Desktop.
Save jfversluis/c5f1a268f75f1269ed78d43acf368dbf to your computer and use it in GitHub Desktop.
ETAg Middleware
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
namespace AirplaneModeProof.Api.Middleware
{
// https://gist.github.com/madskristensen/36357b1df9ddbfd123162cd4201124c4
public class ETagMiddleware
{
private readonly RequestDelegate _next;
public ETagMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var response = context.Response;
var originalStream = response.Body;
using (var ms = new MemoryStream())
{
response.Body = ms;
await _next(context);
if (IsEtagSupported(response))
{
string checksum = CalculateChecksum(ms);
response.Headers[HeaderNames.ETag] = checksum;
if (context.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var etag) && checksum == etag)
{
response.StatusCode = StatusCodes.Status304NotModified;
return;
}
}
ms.Position = 0;
await ms.CopyToAsync(originalStream);
}
}
private static bool IsEtagSupported(HttpResponse response)
{
if (response.StatusCode != StatusCodes.Status200OK)
return false;
// The 20kb length limit is not based in science. Feel free to change
if (response.Body.Length > 20 * 1024)
return false;
if (response.Headers.ContainsKey(HeaderNames.ETag))
return false;
return true;
}
private static string CalculateChecksum(MemoryStream ms)
{
string checksum = "";
using (var algo = SHA1.Create())
{
ms.Position = 0;
byte[] bytes = algo.ComputeHash(ms);
checksum = $"\"{WebEncoders.Base64UrlEncode(bytes)}\"";
}
return checksum;
}
}
public static class ApplicationBuilderExtensions
{
public static void UseETagger(this IApplicationBuilder app)
{
app.UseMiddleware<ETagMiddleware>();
}
}
}
@yolofy
Copy link

yolofy commented May 15, 2019

Hey Gerald! What is the rationale for upper bound limit for etag support, shouldn't be a lower bound? ie:
if (response.Body.Length <= 20 * 1024) return false; //instead of
if (response.Body.Length > 20 * 1024) return false;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment