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`.
#