aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG2
-rw-r--r--actionpack/lib/action_controller/mime_type.rb80
-rw-r--r--actionpack/test/controller/mime_type_test.rb24
3 files changed, 93 insertions, 13 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 014287b924..4024be84c1 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Make Mime::Type.parse consider q values (if any) [Jamis Buck]
+
* XML-formatted requests are typecast according to "type" attributes for :xml_simple [Jamis Buck]
* Added protection against proxy setups treating requests as local even when they're not #3898 [stephen_purcell@yahoo.com]
diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb
index 35095dc770..43c4b056f7 100644
--- a/actionpack/lib/action_controller/mime_type.rb
+++ b/actionpack/lib/action_controller/mime_type.rb
@@ -1,31 +1,85 @@
module Mime
class Type
+
+ # A simple helper class used in parsing the accept header
+ class AcceptItem #:nodoc:
+ attr_accessor :order, :name, :q
+
+ def initialize(order, name, q=nil)
+ @order = order
+ @name = name.strip
+ q ||= 0.0 if @name == "*/*" # default "*/*" to end of list
+ @q = ((q || 1.0).to_f * 100).to_i
+ end
+
+ def to_s
+ @name
+ end
+
+ def <=>(item)
+ result = item.q <=> q
+ result = order <=> item.order if result == 0
+ result
+ end
+
+ def ==(item)
+ name == (item.respond_to?(:name) ? item.name : item)
+ end
+ end
+
class << self
def lookup(string)
LOOKUP[string]
end
def parse(accept_header)
- mime_types = accept_header.split(",").collect! do |mime_type|
- mime_type.split(";").first.strip
+ # keep track of creation order to keep the subsequent sort stable
+ index = 0
+ list = accept_header.split(/,/).
+ map! { |i| AcceptItem.new(index += 1, *i.split(/;\s*q=/)) }.sort!
+
+ # Take care of the broken text/xml entry by renaming or deleting it
+
+ text_xml = list.index("text/xml")
+ app_xml = list.index("application/xml")
+
+ if text_xml && app_xml
+ # set the q value to the max of the two
+ list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
+
+ # make sure app_xml is ahead of text_xml in the list
+ if app_xml > text_xml
+ list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
+ app_xml, text_xml = text_xml, app_xml
+ end
+
+ # delete text_xml from the list
+ list.delete_at(text_xml)
+
+ elsif text_xml
+ list[text_xml].name = "application/xml"
end
- reorder_xml_types!(mime_types)
- mime_types.collect! { |mime_type| Mime::Type.lookup(mime_type) }
- end
-
- private
- def reorder_xml_types!(mime_types)
- mime_types.delete("text/xml") if mime_types.include?("application/xml")
+ # Look for more specific xml-based types and sort them ahead of app/xml
- if index_for_generic_xml = mime_types.index("application/xml")
- specific_xml_types = mime_types[index_for_generic_xml..-1].grep(/application\/[a-z]*\+xml/)
+ if app_xml
+ idx = app_xml
+ app_xml_type = list[app_xml]
- for specific_xml_type in specific_xml_types.reverse
- mime_types.insert(index_for_generic_xml, mime_types.delete(specific_xml_type))
+ while(idx < list.length)
+ type = list[idx]
+ break if type.q < app_xml_type.q
+ if type.name =~ /\+xml$/
+ list[app_xml], list[idx] = list[idx], list[app_xml]
+ app_xml = idx
end
+ idx += 1
end
end
+
+ list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
+ list
+ end
end
def initialize(string, symbol = nil, synonyms = [])
diff --git a/actionpack/test/controller/mime_type_test.rb b/actionpack/test/controller/mime_type_test.rb
new file mode 100644
index 0000000000..aa1d4459ee
--- /dev/null
+++ b/actionpack/test/controller/mime_type_test.rb
@@ -0,0 +1,24 @@
+require File.dirname(__FILE__) + '/../abstract_unit'
+
+class MimeTypeTest < Test::Unit::TestCase
+ Mime::PNG = Mime::Type.new("image/png")
+ Mime::PLAIN = Mime::Type.new("text/plain")
+
+ def test_parse_single
+ Mime::LOOKUP.keys.each do |mime_type|
+ assert_equal [Mime::Type.lookup(mime_type)], Mime::Type.parse(mime_type)
+ end
+ end
+
+ def test_parse_without_q
+ accept = "text/xml,application/xhtml+xml,text/yaml,application/xml,text/html,image/png,text/plain,*/*"
+ expect = [Mime::HTML, Mime::XML, Mime::YAML, Mime::PNG, Mime::PLAIN, Mime::ALL]
+ assert_equal expect, Mime::Type.parse(accept)
+ end
+
+ def test_parse_with_q
+ accept = "text/xml,application/xhtml+xml,text/yaml; q=0.3,application/xml,text/html; q=0.8,image/png,text/plain; q=0.5,*/*; q=0.2"
+ expect = [Mime::HTML, Mime::XML, Mime::PNG, Mime::PLAIN, Mime::YAML, Mime::ALL]
+ assert_equal expect, Mime::Type.parse(accept)
+ end
+end \ No newline at end of file