Introduction
This article explains how to improve application performance using HTTP Handlers and shows it with an example.
To reduce the page load time of the web pages in our application, we can compress content, cache them and then load them on demand. Each time a CSS or a script file is loaded, it takes time for the browser to download the content. That slows down the application until the loading of the CSS or scripts are over. We can use a HTTP Handler to combine the script and CSS files, cache them and then load the compressed content for faster page load in our application. The first time the files are requested, they will be loaded into the cache and then served; each subsequent requests for those files would then be served from the cache only. This would reduce the load time and provide much better responsiveness since the application wouldn't have to wait for the files to be loaded from the disk onto memory and then to the browser's context. Fetching content in this way would improve the application's performance to a considerable extent. We can use a HttpHandler that, when injected into the response stream, would compress the content, cache it and then fetch it to the web page.
Let us start using the Handler
Using this handler is also simple. All we need is a few simple steps outlined below.
1. Include the handler in the web project as in the following:
- #region Namespaces
- using System;
- using System.Net;
- using System.IO;
- using System.IO.Compression;
- using System.Text;
- using System.Web;
- #endregion
-
- namespace PerformanceOptimizationDemo
- {
-
-
-
- public class CompressFiles : IHttpHandler
- {
- #region Data members
-
- private const bool COMPRESSCONTENT = true;
- private const string CACHE_KEY_PREFIX = "HttpCompress.";
- private readonly static TimeSpan CACHE_DURATION = TimeSpan.FromHours(10);
-
- #endregion
-
-
-
-
-
- public void ProcessRequest(HttpContext context)
- {
- HttpRequest request = context.Request;
-
-
-
- string setName = request["s"] ?? string.Empty;
- string contentType = request["t"] ?? string.Empty;
- string version = request["v"] ?? string.Empty;
-
-
- bool isCompressed = COMPRESSCONTENT && this.IsGZipEnabled(context.Request);
-
-
-
- UTF8Encoding encoding = new UTF8Encoding(false);
-
-
-
- if (!this.WriteBytesFromCache(context, setName, version, isCompressed, contentType))
- {
- using (MemoryStream memoryStream = new MemoryStream(5000))
- {
-
-
- using (Stream writer = isCompressed ?
- (Stream)(new GZipStream(memoryStream, CompressionMode.Compress)) :
- memoryStream)
- {
-
-
- string setDefinition =
- System.Configuration.ConfigurationManager.AppSettings[setName] ?? "";
- string[] fileNames = setDefinition.Split(new char[] { ',' },
- StringSplitOptions.RemoveEmptyEntries);
-
- foreach (string fileName in fileNames)
- {
- byte[] fileBytes = this.GetFileContent(context, fileName.Trim(), encoding);
- writer.Write(fileBytes, 0, fileBytes.Length);
- }
-
- writer.Close();
- }
-
-
-
- byte[] responseBytes = memoryStream.ToArray();
- context.Cache.Insert(GetKeyFromCache(setName, version, isCompressed),
- responseBytes, null, System.Web.Caching.Cache.NoAbsoluteExpiration,
- CACHE_DURATION);
-
-
- this.WriteBytes(responseBytes, context, isCompressed, contentType);
- }
- }
- }
-
- #region Private methods
-
-
-
-
-
-
-
-
- private byte[] GetFileContent(HttpContext context, string virtualPath, Encoding encoding)
- {
- if (virtualPath.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
- {
- using (WebClient client = new WebClient())
- {
- return client.DownloadData(virtualPath);
- }
- }
- else
- {
- string physicalPath = context.Server.MapPath(virtualPath);
- byte[] bytes = File.ReadAllBytes(physicalPath);
- return bytes;
- }
- }
-
-
-
-
-
-
-
-
-
-
- private bool WriteBytesFromCache(HttpContext context, string setName, string version,
- bool isCompressed, string contentType)
- {
- byte[] responseBytes = context.Cache[GetKeyFromCache(setName, version, isCompressed)] as byte[];
-
- if (null == responseBytes || 0 == responseBytes.Length) return false;
-
- this.WriteBytes(responseBytes, context, isCompressed, contentType);
- return true;
- }
-
-
-
-
-
-
-
-
- private void WriteBytes(byte[] bytes, HttpContext context,
- bool isCompressed, string contentType)
- {
- HttpResponse response = context.Response;
-
- response.AppendHeader("Content-Length", bytes.Length.ToString());
- response.ContentType = contentType;
-
- if (isCompressed)
- response.AppendHeader("Content-Encoding", "gzip");
-
- context.Response.Cache.SetCacheability(HttpCacheability.Public);
- context.Response.Cache.SetExpires(DateTime.Now.Add(CACHE_DURATION));
- context.Response.Cache.SetMaxAge(CACHE_DURATION);
- context.Response.Cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
-
- response.OutputStream.Write(bytes, 0, bytes.Length);
- response.Flush();
- }
-
-
-
-
-
-
- private bool IsGZipEnabled(HttpRequest request)
- {
- string acceptEncoding = request.Headers["Accept-Encoding"];
- if (!string.IsNullOrEmpty(acceptEncoding) &&
- (acceptEncoding.Contains("gzip") || acceptEncoding.Contains("deflate")))
- return true;
- return false;
- }
-
-
-
-
-
-
-
-
- private string GetKeyFromCache(string setName, string version, bool isCompressed)
- {
- return CACHE_KEY_PREFIX + setName + "." + version + "." + isCompressed;
- }
-
- #endregion
-
-
-
-
- public bool IsReusable
- {
- get
- {
- return true;
- }
- }
- }
- }
2. Define the files to be compressed and cached in the <appSettings> section of the web.config of the web project as in the following:
- <?xml version="1.0"?>
- <configuration>
-
- <appSettings>
- <add key="Css_Files" value="~/Styles/Css1.css,~/Styles/Css2.css,~/Styles/jquery.ribbon.css,~/Styles/liteaccordion.css"/>
- <add key="Script_Files" value="~/Scripts/Js1.js,~/Scripts/Js2.js,~/Scripts/jquery.js"/>
- </appSettings>
-
- <system.web>
- <compilation debug="true" targetFramework="4.0" />
- </system.web>
- </configuration>
3. Invoke the handler in the master page or content page as appropriate, as in the following:
- <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Demo.aspx.cs" Inherits="PerformanceOptimizationDemo.Demo" %>
-
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head runat="server">
- <title>Demo Application</title>
- <link type="text/css"
- rel="Stylesheet"
- href="CompressFiles.ashx?s=Css_Files&t=text/css&v=1" />
- </head>
- <body>
- <script type="text/javascript"
- src="CompressFiles.ashx?s=Script_Files&t=type/javascript&v=1" >
- </script>
-
- <form id="form1" runat="server">
- <div>
-
- </div>
- </form>
- </body>
- </html>
SummaryIn this article we have learned how to optimize application Performance using the HTTP Handler. Please find the attached source code.