.NetCF HttpWebRequest and HttpWebResponse Have No Cookie Support

By Joshua Freeman at April 25, 2009 00:58
Filed Under: .Net Compact Framework

For space saving reasons some parts of the .Net Framework are left out of the .Net Compact Framework. Unfortunately, (at least for me), one of the things not making the cut is the cookie support methods for the HttpWebRequest and HttpWebResponse classes. What this means is developers are left to handle cookies in their own code.

Cookies play an important role in web applications and sometimes your application will be required to read and/or set them. One of my applications Chronobis falls into this category. In order to authenticate to OWA servers using FORM based authentication, you must accept, store, and send cookies. The full version of the .Net Framework handles this almost seamlessly for you. However, in the Compact Framework you are on your own.

I decided to write an encapsulated CookieManager class to solve this problem for Chronobis, which I am providing here as an example of how to handle cookies in your .NET Compact Framework application.

Warning: This code assumes that every cookie stored will be sent. This means that I effectively ignore the cookie attribute values (Comment, Domain, Max-Age, Path, Secure, and Version). If your situation warrants, you might need to extend this code to account for them.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;

namespace net.frejos.http
{
    public class CookieManager
    {
        private Dictionary cookieValues;

        public Dictionary CookieValues {
            get {
                if (this.cookieValues == null) {
                    this.cookieValues = new Dictionary();
                }

                return this.cookieValues;
            }
        }

        public void PublishCookies(HttpWebRequest webRequest)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("Cookie: ");
            foreach (string key in this.CookieValues.Keys) {
                sb.Append(key);
                sb.Append("=");
                sb.Append(this.CookieValues[key]);
                sb.Append("; ");
                sb.Append("$Path=\"/\"; ");               
            }

            webRequest.Headers.Add(sb.ToString());
            sb = null;
            webRequest = null;
        }

        public void StoreCookies(HttpWebResponse webResponse)
        {
            for (int x=0; x < webResponse.Headers.Count; x++) {
                if (webResponse.Headers.Keys[x].ToLower().Equals("set-cookie")) {
                    this.AddRawCookie( webResponse.Headers[x] );
                }
            }

            webResponse = null;
        }

        private void AddRawCookie(string rawCookieData)
        {
            string key = null;
            string value = null;

            string[] entries = null;

            if (rawCookieData.IndexOf(",") > 0)
            {
                entries = rawCookieData.Split(',');
            }
            else { 
                entries = new string[] { rawCookieData };
            }

            foreach (string entry in entries) {
                string cookieData = entry.Trim();

                if (cookieData.IndexOf(';') > 0)
                {
                    string[] temp = cookieData.Split(';');
                    cookieData = temp[0];
                }

                int index = cookieData.IndexOf('=');
                if (index > 0)
                {
                    key = cookieData.Substring(0, index);
                    value = cookieData.Substring(index + 1);
                }

                if (key != null && value != null)
                {
                    this.CookieValues[key] = value;
                }

                cookieData = null;
            }

            rawCookieData = null;
            entries = null;
            key = null;
            value = null;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("[");
            foreach (string key in this.CookieValues.Keys) {
                sb.Append("{");
                sb.Append(key);
                sb.Append(",");
                sb.Append(this.CookieValues[key]);
                sb.Append("}, ");
            }
            if (this.CookieValues.Keys.Count > 0)
            {
                sb.Remove(sb.Length - 2, 2);
            }
            sb.Append("]");

            return sb.ToString();
        }
    }
}

A typical usage of this class would look something like this:

CookieManager cookieManager = new CookieManager();
// Set a cookie value
cookieManager.CookieValues["FavoriteCookie"] = "Chocolate Chip";

HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
// Publish the cookies to the request before asking for the response
cookieManager.PublishCookies(webRequest);

HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
// Store any cookies returned from the response
cookieManager.StoreCookies(webResponse);

// Get the value of a cookie
string session = cookieManager.CookieValues["SESSIONID"];

webRequest = (HttpWebRequest)WebRequest.Create(url2);
cookieManager.PublishCookies(webRequest);

webResponse = (HttpWebResponse)webRequest.GetResponse();

Just in case you need a cookie RFC refresher, or need to add the missing functionality, you can find the RFC here.

The example source code contained within this post is provided under the terms of the MIT License. Copyright© 2008-2009 Joshua Freeman.

CookieManager.cs (3.39 kb)

About the Author

I'm Joshua Freeman a Senior Software Developer who has been developing software for the past 13 years, not counting all of those Comadore64 programs as a child. Developing software has always been a passion of mine. I've had the opportunity to use many different technologies over those years, including things like: HTML, JavaScript, Perl, ASP, JSP, SQL. The things that I use most often now are: JBoss, Tomcat, Java, .Net Framework, .Net Compact Framework, Windows Mobile, ASP.Net, JavaServer Faces to name a few.

I finally decided to start a blog in order to share and discuss some of the tools tips and other obscure findings that I've found over the years. So I present circumdev, which is a word that I've creaed that literally means "around development". (One of the great things about creating words is that their domains are usually still available.)

I hope you find the information that I post here useful in your software development journey, the way that I have found so many others helpful in mine.

Month List

Disclaimer

The opinions expressed within my blog entries, comments, and other content of this site are those of my own. They do not represent my employer's, or anyone else's views in anyway.