lib/rack/linkeddata/conneg.rb in rack-linkeddata-1.0.0 vs lib/rack/linkeddata/conneg.rb in rack-linkeddata-1.1.0

- old
+ new

@@ -16,11 +16,11 @@ # use Rack::LinkedData::ContentNegotiation, :format => RDF::NTriples::Format # use Rack::LinkedData::ContentNegotiation, :default => 'application/rdf+xml' # # @see http://www4.wiwiss.fu-berlin.de/bizer/pub/LinkedDataTutorial/ class ContentNegotiation - DEFAULT_CONTENT_TYPE = "text/plain" # N-Triples + DEFAULT_CONTENT_TYPE = "application/n-triples" # N-Triples VARY = {'Vary' => 'Accept'}.freeze # @return [#call] attr_reader :app @@ -38,15 +38,19 @@ @options[:default] = (@options[:default] || DEFAULT_CONTENT_TYPE).to_s end ## # Handles a Rack protocol request. + # Parses Accept header to find appropriate mime-type and sets content_type accordingly. # + # Inserts ordered content types into the environment as `ORDERED_CONTENT_TYPES` if an Accept header is present + # # @param [Hash{String => String}] env # @return [Array(Integer, Hash, #each)] # @see http://rack.rubyforge.org/doc/SPEC.html def call(env) + env['ORDERED_CONTENT_TYPES'] = parse_accept_header(env['HTTP_ACCEPT']) if env.has_key?('HTTP_ACCEPT') response = app.call(env) body = response[2].respond_to?(:body) ? response[2].body : response[2] case body when RDF::Enumerable response[2] = body # Put it back in the response, it might have been a proxy @@ -134,25 +138,21 @@ # # @param [String, #to_s] header # @return [Array<String>] # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1 def parse_accept_header(header) - content_types = header.to_s.split(',').map do |content_type_and_weight| - content_type_and_weight.strip! - case content_type_and_weight - when /^([^;]+);\s*q=(\d+\.\d+)$/ - [[1.0, $2.to_f].min, $1, content_type_and_weight] - when /(\S+)/ - [1.0, $1, content_type_and_weight] - else nil - end - end - content_types.compact! # remove nils - content_types = content_types.sort_by { |elem| [elem[0], elem[2].size] } - content_types.reverse.map { |elem| elem[1] } + entries = header.to_s.split(',') + entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first) end + def accept_entry(entry) + type, *options = entry.delete(' ').split(';') + 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]] + end + ## # Outputs an HTTP `406 Not Acceptable` response. # # @param [String, #to_s] message # @return [Array(Integer, Hash, #each)] @@ -167,10 +167,10 @@ # @param [String, #to_s] message # @param [Hash{String => String}] headers # @return [Array(Integer, Hash, #each)] def http_error(code, message = nil, headers = {}) message = http_status(code) + (message.nil? ? "\n" : " (#{message})\n") - [code, {'Content-Type' => 'text/plain; charset=utf-8'}.merge(headers), [message]] + [code, {'Content-Type' => "#{DEFAULT_CONTENT_TYPE}; charset=utf-8"}.merge(headers), [message]] end ## # Returns the standard HTTP status message for the given status `code`. #