Jump To …

WebCacheHttpHandler.cs

HttpHandler to look for cached assets, and if they exist, send them compressed with long-lived HTTP cache headers.

using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace WebCache
{
	public class WebCacheHttpHandler : IHttpHandler
	{
		public bool IsReusable { get { return false; } }

		public void ProcessRequest(HttpContext context)
		{
			var response = context.Response;
			var path = context.Request.Path;

			var cachedAsset = WebCache.Bundles
				.SelectMany(b => b.Value)
				.SingleOrDefault(a => a.CachedVirtualPath == path);

			if (cachedAsset == null)
			{
				response.StatusCode = (int)HttpStatusCode.NotFound;
				response.End();
				return;
			}

			var file = cachedAsset.File;

			if (!cachedAsset.File.Exists)
			{
				response.StatusCode = (int)HttpStatusCode.NotFound;
				response.End();
				return;
			}

			SetHeaders(response);
			response.ContentType = GetContentType(file.Extension);

TODO: Add support for content negotiation

			response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);

TransmitFile() is awesome and doesn't buffer the file in memory. This is the reason cached assets are written to disk.

			response.TransmitFile(file.FullPath);
		}

Based on Google-suggested best practice.

		private void SetHeaders(HttpResponse response)
		{
			var now = DateTime.UtcNow;
			var expires = now.AddYears(1);
			var lastModified = now.AddMonths(-1);

			response.ClearHeaders();
			response.AppendHeader("Content-Encoding", "gzip");
			response.AppendHeader("Vary", "Accept-Encoding");
			response.AppendHeader("Last-Modified", lastModified.ToHttpDate());
			response.AppendHeader("Expires", expires.ToHttpDate());
			response.AppendHeader("Cache-Control", "public");
		}

		private string GetContentType(string fileExtension)
		{
			switch (fileExtension)
			{
				case ".css":
					return "text/css";

There are two other obsoleted content types for JavaScript.

				case ".js":					
					return "application/javascript"; 

				default:
					return "application/unknown";
			}
		}
	}
}