Exposing Accept header parameters.
This commit is contained in:
parent
7721545ff2
commit
b7c615c335
|
@ -16,16 +16,15 @@ module Sinatra
|
|||
# The request object. See Rack::Request for more info:
|
||||
# http://rack.rubyforge.org/doc/classes/Rack/Request.html
|
||||
class Request < Rack::Request
|
||||
HEADER_PARAM = /;[\d\w.]+=(?:[\d\w.]+|"(?:[^"\\]|\\.)*")?/
|
||||
HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+|\*))(?:#{HEADER_PARAM})*/
|
||||
HEADER_PARAM = /\s*[\d\w._]+=(?:[\d\w._]+|"(?:[^"\\]|\\.)*")?\s*/
|
||||
HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+|\*))\s*(?:;#{HEADER_PARAM})*/
|
||||
|
||||
# Returns an array of acceptable media types for the response
|
||||
def accept
|
||||
@env['sinatra.accept_entries'] ||= begin
|
||||
@env['sinatra.accept'] ||= begin
|
||||
entries = @env['HTTP_ACCEPT'].to_s.scan(HEADER_VALUE_WITH_PARAMS)
|
||||
entries.map { |e| accept_entry(e) }.sort_by(&:last)
|
||||
entries.map { |e| AcceptEntry.new(e) }.sort
|
||||
end
|
||||
@env['sinatra.accept'] ||= @env['sinatra.accept_entries'].map(&:first)
|
||||
end
|
||||
|
||||
def preferred_type(*types)
|
||||
|
@ -54,12 +53,37 @@ module Sinatra
|
|||
|
||||
private
|
||||
|
||||
def accept_entry(entry)
|
||||
type = entry.delete(' ').split(';').first
|
||||
options = entry.scan(HEADER_PARAM).map { |s| s[1..-1] }
|
||||
quality = 0 # we sort smallest first
|
||||
options.delete_if { |e| quality = 1 - e[2..-1].to_f if e.start_with? 'q=' }
|
||||
[type, options, [quality, type.count('*'), 1 - options.size]]
|
||||
class AcceptEntry
|
||||
attr_accessor :params
|
||||
|
||||
def initialize(entry)
|
||||
params = entry.scan(HEADER_PARAM).map do |s|
|
||||
key, value = s.strip.split('=', 2)
|
||||
value = value[1..-2].gsub(/\\(.)/, '\1') if value.start_with?('"')
|
||||
[key, value]
|
||||
end
|
||||
|
||||
@type = entry[/[^;]+/].delete(' ')
|
||||
@params = Hash[params]
|
||||
@q = @params.delete('q') { "1.0" }.to_f
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
other.priority <=> self.priority
|
||||
end
|
||||
|
||||
def priority
|
||||
# We sort in descending order; better matches should be higher.
|
||||
[ @q, -@type.count('*'), @params.size ]
|
||||
end
|
||||
|
||||
def [](param)
|
||||
@params[param]
|
||||
end
|
||||
|
||||
def to_str
|
||||
@type
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1293,7 +1317,8 @@ module Sinatra
|
|||
if type = response['Content-Type']
|
||||
types.include? type or types.include? type[/^[^;]+/]
|
||||
elsif type = request.preferred_type(types)
|
||||
content_type(type)
|
||||
params = (type.respond_to?(:params) ? type.params : {})
|
||||
content_type(type, params)
|
||||
true
|
||||
else
|
||||
false
|
||||
|
|
|
@ -42,4 +42,19 @@ class RequestTest < Test::Unit::TestCase
|
|||
dumped = Marshal.dump(request.params)
|
||||
assert_equal 'bar', Marshal.load(dumped)['foo']
|
||||
end
|
||||
|
||||
it "exposes the preferred type's parameters" do
|
||||
request = Sinatra::Request.new(
|
||||
'HTTP_ACCEPT' => 'image/jpeg; compress=0.25'
|
||||
)
|
||||
assert_equal({ 'compress' => '0.25' }, request.preferred_type.params)
|
||||
end
|
||||
|
||||
it "properly decodes MIME type parameters" do
|
||||
request = Sinatra::Request.new(
|
||||
'HTTP_ACCEPT' => 'image/jpeg;unquoted=0.25;quoted="0.25";chartest="\";,\x"'
|
||||
)
|
||||
expected = { 'unquoted' => '0.25', 'quoted' => '0.25', 'chartest' => '";,x' }
|
||||
assert_equal(expected, request.preferred_type.params)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue