IE6 Accept Header is Faulty and Makes format.any Suck May 14, 2008Posted by reidmix in AbstractRequest, ActionController, acts_as_authenticated, Changeset, Code, Example, MimeResponds, Monkey Patch, Plugins & Gems, Rails.
Tags: Accept Header, Any, Format, Format.any, HTTP_ACCEPT, ie, ie bug, ie6, ie6 bug, internet explorer, internet explorer 6
I’ve been using the awesome acts_as_authenticated plugin as the basis for user login and authorization. Its access_denied method takes advantage of the ActionController::MimeResponds.any method which will be invoked on a login_required before filter:
def access_denied respond_to do |format| format.html do store_location redirect_to new_session_path end format.any do request_http_basic_authentication 'Web Password' end end end
This code essentially says, if it’s an html request, store the original request and redirect to the login page, any other requests get the 401 Unauthorized header which the user agent can then use to initiate an HTTP Auth. This bit of code only works correctly with Edge Rail’s 8987 Changeset that allows any to act as a catch-all for any request mime-types not already specified.
image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
As you can see, it doesn’t specify application/xhtml+xml or even text/html and what’s worse is the subsequent requests to the same page sends the catch-all */*.
What does this all mean?
When IE6 navigates to a page that requires login, instead of redirecting the user to the login page, the user is presented with an HTTP Auth Dialog. What’s worse is that in my application, cookies and other application state for an web-based end-user is set outside of the login_from_basic_auth method — you know — in my login action.
My guess is when the format.any fix goes out in the next rails release, IE6 users on acts_as_authenticated sites are going to be sad-faced. I’m not entirely sure the correct way to fix this problem but this is how I solved it in my application:
class ActionController::AbstractRequest def accepts_with_faulty_header @env['HTTP_ACCEPT']='*/*' if @env['HTTP_USER_AGENT'] =~ /msie/i and @env['HTTP_USER_AGENT'] !~ /opera|webtv/i accepts_without_faulty_header end alias_method_chain :accepts, :faulty_header end
Originally, I thought to patch the Mime::Type.parse function, but decided that I wanted to make sure to look at the user agent for IE6. As you can see, I switch the HTTP_ACCEPT header to the catch-all */* string if the original HTTP_ACCEPT header is a match to the exact (bizarre) IE6 string and the HTTP_USER_AGENT is IE (matches MSIE but not Opera or WebTV). Then I call the original ActionController::AbstractRequest.accepts method.
I’d love to hear your ideas and suggestions — and if others have run into this problem as I didn’t find many users have this exact problem and how they may have solved it.
Update: Based on the conversation in the comments, I’ve re-written the patch to always change the HTTP_ACCEPT header to the catch-all */* if it is an IE Browser.