Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Exposing Accept header parameters.

  • Loading branch information...
commit b7c615c335ba4f522cc43e8ad1cc74b6dbec6ca7 1 parent 7721545
Pieter van de Bruggen pvande authored
Showing with 52 additions and 12 deletions.
  1. +37 −12 lib/sinatra/base.rb
  2. +15 −0 test/request_test.rb
49 lib/sinatra/base.rb
View
@@ -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 @@ def idempotent?
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 @@ def provides(*types)
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
15 test/request_test.rb
View
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.