mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
Bringing Accept header parsing in line with the spec.
For reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
This commit is contained in:
parent
bac3f4f671
commit
43de6d2d8a
3 changed files with 62 additions and 8 deletions
|
@ -16,12 +16,16 @@ 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})*/
|
||||
|
||||
# Returns an array of acceptable media types for the response
|
||||
def accept
|
||||
@env['sinatra.accept'] ||= begin
|
||||
entries = @env['HTTP_ACCEPT'].to_s.split(',')
|
||||
entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
|
||||
@env['sinatra.accept_entries'] ||= begin
|
||||
entries = @env['HTTP_ACCEPT'].to_s.scan(HEADER_VALUE_WITH_PARAMS)
|
||||
entries.map { |e| accept_entry(e) }.sort_by(&:last)
|
||||
end
|
||||
@env['sinatra.accept'] ||= @env['sinatra.accept_entries'].map(&:first)
|
||||
end
|
||||
|
||||
def preferred_type(*types)
|
||||
|
@ -51,10 +55,11 @@ module Sinatra
|
|||
private
|
||||
|
||||
def accept_entry(entry)
|
||||
type, *options = entry.delete(' ').split(';')
|
||||
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, [quality, type.count('*'), 1 - options.size]]
|
||||
[type, options, [quality, type.count('*'), 1 - options.size]]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -431,7 +431,7 @@ class AfterFilterTest < Test::Unit::TestCase
|
|||
get('/') { @type }
|
||||
end
|
||||
|
||||
get('/', {}, { 'HTTP_ACCEPT' => '*' })
|
||||
get('/', {}, { 'HTTP_ACCEPT' => '*/*' })
|
||||
assert_body 'txt'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -779,7 +779,7 @@ class RoutingTest < Test::Unit::TestCase
|
|||
get('*', :provides => :html) { 'html' }
|
||||
end
|
||||
|
||||
get '/', {}, { 'HTTP_ACCEPT' => '*' }
|
||||
get '/', {}, { 'HTTP_ACCEPT' => '*/*' }
|
||||
assert ok?
|
||||
assert_equal 'text/plain;charset=utf-8', response.headers['Content-Type']
|
||||
assert_body 'txt'
|
||||
|
@ -826,7 +826,7 @@ class RoutingTest < Test::Unit::TestCase
|
|||
assert_equal 'default', body
|
||||
end
|
||||
|
||||
it 'respects user agent prefferences for the content type' do
|
||||
it 'respects user agent preferences for the content type' do
|
||||
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
||||
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,text/html;q=0.8' }
|
||||
assert_body 'text/html;charset=utf-8'
|
||||
|
@ -881,6 +881,55 @@ class RoutingTest < Test::Unit::TestCase
|
|||
assert_body 'text/html;charset=utf-8'
|
||||
end
|
||||
|
||||
it 'supplies a default quality of 1.0' do
|
||||
mock_app { get('/', :provides => [:png, :html]) { content_type }}
|
||||
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5, text/*' }
|
||||
assert_body 'text/html;charset=utf-8'
|
||||
end
|
||||
|
||||
it 'orders types with equal quality by parameter count' do
|
||||
mock_app do
|
||||
get('/', :provides => [:png, :jpg]) { content_type }
|
||||
end
|
||||
|
||||
lo_png = 'image/png;q=0.5'
|
||||
hi_png = 'image/png;q=0.5;profile=FOGRA40;gamma=0.8'
|
||||
jpeg = 'image/jpeg;q=0.5;compress=0.25'
|
||||
|
||||
get '/', {}, { 'HTTP_ACCEPT' => "#{lo_png}, #{jpeg}" }
|
||||
assert_body 'image/jpeg'
|
||||
get '/', {}, { 'HTTP_ACCEPT' => "#{hi_png}, #{jpeg}" }
|
||||
assert_body 'image/png'
|
||||
end
|
||||
|
||||
it 'ignores the quality parameter when ordering by parameter count' do
|
||||
mock_app do
|
||||
get('/', :provides => [:png, :jpg]) { content_type }
|
||||
end
|
||||
|
||||
lo_png = 'image/png'
|
||||
hi_png = 'image/png;profile=FOGRA40;gamma=0.8'
|
||||
jpeg = 'image/jpeg;q=1.0;compress=0.25'
|
||||
|
||||
get '/', {}, { 'HTTP_ACCEPT' => "#{jpeg}, #{lo_png}" }
|
||||
assert_body 'image/jpeg'
|
||||
get '/', {}, { 'HTTP_ACCEPT' => "#{jpeg}, #{hi_png}" }
|
||||
assert_body 'image/png'
|
||||
end
|
||||
|
||||
it 'properly handles quoted strings in parameters' do
|
||||
mock_app do
|
||||
get('/', :provides => [:png, :jpg]) { content_type }
|
||||
end
|
||||
|
||||
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5;profile=",image/jpeg,"' }
|
||||
assert_body 'image/png'
|
||||
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,image/jpeg;q=0;x=";q=1.0"' }
|
||||
assert_body 'image/png'
|
||||
get '/', {}, { 'HTTP_ACCEPT' => 'image/png;q=0.5,image/jpeg;q=0;x="\";q=1.0"' }
|
||||
assert_body 'image/png'
|
||||
end
|
||||
|
||||
it 'accepts both text/javascript and application/javascript for js' do
|
||||
mock_app { get('/', :provides => :js) { content_type }}
|
||||
get '/', {}, { 'HTTP_ACCEPT' => 'application/javascript' }
|
||||
|
|
Loading…
Add table
Reference in a new issue