1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
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
|