jump to navigation

IE6 Accept Header is Faulty and Makes format.any Suck May 14, 2008

Posted by reidmix in AbstractRequest, ActionController, acts_as_authenticated, Changeset, Code, Example, MimeResponds, Monkey Patch, Plugins & Gems, Rails.
Tags: , , , , , , , , , ,
trackback

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.

Awesome!
Except when Internet Explorer 6 comes into the picture and happily sends this
strange and incomplete ‘Accept:’ header:

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.

HTTP Auth Prompt by IE6

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.

Advertisements

Comments»

1. EricLaw [MSFT] - May 14, 2008

Checking for an exact string is prone to failure, as there’s no guarantee that IE6 will send the Accept tokens in that order, and/or that all of these tokens will be present (e.g. the following application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword are only sent when those applications are installed).

You can not rely on the Accept header (in any version of IE) to determine whether or not the request represents a top-level navigation.

2. reidmix - May 14, 2008

Good to know, I wondered if certain versions of IE6 only sent those headers specifically.

So would you suggest I normalize all IE6 HTTP_ACCEPT headers to be the catch-all?

I’m still feeling unsure about normalizing the header to the catch-all rather than knowing what IE6’s true capabilities are via the Accept header.

3. EricLaw [MSFT] - May 14, 2008

I’m not entirely sure I understand the scenario of what you’re trying to do here (likely because I don’t know anything about Rails).

The Accept header in IE is one of two values: */*, or a string made up from the values of the “Accepted Documents” key in the system registry. That key is accessible to anything installed on the system, so some applications (e.g. Office) and add-ons (Flash/Silverlight, etc) shove their own values in there.

4. reidmix - May 14, 2008

AFAIK, rails will look at the Accept header and find the first match between the list and the types of files that can be delivered. Lets say there is an action that can respond (deliver) the follow formats:

def action
  respond_to do |format|
    format.html { # render html }
    format.xml  { # render xml }
    format.any  { # render json }
  end
end

If you have an Accept header that has text/html, application/xml, application/json, */*, the action will deliver html.

If you have an Accept header that has application/xml, text/html, application/json, */*, the action will deliver xml (first priority in the Accept list that matches).

If you have an Accept header that has text/plain, */* then the format.any will be selected and deliver json (impractical but good for my example).

And lastly if you just have */* as the Accept header, the action will deliver the first match in the action’s respond_to, in this case, html.

Because, IE is not specifying html, xhtml in it’s list but is specifying a bunch of other formats or just */*, then in the first case it will deliver text (format.any) and in the latter case it will deliver html (correctly).

If I cannot rely on the Accept header and the header does not list html or xhtml, then I would say if I know the user agent is MSIE I should normalize it’s Accept header to be the catch-all */* if it is not already.

5. DrMark - June 27, 2008

When I tried this on edge rails it didn’t fix the IE6 problem. I fixed it in the lib/authenticated_system.rb file. I did the following to fix it:

def access_denied
+ request.format = :html if request.env[‘HTTP_USER_AGENT’] =~ /msie/i && (request.format.to_s =~ /(text|html|xml|js|\*)/).nil?
respond_to do |format|
format.html do
store_location
redirect_to new_sessions_path
end
# format.any doesn’t work in rails version < http://dev.rubyonrails.org/changeset/8987
# you may want to change format.any to e.g. format.any(:js, :xml)
format.any do
request_http_basic_authentication ‘Web Password’
end
end
end

My copy of IE6 was sending a type through. It was “image/gif” and for some reason the above code didn’t fire and the http_auth dialog was displayed.

With my code, it works properly. What do you think?

Best of luck!

6. Recomand Mozilla Firefox | Daniel Matasaru - June 29, 2008

[…] cu primul post. Nu sunteti primii, toata lumea a observat ca Microsoft are probleme… ( vezi aici […]

7. Riopro Blog - » Rails e um bug chamado IE6 - September 29, 2008

[…] solução para esse problema encontramos  nesse link que está em inglês. Com base nessa solução, fizemos (na verdade, o Rodrigo fez) um initializer […]

8. reidmix - October 28, 2008

Looks like they turned of the Accept header in Rails 2.2:

The HTTP Accept header is disabled by default now. You should prefer the use of formatted URLs (such as /customers/1.xml) to indicate the format that you want. If you need the Accept headers, you can turn them back on with config.action_controller.user_accept_header = true.

http://guides.rubyonrails.org/2_2_release_notes.html section 6.3

9. dougmcinnes - April 2, 2009

The problem still occurs in Rails 2.3.2

If you change “ActionController::AbstractRequest” to “ActionController::Request” in the fix you will be good to go.

Rails 2.3 swapped out CGI requests for Rack requests and removed the “Abstract” part of the class name.

10. jQuery, jRails and the Accept header - Moserei - June 18, 2009

[…] browser compatibility I should favor the extension approach. And I’m not surprised it’s Microsoft’s […]

11. distributor minuman serbuk - September 10, 2014

Nice post. I learn something new and challenging on sites
I stumbleupon every day. It’s always interesting to read
articles from other writers and practice something from other web sites.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: