IE7 header issues
July 20th, 2008
I got to deploy a new build to TheWineSpies.com this weekend, which among many other things, upgraded the app to rails 2.1. This process had me refactoring the admin to be RESTfull-er, and incorporating the latest version of Fleximage. I thought everything was dandy.
The Problem
My client was doing some testing on this code with IE7, their browser of choice. But something odd was happening. They would click a link triggering a resources show action, and instead of a html page about that resource, they would just get the image. At first I was stumped. I never thought cross browser issues could change server behavior. Firefox got a page of HTML and IE7 got an image. And I confirmed that the server was actually sending different content to the different browsers. The truly bizarre part, was that after clicking a link to this page, you could refresh and get the content you were expecting.
What the hell?
The Code
The Wine Spies uses Fleximage enahnced models across a few different resources, but here is a simple action that was causing me grief.
1 2 3 4 5 6 7 8 9 10 |
def show @product = Product.find(params[:id]) respond_to do |format| format.html format.jpg { render :template => '/some/path/small.flexi' } format.xml { render :xml => @product.to_xml } end end |
This is pretty straightforward.
- If
htmlis requested, render the default template - If
jpgis requested, render some.flexitemplate - If
xmlis requested, dump the object to xml and send it out
But still, IE7 on a url of /products/123 would trigger the jpg format renderer. Somehow rails was thinking that IE7 wanted a jpeg image instead of HTML. Where other browser were making rails think they want HTML unless overridden by a format extension on the url.
Tracking down the cause
I began to think back to the DHH keynote “World of Resources” and some of the early stuff that was forming around rails, and luckily, I remembered something important. I tend to use this format pattern a lot, and I usually trigger it by url extensions like /products/123.jpg or /products/123.xml. But rails looks at something else too.
Your browser, or any HTTP client, sends along a header named HTTP_ACCEPT with any request. I decided to add some debug code to my action to see what was in this header:
raise request.headers['HTTP_ACCEPT'] |
In Firefox 3, the result was (minor edit for simplicity):
text/html,application/xhtml+xml,application/xml,*/* |
This is a comma separated list of MIME types. The purpose of this header is to let to receiver know what kind of data the client wants, or what it will “accept”. It’s a prioritized list where the first items are wanted more then the items at the end of the list. Firefox is saying “Please give me html. If you don’t have that give me xhtml. But I guess xml would be ok too if that’s all you got. You got none of those? Well give me whatever you got then, I don’t care.”
Ok what does IE7 send?
image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/ag-plugin, */* |
Until I refreshed the page, instead of clicking a link to this page, and got:
*/* |
What the hell?
IE7 doesn’t say it wants HTML at all! It says it would rather have a gif, bitmap, jpg, flash, or even a plugin, rather than any other MIME type, including html. Rails gets this header, and sees that is no explicit format on the URL, so it goes down the list:
- Does this action respond to the
image/gifformat? nope. - How about the
image/x-bitmapformat? no. - Maybe the
image/jpegformat? Oh look at that yes it does!
The item that would have matched the html renderer */* is too far down the list, and we don’t get to it before a suitable responder is found.
Since IE7 sends a different HTTP_ACCEPT of */* on a refresh, it is declaring it has no preferred format at all. Rails then renders the default: html.
The Solution
After trying many ways to capture the damn header, override it somehow, shoving some Mime::Type object into request.format, and other failed solutions, here is what I finally ended up with:
1 2 3 4 5 6 7 |
#application.rb before_filter :set_default_format def set_default_format params[:format] ||= 'html' end |
This basically tells rails to ignore the HTTP_ACCEPT header. If a format is not explicitly declared on the URL, then force it to be HTML. It’s as if every html page on the site has a .html appended to the URL.
And finally salvation from that circle of hell.
Conclusions
What makes a good HTTP_ACCEPT header? I think Firefox has it right. Being a web browser, its main job is to display HTML, with embedded media. So it should want HTML more than anything since the images are just there in support of the html (mostly anyway).
IE7 makes 2 mistakes:
- It sends different headers depending on how the page was loaded
- It states that is prefers images over HTML
HTTP headers are a powerful tool. They can make interfacing with web applications and API’s much cleaner. But as we depend on them increasingly, we need have a level of trust with the clients that people use to connect. IE7 said it wanted one thing, and I forced to create a hack in order to tell IE7 “No, you don’t really want that. You really want something else.” And that makes me sad.
I thought clients who don’t know what they want are pain, but now browsers have the same problem. IE7 is a huge improvement over IE6, however it obviously still has its issues for web developers. The simple fact is that both versions of this browser have cost me way too much time, and that means it cost me money. The number of hours I have spent troubleshooting issues like this, as well as CSS and javascript issues, is way more than I want to admit to.
And that makes cranky. So tell your parents, “Save a web developer. Switch to Firefox.” Then maybe the world will be a better place.
Leave a Reply