Sergey Maskalik

Sergey Maskalik's blog

In the pursuit of mastery

If you have been doing web development for some time I’m sure you have come across internet explorer caching feature. A less known redirect caching feature that was released in IE9 has just caused me a day of frustration and hopefully it will save you a day of nightmare.

From msdn article on caching improvements in IE9

Internet Explorer 9 now supports caching of HTTP redirect responses, as described by RFC 2616. Responses with Permanent Redirect status (301) are cacheable unless there are headers which forbid it (e.g. Cache-Control: no-cache) and those with Temporary Redirect status (302 or 307) are cacheable if there are headers which permit it (e.g. Cache-Control: max-age=120).

In that article it even warns developers that your “misconfigured” application might not work.

While this significantly improves performance, web applications that are misconfigured might not work as expected

So unless you are up to date on all msdn internet explorer blog posts you wouldn’t know that IE9 will cache your redirects unless you tell it not to. The problem with redirect caching is that if your server has some significant logic, like setting cookies on redirect it will no longer work in IE9, because the request will never be sent to the server.

What’s worse is that when you are trying to troubleshoot it present itself as bizarre behavior. You see redirect happening in the “Net” tab of developer tools but it never hits your server. Doesn’t hit any HttpModules or any part of asp.net request handling pipeline. And to add to insult Firefox works with no issues :) At one point you begin to imagine that maybe IE has some kind of a secret agreement with asp.net, but that’s just your brain looking for ridiculous ways to justify the problem.

So the rule of thumb: If you have a redirect that sets important cookies you will need to make sure that you tell IE not to cache the redirect. You can read more about caching headers or to fix this problem simply add following lines to your redirect code.

Response.Cache.SetNoStore();
Response.Cache.AppendCacheExtension("no-cache");

If you are using <httpCookies domain=".maskalik.com" /> element in your web.config you might think that all your cookies be default will have “.maskalik.com” domain set. I thought the same until I had to debug a weird problem where cookies were not being removed properly.

Let’s see if you can spot the problem, I certainly couldn’t at first. We are checking if cookie is already set and removing it if it is.

if (Request.Cookies["Color"] != null)
{
    HttpCookie colorCookie = Request.Cookies["Color"];
    colorCookie.Expires = DateTime.Now.AddDays(-1);
    Response.Cookies.Set(colorCookie);
}

By looking at the network tab in the internet explorer tools I can see that cookie that is being sent from the browser has a .maskalik.com domain. And server responds with expired cookie that has www.maskalik.com so the original cookie that browser sent does not match what the server sent therefore does not get removed. But how is that possible, all of our cookies should have domain specified by default. Apparently that’s not the case. If you look at the implementation of the System.Web.HttpCookie you can see that default values are only set when you create a cookie by using a constructor:

public HttpCookie(string name)
{
  this._name = name;
  **this.SetDefaultsFromConfig();**
  this._changed = true;
}

public HttpCookie(string name, string value)
{
  this._name = name;
  this._stringValue = value;
  **this.SetDefaultsFromConfig();**
  this._changed = true;
}

So when we read values from the Request.Cookies collection those values don’t have a domain set and we are expiring cookie with the different domain which doesn’t remove the original cookie.

So our fix would be pretty simple, instead of getting an existing cookie we create a new cookie with the same name that way the default domain is going to get properly set by HttpCookie constructor.

if (Request.Cookies["Color"] != null)
{
    HttpCookie colorCookie = new HttpCookie("Color");
    colorCookie.Expires = DateTime.Now.AddDays(-1);
    Response.Cookies.Set(colorCookie);
}

Lesson learned, hopefully it will help someone.

Immediate Function

Execute function as soon as it’s defined, variables declared inside have local function scope. Can return values or other functions.

var sayHello = (function() {
    var sometext = 'Hello world';
    return function() {
        return sometext;
    };
}());

Privacy, Module Pattern

Private variables can be hidden within the scope of a function, in the case below foo is a private variable.

var foo = (function() {
	//private
	var bar = "Hello I'm private";
	
	//public accessor
	return {
		getBar: function() {
			return bar;
		}
	}
}());

Since private variables will be recreated every time you can save memory by putting private variables into prototypes.

function Fast() {
}

Fast.prototype = (function() {
	//private 
	var fastFoo = "Hello I'm fast private Foo";
	//public prototype
	return {
		getFastFoo: function() {
			return fastFoo;
		}
	};
}());