More on GZip compression with ASP.NET Content
After I posted the GZip Script Compression module code a while back, I’ve gotten a number of questions regarding GZip and compression in ASP.NET applications so I thought I show a couple of other ways you can use the new GZipStream class in ASP.NET.
The beauty of this new GZip support is how easy it is to use in your own ASP.NET code. I use the GZip functionality a bit in my WebLog code. For example the the cached RSS feed is GZip encoded which uses GZipStream on the Response.OutputStream fed to an XmlWriter():
Response.ContentType = "text/xml";
Stream Output = Response.OutputStream;
if (Westwind.Tools.wwWebUtils.IsGZipSupported())
{
GZipStream gzip = new GZipStream(Response.OutputStream, CompressionMode.Compress);
Response.AppendHeader("Content-Encoding", "gzip");
Output = gzip;
}
Encoding Utf8 = new UTF8Encoding(false); // No BOM!
XmlTextWriter Writer = new XmlTextWriter(Output,Utf8);
Writer.Formatting = Formatting.Indented;
Writer.WriteStartElement("rss");
…
Writer.WriteEndElement(); // rss
Writer.Close();
Response.End();
GZipStream is a stream acts like a stream filter so you can assign it to an existing stream like ResponseStream can intercept all inbound data then write the updated data into the actual Response stream. It’s super easy to do.
You should always check however if GZip is enabled which is done by this helper method IsGZipSupported() which looks like this:
/// <summary>
/// Determines if GZip is supported
/// </summary>
/// <returns></returns>
public static bool IsGZipSupported()
{
string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
if (!string.IsNullOrEmpty(AcceptEncoding) &&
AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate") )
return true;
return false;
}
This ensures that you don’t encode content when the client doesn’t understand GZip and would be unable to read the encoded GZip content. Note that the code checks for either gzip or deflate so this assumes before encoding you’ll pick the right encoding algorithm.
GZip in Page Content
Speaking of encoding - above was XML and raw ResponseStream encoding, but you can also apply GZip content very, very easily to your page level or any ASP.NET level code (such as in an HTTP handler). In fact, you can use a very generic mechanism to encode any output by using a Response.Filter which the following helper method (also in wwWebUtils) demonstrates:
/// <summary> /// Sets up the current page or handler to use GZip through a Response.Filter /// IMPORTANT: /// You have to call this method before any output is generated! /// </summary> public static void GZipEncodePage() { HttpResponse Response = HttpContext.Current.Response; if (IsGZipSupported()) { string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"]; if (AcceptEncoding.Contains("deflate")) { Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter, System.IO.Compression.CompressionMode.Compress); Response.AppendHeader("Content-Encoding", "deflate"); } else { Response.Filter = new System.IO.Compression.GZipStream(Response.Filter, System.IO.Compression.CompressionMode.Compress); Response.AppendHeader("Content-Encoding", "gzip"); } } // Allow proxy servers to cache encoded and unencoded versions separately Response.AppendHeader("Vary", "Content-Encoding"); }
You can now take this helper function and use this in page level code. For example, the main page in my blog which is HUGE (frequently around 500k) uses it like this:
protected void Page_Load(object sender, EventArgs e)
{
wwWebUtils.GZipEncodePage();
Entry = WebLogFactory.GetEntry();
if (Entry.GetLastEntries(App.Configuration.ShowEntryCount, "pk,Title,Body,Entered,Feedback,Location") < 0)
throw new ApplicationException("Couldn't load WebLog Entries: " + Entry.ErrorMessage);
this.repEntries.DataSource = this.Entry.EntryList;
this.repEntries.DataBind();
}
That’s it. One line and the page will now conditionally GZip encode if the client supports it.
If you really wanted to you can take this even one step further and create a module that automatically sets the Response.Filter early in the pipeline and based on that automatically compresses all content.
However, remember GZip encoding applies compression on the fly so there’s some overhead in the GZip encoding – you are basically adding more CPU processing to your content to reduce the output size. If your content is not that large to start with there’s probably not that much sense in compressing in the first place so I don’t think that whole-sale compression of dynamic content is a good idea.
Caching
But one thing that can mitigate the overhead of GZip compression is caching.
Ah yes, both the RSS feed and the home page are usually heavily cached – the content doesn’t change all that frequently so there’s no reason to keep re-generating it right? If you use GZip on your content you have to be careful to cache both the GZipped content and the non-encoded content or else you’ll feed garbage to clients that don’t understand GZip.
So for example the default page has:
<%@ OutputCache Duration="60" VaryByParam="none" VaryByCustom="GZIP" %>
And then a custom Global.asax handler that looks like this:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "GZIP")
{
if (Westwind.Tools.wwWebUtils.IsGZipSupported())
return "GZip";
return "";
}
return base.GetVaryByCustomString(context, custom);
}
Which results in possibly two different versions of the GZipped page being cached.
And there you have it - GZipped content is easy to create now in ASP.NET 2.0/.NET 2.0 and if judiciously applied it can save some significant bandwidth. On my homepage which is close to 500k of blog text content (gotta review that <s>) the GZipped size is 55k or so – nearly a 90% reduction in size. I’d say that’s plenty of worth it, especially when caching is added.
Other Posts you might also like
The Voices of Reason
# re: More on GZip compression with ASP.NET Content
BTW, your site has some great stuff on there! If you haven't gone over to Milan's site do check it out - there are a ton of great articles and a number of useful tools over there.
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
http://weblogs.asp.net/owscott/archive/2004/01/12/57916.aspx
# re: More on GZip compression with ASP.NET Content
http://www.sitemapwriter.com
# re: More on GZip compression with ASP.NET Content
The null check below, requires another set of parenthesis, since A and B or C is true if C is true, clearly the intention was A and (B or C).
if (!string.IsNullOrEmpty(AcceptEncoding) &&
AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate"))
should be:
if (!string.IsNullOrEmpty(AcceptEncoding) &&
(AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate")))
Anyway, great article, and thanks for the awesome .net website.
# re: More on GZip compression with ASP.NET Content
If Request.Headers("Accept-Encoding") IsNot Nothing Then
If LCase(Request.Headers("Accept-Encoding")).Contains("gzip") Then
Response.Filter = New GZipStream(Response.Filter, CompressionMode.Compress)
Response.AddHeader("Content-Encoding", "gzip")
ElseIf LCase(Request.Headers("Accept-Encoding")).Contains("deflate") Then
Response.Filter = New DeflateStream(Response.Filter, CompressionMode.Compress)
Response.AddHeader("Content-Encoding", "deflate")
End If
End If
This works fine for the ASPX pages, but for some reason it screws up CSS, and image files. Any ideas?
(note that I can't use IIS compression features because of the wildcard mapping this doesn't seem to work)
# re: More on GZip compression with ASP.NET Content
Thanks for the information.
# re: More on GZip compression with ASP.NET Content
how can Gzip less size page like homepage with CSS File and JavaScript file
it can compression all files with same time
thanks for your Time
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
That'll teach me for not reading through all the comments before using the code in a project.
Please, can the article be updated to include the extra brackets suggested.
# re: More on GZip compression with ASP.NET Content
Can any one help me out here.
Thanks
Shikhar
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
For example, i use jQuery for my client output and with that I can easily encode all my output via GZip with no problems.
# re: More on GZip compression with ASP.NET Content
If you are just calling the code above in the Page_Init or Page_Load you shouldn't have to worry about this, but if you have the compression code wired up in Global.asax in BeginRequest or something like that, then you'll need to ensure that you skip compression for requests to WebResource.axd.
There are more elegant ways to do this, but to just hardcode an exclusion of WebResource.axd you could do something like this
public static void GZipEncodePage() { if (IsGZipSupported() && HttpContext.Current.Request.Path.IndexOf("WebResource.axd") == -1) { // snipped.... but do compression here } }
# re: More on GZip compression with ASP.NET Content
I haven't check with MS AJAX output - but shouldn't that be ScriptResource.axd rather than WebResource.axd? The reason that doesn't work most likely is that MS AJAX internally injects into the Http pipeline and applies its own compression so there'd be a problem.
Additional caution may have to be taken with UpdatePanel pages. I'm not sure offhand if this would break UpdatePanel results which also flow through the page pipeline - mainly because it too injects into the page nad filters the Response data.
In short anything that applies Response Filters should not use code the above code for checking whether compression can be applied because the filter order may screw up the compression (especially if another filter already applies compression).
# re: More on GZip compression with ASP.NET Content
After I managed to attach with my project it worked...
however after some clicking on my site there and then the site simply crashed.. the web page became a lot of garbage text.. to give some example.
� �������`I�%&/m�{J�J��t��`$ؐ@�����iG#)�*��eVe]f@�흼 ��{���{��;�N'���?\fdl��J�ɞ!���?~|?"~� �=�]�~y���yy���E��������w�{���ݧo�����|�<��o�l�mQ-������ ��v���ݫ���սqU_�}���;������v�9������s��ݢ\6�E �>|�P�|��u�͎/�6K��"��˼.ί�/w?J�ղ͗�g��}� ����O��>�߽�{���O�|���}�^N�Ի���㝟|Y_|�Qz7��6����Yせ� ��(��)�[�.Fi�M�b���t����YN_44�6����u��2����ҫ˲X��G��k�I��3�`Z�W�4k ��Qz^�y;�m[җM���'뢜�ҫ<[U� ˲�ȗS�����:GӺ�j��� �i]�0�0�����9ƞM�1�`(�����#�^��:O��u�W�v��uE_V�i�L�٢������hf��~ [�6��8(!oD�X宀~L/�M�u~��G��X6�u��� �p���}�7s��Gi{�"�`~�r���XJ���X��6�4`Ջ��(����i��jqw]O��r��M���L��p������M?K?��x����;��o�t(_Jo$�ӷy�uG?}|W@��aX��I5����W�B�#kV˼}F|��̫�g���P���g��EѴy=���>�Ii֓EA<��~T��^���'����|�m���b�\f�%~���b^�f9q�`�����?y��͛�W�����.�rM ��7C����~
This weird text was given after around 3-4 times clicking ...
what do you think is the problem?
Am I missing something?
Thanks!
# re: More on GZip compression with ASP.NET Content
Have a question about the last comment on VaryByCustom in the OutputCache. Can't you use the VaryByContentEncodings attribute of OutputCache?
Loved your simple math captcha below too :).
# re: More on GZip compression with ASP.NET Content
Response.AppendHeader("Vary", "Accept-Encoding");
even if you don't do output cache, since proxy servers in between may cache (read from pg 33 of Steve Souder's High Performance Websites book :))?
# re: More on GZip compression with ASP.NET Content
# RE: (Martin) More on GZip compression with ASP.NET Content
You are getting garbage because your server (or hosting company) is already applying URL compression to your web site,
So your pages are getting compressed twice,
You will probably will see it when removing temp files (or refresh while holding CTRL+F5)
Here is what you probably have setup on your server: http://www.iis.net/ConfigReference/system.webServer/urlCompression
Just add this in your web config to avoid compression conflicts:
<system.webServer>
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" />
</system.webServer>
# re: More on GZip compression with ASP.NET Content
Something may also be causing the page_load to occur twice. I found I could use this gzip/deflate into my sitemaster, but not in a page that didn't. For some reason, the page_load is being called twice on a single request. It may have something to do with the page using callbacks, and some asynchronous code. I had to put some extra code in, to not compress on subsequent calls for that page instance.
# re: More on GZip compression with ASP.NET Content
If you want to cache the compressed content, you might consider using IIS compression with <urlCompression dynamicCompressionBeforeCache="true" />
# re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
Anyone faced this problem?
# re: More on GZip compression with ASP.NET Content
Is there an easy way to see if its working?
Trace does not show anything I can see.
Here is the vb.net code:
Public Shared Sub GZipEncodePage()
Dim Response As HttpResponse = HttpContext.Current.Response
'If (IsGZipSupported() And HttpContext.Current.Request.Path.IndexOf(".axd") = -1) Then
If (IsGZipSupported()) Then
Dim AcceptEncoding As String = HttpContext.Current.Request.Headers("Accept-Encoding")
If (AcceptEncoding.Contains("deflate")) Then
Response.Filter = New System.IO.Compression.DeflateStream(Response.Filter, System.IO.Compression.CompressionMode.Compress)
Response.AppendHeader("Content-Encoding", "deflate")
Else
Response.Filter = New System.IO.Compression.GZipStream(Response.Filter, System.IO.Compression.CompressionMode.Compress)
Response.AppendHeader("Content-Encoding", "gzip")
End If
End If
' Allow proxy servers to cache encoded and unencoded versions separately
Response.AppendHeader("Vary", "Content-Encoding")
Response.AppendHeader("Vary", "Accept-Encoding")
End Sub
Public Shared Function IsGZipSupported() As Boolean
Dim AcceptEncoding As String = HttpContext.Current.Request.Headers("Accept-Encoding")
If (Not String.IsNullOrEmpty(AcceptEncoding) And AcceptEncoding.Contains("gzip") Or AcceptEncoding.Contains("deflate")) Then
Return True
End If
Return False
End Function
# re: More on GZip compression with ASP.NET Content
Thanks again!
# # re: More on GZip compression with ASP.NET Content
# re: More on GZip compression with ASP.NET Content
for problem with garbage encoding there is another workaround.
On Application_Error remove filter from response:
protected void Application_Error(Object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
app.Response.Filter = null;
}
This helps me a lot.
# re: More on GZip compression with ASP.NET Content
Bit late to the party but this is very handy. Been trying to figure out how to compress and outputcache pages for ages.
Had a problem with the first two lines of IsGZipSupported() like a few others. I was getting an error (Object reference not set to an instance of an object.) when I was checking what was returned in a client didn't accept compressed responses (using this handy tool http://site-perf.com/)
Changed it to (i use VB)
Dim AcceptEncoding As String = HttpContext.Current.Request.Headers("Accept-Encoding") & ""
If AcceptEncoding <> "" And (AcceptEncoding.Contains("gzip") Or AcceptEncoding.Contains("deflate")) Then
Works great for me now. Thanks a lot dude.
Cheers
# re: More on GZip compression with ASP.NET Content
Like yourself, I've been advocating HTTP compression for a long time and even put together a tool that measures how much bandwidth you can save with various methods and levels of compression: http://www.aspnetresources.com/tools/httpcompression.aspx
Folks in charge of Microsoft.com had better embrace compression. They could be saving ~80%!