The idea for the original title of this article came from the 1985 Swedish foreign
film "My Life As a Dog" which I highly reccommend. Few Hollywood movies ever achieve the sensitivity,
simple beauty and depth of some of the foreign films I've come to love. The
"Redux" part is because it is, as the Latin phrase indicates, "back
again" - improved, and now - Silverlight compatible.
I've always been an "experimenter". When I was a kid, I reengineered
my CB radio with an illegal 40 watt power tube (the legal limit then was only
5 watts). I also set Hi Tor mountain in Haverstraw, NY on fire. And that was
just the beginning. Now, I experiment a bit more safely with code, and to a lesser
extent with digitally processed photography (Maya) and music (hang drum, didjeridoo,
and flute). Some experiments don't work, and need to be thrown out. But others
can produce really useful things when you are a programmer building up your personal
codebase of "tricks". The compressed cookie, which I like to think
I invented, is one of those successful experiments.

The question now becomes: Why would somebody want to compress an HTTP Cookie? There
are several reasons I can think of, and all of them seem valid to me:
1) Because you can!
2) Store more information in a single cookie. A LOT more information -- for example, an
entire serialized class instance with all it's property values.
3) Keep prying eyes out of your cookie business. Compressed cookies are virtually
impossible to figure out and decode.
4) Performance on both storing and retrieving compressed cookies is excellent.
5) This all works in Silverlight too.
To create a compressed cookie, we need to be able to do four things:
1. Efficiently serialize whatever object instance we want to store to a byte array.
2. Compress the byte array to allow for more storage within the legal limit for an
HTTP cookie (approximately 4096 characters).
3. Base64 encode the compressed bytes so that they can be stored as legal HTTP Cookie
characters.
4. Set a normal HTTP browser cookie with the correct name, value and expiration date
using our compressed payload as the value.
To read a compressed cookie, we simply perform the above operations in reverse:
1. Read the cookie the way we normally would do.
2. Get the compressed byte array from the base64 encoded cookie value.
3. Decompress the byte array.
4. Deserialize the byte array into an instance of the original type so it can be
used in our code again.
Compressed cookies can be used to store settings, to persist state between page navigations,
and other uses. The algorithm itself does not have to be used only for cookies
- the same code can be modified so that it will serialize and compress objects
for transmission over the wire instead.
Let's have a look at some code!
For the compression algorithm, I chose the QuickLZ data compression library by Lasse
Mikkel Reinhold. This choice was based on my research in this article on comparison of Data Compression Algorithms.
The QuickLZ algorithm is Silverlight-compatible and provides excellent compression
ratios combined with very fast decompression, making it the ideal choice for
this project. The only things I have added to this class are two extension methods
on the byte[] type, which makes it easier to use.
For Serialization and Deserialization, I chose Mike Talbot's Silverlight Serializer
class. You can see Mike's work here. I made a couple of contributions to Mike's work, most notably a way to deserialize
a type that has no parameterless constructor, and he has already incorporated
my ideas. Silverlight Serializer is not only very fast, it also produces byte
arrays that are about 30% smaller than the BinaryFormatter - which of course,
is not even available in Silverlight.
You can examine either of these classes in the downloadable solutions I've made
available at the bottom of this article.
Here is my standard CpCookies class that handles everything:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using QuickLZSharp;
using Serialization;
namespace CompressedCookies
{
public static class CpCookies
{
private static object DeserializeBase64Decompress(string cookieName)
{
return SilverlightSerializer.Deserialize(Convert.FromBase64String(HttpContext.Current.Request.Cookies[cookieName].Value).Decompress());
}
private static void SetCookie(string cookieName, string sCookieValue, DateTime expirationDate)
{
HttpCookie cook = new HttpCookie(cookieName);
cook.Value = sCookieValue;
cook.Expires = expirationDate;
HttpContext.Current.Response.Cookies.Add(cook);
}
public static int Set(string cookieName, object cookieValue, DateTime expirationDate)
{
int siz =0;
try
{
string sCookieVal = SerializeAndCompressBase64(cookieValue);
siz = sCookieVal.Length;
if (siz > 4095)
throw new InvalidOperationException("Cookie value cannot exceed 4095 characters.");
SetCookie(cookieName ,sCookieVal ,expirationDate );
}
catch
{
throw ;
}
return siz;
}
private static string SerializeAndCompressBase64( object cookieValue)
{
return Convert.ToBase64String(SilverlightSerializer.Serialize(cookieValue).Compress());
}
public static object Get(string cookieName)
{
object retval = null;
try
{
retval = DeserializeBase64Decompress(cookieName);
}
catch
{
throw ;
}
return retval;
}
public static bool Delete(string cookieName)
{
bool retval = true;
try
{
HttpContext.Current.Response.Cookies[cookieName].Expires = DateTime.Now.AddDays(-365);
}
catch
{
retval = false;
}
return retval;
}
}
}
The class should be pretty much self-explanatory. To use this, we perform our set
operation as follows:
CpCookies.Set("Test2", t, DateTime.Now.AddDays(100)); // Where t is your type to be stored.
And to retrieve and use an existing cookie:
Test t2 = (Test)CpCookies.Get("Test2"); // "Test" is the sample class in the solution.
The Set method returns the size of your cookie content and throws an exception if
it is over 4095 bytes. However, it is better to err on the side of caution since
the maximum allowed cookie size in most browsers includes the cookie name and
expiration - everything in an HTTP cookie is stored as one big delimited string.
Here is the Silverlight version - it's a bit different since in Silverlight we
are using the HtmlPage.Document object:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Browser;
using System.IO;
using QuickLZSharp;
using Serialization;
namespace CompressedCookies
{
public static class CpCookies
{
private static object DeserializeBase64Decompress(string cookieName)
{
string[] cookies = HtmlPage.Document.Cookies.Split(';');
object o = null;
foreach (string cookie in cookies)
{
string[] keyValue = cookie.Split('=');
if (keyValue.Length == 2)
{
if (keyValue[0].ToString() == cookieName)
o= SilverlightSerializer.Deserialize(Convert.FromBase64String(keyValue[1]).Decompress());
break;
}
}
return o;
}
private static void SetCookie(string cookieName, string sCookieValue, DateTime expirationDate)
{
StringBuilder fullCookie = new StringBuilder();
fullCookie.Append(string.Concat(cookieName, "=", sCookieValue ));
fullCookie.Append(string.Concat(";expires=", expirationDate.ToString("R")));
HtmlPage.Document.SetProperty("cookie", fullCookie.ToString());
}
public static int Set(string cookieName, object cookieValue, DateTime expirationDate)
{
int siz =0;
try
{
string sCookieVal = SerializeAndCompressBase64(cookieValue);
siz = sCookieVal.Length;
if (siz > 4095)
throw new InvalidOperationException("Cookie value cannot exceed 4095 characters.");
SetCookie(cookieName ,sCookieVal ,expirationDate );
}
catch
{
throw ;
}
return siz;
}
private static string SerializeAndCompressBase64( object cookieValue)
{
return Convert.ToBase64String(SilverlightSerializer.Serialize(cookieValue).Compress());
}
public static object Get(string cookieName)
{
object retval = null;
try
{
retval = DeserializeBase64Decompress(cookieName);
}
catch
{
throw ;
}
return retval;
}
public static bool Delete(string cookieName)
{
bool retval = true;
try
{
SetCookie(cookieName,null,DateTime.Now.AddDays(-365));
}
catch
{
retval = false;
}
return retval;
}
}
}
You can download the ASP.NET compatible version here, and the Silverlight version here. When running the Silverlight version, just reload the page in the browser, and the
App.Xaml.cs class will change the RootVisual after running the app one time and
detecting that the "Test2" cookie has been set, so that it will instead
display the Xaml page that reads and shows the contents of the cookie that you
set:
private void Application_Startup(object sender, StartupEventArgs e)
{
if(HtmlPage.Document.Cookies.Contains("Test2") )
this.RootVisual = new Page2();
else
this.RootVisual = new MainPage();
}