149 lines
3.8 KiB
Ruby
149 lines
3.8 KiB
Ruby
|
module OpenID
|
||
|
|
||
|
module Yadis
|
||
|
|
||
|
# Generate an accept header value
|
||
|
#
|
||
|
# [str or (str, float)] -> str
|
||
|
def self.generate_accept_header(*elements)
|
||
|
parts = []
|
||
|
elements.each { |element|
|
||
|
if element.is_a?(String)
|
||
|
qs = "1.0"
|
||
|
mtype = element
|
||
|
else
|
||
|
mtype, q = element
|
||
|
q = q.to_f
|
||
|
if q > 1 or q <= 0
|
||
|
raise ArgumentError.new("Invalid preference factor: #{q}")
|
||
|
end
|
||
|
qs = sprintf("%0.1f", q)
|
||
|
end
|
||
|
|
||
|
parts << [qs, mtype]
|
||
|
}
|
||
|
|
||
|
parts.sort!
|
||
|
chunks = []
|
||
|
parts.each { |q, mtype|
|
||
|
if q == '1.0'
|
||
|
chunks << mtype
|
||
|
else
|
||
|
chunks << sprintf("%s; q=%s", mtype, q)
|
||
|
end
|
||
|
}
|
||
|
|
||
|
return chunks.join(', ')
|
||
|
end
|
||
|
|
||
|
def self.parse_accept_header(value)
|
||
|
# Parse an accept header, ignoring any accept-extensions
|
||
|
#
|
||
|
# returns a list of tuples containing main MIME type, MIME
|
||
|
# subtype, and quality markdown.
|
||
|
#
|
||
|
# str -> [(str, str, float)]
|
||
|
chunks = value.split(',', -1).collect { |v| v.strip }
|
||
|
accept = []
|
||
|
chunks.each { |chunk|
|
||
|
parts = chunk.split(";", -1).collect { |s| s.strip }
|
||
|
|
||
|
mtype = parts.shift
|
||
|
if mtype.index('/').nil?
|
||
|
# This is not a MIME type, so ignore the bad data
|
||
|
next
|
||
|
end
|
||
|
|
||
|
main, sub = mtype.split('/', 2)
|
||
|
|
||
|
q = nil
|
||
|
parts.each { |ext|
|
||
|
if !ext.index('=').nil?
|
||
|
k, v = ext.split('=', 2)
|
||
|
if k == 'q'
|
||
|
q = v.to_f
|
||
|
end
|
||
|
end
|
||
|
}
|
||
|
|
||
|
q = 1.0 if q.nil?
|
||
|
|
||
|
accept << [q, main, sub]
|
||
|
}
|
||
|
|
||
|
accept.sort!
|
||
|
accept.reverse!
|
||
|
|
||
|
return accept.collect { |q, main, sub| [main, sub, q] }
|
||
|
end
|
||
|
|
||
|
def self.match_types(accept_types, have_types)
|
||
|
# Given the result of parsing an Accept: header, and the
|
||
|
# available MIME types, return the acceptable types with their
|
||
|
# quality markdowns.
|
||
|
#
|
||
|
# For example:
|
||
|
#
|
||
|
# >>> acceptable = parse_accept_header('text/html, text/plain; q=0.5')
|
||
|
# >>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg'])
|
||
|
# [('text/html', 1.0), ('text/plain', 0.5)]
|
||
|
#
|
||
|
# Type signature: ([(str, str, float)], [str]) -> [(str, float)]
|
||
|
if accept_types.nil? or accept_types == []
|
||
|
# Accept all of them
|
||
|
default = 1
|
||
|
else
|
||
|
default = 0
|
||
|
end
|
||
|
|
||
|
match_main = {}
|
||
|
match_sub = {}
|
||
|
accept_types.each { |main, sub, q|
|
||
|
if main == '*'
|
||
|
default = [default, q].max
|
||
|
next
|
||
|
elsif sub == '*'
|
||
|
match_main[main] = [match_main.fetch(main, 0), q].max
|
||
|
else
|
||
|
match_sub[[main, sub]] = [match_sub.fetch([main, sub], 0), q].max
|
||
|
end
|
||
|
}
|
||
|
|
||
|
accepted_list = []
|
||
|
order_maintainer = 0
|
||
|
have_types.each { |mtype|
|
||
|
main, sub = mtype.split('/', 2)
|
||
|
if match_sub.member?([main, sub])
|
||
|
q = match_sub[[main, sub]]
|
||
|
else
|
||
|
q = match_main.fetch(main, default)
|
||
|
end
|
||
|
|
||
|
if q != 0
|
||
|
accepted_list << [1 - q, order_maintainer, q, mtype]
|
||
|
order_maintainer += 1
|
||
|
end
|
||
|
}
|
||
|
|
||
|
accepted_list.sort!
|
||
|
return accepted_list.collect { |_, _, q, mtype| [mtype, q] }
|
||
|
end
|
||
|
|
||
|
def self.get_acceptable(accept_header, have_types)
|
||
|
# Parse the accept header and return a list of available types
|
||
|
# in preferred order. If a type is unacceptable, it will not be
|
||
|
# in the resulting list.
|
||
|
#
|
||
|
# This is a convenience wrapper around matchTypes and
|
||
|
# parse_accept_header
|
||
|
#
|
||
|
# (str, [str]) -> [str]
|
||
|
accepted = self.parse_accept_header(accept_header)
|
||
|
preferred = self.match_types(accepted, have_types)
|
||
|
return preferred.collect { |mtype, _| mtype }
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
end
|