Renamed content_negotation' as content_negotiation' (fixed typo)
Updated .ecf and Eiffel code depending on previous CONNEG
This commit is contained in:
4
library/network/protocol/content_negotiation/src/.gitignore
vendored
Normal file
4
library/network/protocol/content_negotiation/src/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*~
|
||||
EIFGEN* # ignore all files in the EIFGENs/ directory
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
note
|
||||
description: "Summary description for {CONNEG_SERVER_SIDE}. Utility class to support Server Side Content Negotiation "
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
description: "[
|
||||
Reference : http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.1
|
||||
Server-driven Negotiation : If the selection of the best representation for a response is made by an algorithm located at the server,
|
||||
it is called server-driven negotiation. Selection is based on the available representations of the response (the dimensions over which it can vary; e.g. language, content-coding, etc.)
|
||||
and the contents of particular header fields in the request message or on other information pertaining to the request (such as the network address of the client).
|
||||
Server-driven negotiation is advantageous when the algorithm for selecting from among the available representations is difficult to describe to the user agent,
|
||||
or when the server desires to send its "best guess" to the client along with the first response (hoping to avoid the round-trip delay of a subsequent request if the "best guess" is good enough for the user).
|
||||
In order to improve the server's guess, the user agent MAY include request header fields (Accept, Accept-Language, Accept-Encoding, etc.) which describe its preferences for such a response.
|
||||
]"
|
||||
EIS: "name=server driven negotiation", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html#sec12.", "protocol=uri"
|
||||
|
||||
class
|
||||
CONNEG_SERVER_SIDE
|
||||
|
||||
inherit
|
||||
|
||||
SHARED_CONNEG
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (a_mime: READABLE_STRING_8; a_language: READABLE_STRING_8; a_charset: READABLE_STRING_8; a_encoding: READABLE_STRING_8)
|
||||
do
|
||||
set_mime_default (a_mime)
|
||||
set_language_default (a_language)
|
||||
set_charset_default (a_charset)
|
||||
set_encoding_default (a_encoding)
|
||||
ensure
|
||||
mime_default_set: mime = a_mime
|
||||
language_default_set: language_default = a_language
|
||||
charset_default_set: charset_default = a_charset
|
||||
encoding_default_set: encoding_default = a_encoding
|
||||
end
|
||||
|
||||
feature -- AccessServer Side Defaults Formats
|
||||
|
||||
mime_default: READABLE_STRING_8
|
||||
-- Media type which is acceptable for the response.
|
||||
|
||||
language_default: READABLE_STRING_8
|
||||
-- Natural language that is preferred as a response to the request.
|
||||
|
||||
charset_default: READABLE_STRING_8
|
||||
-- Character set that is acceptable for the response.
|
||||
|
||||
encoding_default: READABLE_STRING_8
|
||||
-- Content-coding that is acceptable in the response.
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
set_mime_default (a_mime: READABLE_STRING_8)
|
||||
-- Set the mime_default with `a_mime'
|
||||
do
|
||||
mime_default := a_mime
|
||||
ensure
|
||||
mime_default_set: a_mime = mime_default
|
||||
end
|
||||
|
||||
set_language_default (a_language: READABLE_STRING_8)
|
||||
-- Set the language_default with `a_language'
|
||||
do
|
||||
language_default := a_language
|
||||
ensure
|
||||
language_default_set: a_language = language_default
|
||||
end
|
||||
|
||||
set_charset_default (a_charset: READABLE_STRING_8)
|
||||
-- Set the charset_default with `a_charset'
|
||||
do
|
||||
charset_default := a_charset
|
||||
ensure
|
||||
charset_default_set: a_charset = charset_default
|
||||
end
|
||||
|
||||
set_encoding_default (a_encoding: READABLE_STRING_8)
|
||||
do
|
||||
encoding_default := a_encoding
|
||||
ensure
|
||||
encoding_default_set: a_encoding = encoding_default
|
||||
end
|
||||
|
||||
|
||||
|
||||
feature -- Media Type Negotiation
|
||||
|
||||
media_type_preference (a_mime_types_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): MEDIA_TYPE_VARIANT_RESULTS
|
||||
-- `a_mime_types_supported' represent media types supported by the server.
|
||||
-- `a_header represent' the Accept header, ie, the client preferences.
|
||||
-- Return which media type to use for representation in a response, if the server supports
|
||||
-- the requested media type, or empty in other case.
|
||||
note
|
||||
EIS: "name=media type", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri"
|
||||
local
|
||||
l_mime_match: READABLE_STRING_8
|
||||
do
|
||||
create Result
|
||||
if a_header = Void or else a_header.is_empty then
|
||||
-- the request has no Accept header, ie the header is empty, in this case we use the default format
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_type (mime_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
l_mime_match := mime.best_match (a_mime_types_supported, a_header)
|
||||
if l_mime_match.is_empty then
|
||||
-- The server does not support any of the media types preferred by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (a_mime_types_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_type (l_mime_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Encoding Negotiation
|
||||
|
||||
charset_preference (a_server_charset_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
-- `a_server_charset_supported' represent a list of character sets supported by the server.
|
||||
-- `a_header' represents the Accept-Charset header, ie, the client preferences.
|
||||
-- Return which Charset to use in a response, if the server supports
|
||||
-- the requested Charset, or empty in other case.
|
||||
note
|
||||
EIS: "name=charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri"
|
||||
local
|
||||
l_charset_match: READABLE_STRING_8
|
||||
do
|
||||
create Result
|
||||
if a_header = Void or else a_header.is_empty then
|
||||
-- the request has no Accept-Charset header, ie the header is empty, in this case use default charset encoding
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_type (charset_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
l_charset_match := common.best_match (a_server_charset_supported, a_header)
|
||||
if l_charset_match.is_empty then
|
||||
-- The server does not support any of the compression types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (a_server_charset_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_type (l_charset_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Compression Negotiation
|
||||
|
||||
encoding_preference (a_server_encoding_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): COMPRESSION_VARIANT_RESULTS
|
||||
-- `a_server_encoding_supported' represent a list of encoding supported by the server.
|
||||
-- `a_header' represent the Accept-Encoding header, ie, the client preferences.
|
||||
-- Return which Encoding to use in a response, if the server supports
|
||||
-- the requested Encoding, or empty in other case.
|
||||
note
|
||||
EIS: "name=encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
|
||||
local
|
||||
l_compression_match: READABLE_STRING_8
|
||||
do
|
||||
create Result
|
||||
if a_header = Void or else a_header.is_empty then
|
||||
-- the request has no Accept-Encoding header, ie the header is empty, in this case do not compress representations
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_type (encoding_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
l_compression_match := common.best_match (a_server_encoding_supported, a_header)
|
||||
if l_compression_match.is_empty then
|
||||
-- The server does not support any of the compression types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (a_server_encoding_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_type (l_compression_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Language Negotiation
|
||||
|
||||
language_preference (a_server_language_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): LANGUAGE_VARIANT_RESULTS
|
||||
-- `a_server_language_supported' represent a list of languages supported by the server.
|
||||
-- `a_header' represent the Accept-Language header, ie, the client preferences.
|
||||
-- Return which Language to use in a response, if the server supports
|
||||
-- the requested Language, or empty in other case.
|
||||
note
|
||||
EIS: "name=language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri"
|
||||
|
||||
local
|
||||
l_language_match: READABLE_STRING_8
|
||||
do
|
||||
create Result
|
||||
if a_header = Void or else a_header.is_empty then
|
||||
-- the request has no Accept header, ie the header is empty, in this case we use the default format
|
||||
Result.set_acceptable (TRUE)
|
||||
Result.set_type (language_default)
|
||||
else
|
||||
-- select the best match, server support, client preferences
|
||||
l_language_match := language.best_match (a_server_language_supported, a_header)
|
||||
if l_language_match.is_empty then
|
||||
-- The server does not support any of the media types prefered by the client
|
||||
Result.set_acceptable (False)
|
||||
Result.set_supported_variants (a_server_language_supported)
|
||||
else
|
||||
-- Set the best match
|
||||
Result.set_type (l_language_match)
|
||||
Result.set_acceptable (True)
|
||||
Result.set_variant_header
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,259 @@
|
||||
note
|
||||
description: "[
|
||||
COMMON_ACCEPT_HEADER_PARSER, this class allows to parse Accept-Charset and Accept-Encoding headers
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=Charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri"
|
||||
EIS: "name=Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
|
||||
|
||||
class
|
||||
COMMON_ACCEPT_HEADER_PARSER
|
||||
|
||||
inherit {NONE}
|
||||
|
||||
MIME_TYPE_PARSER_UTILITIES
|
||||
|
||||
|
||||
feature -- Parser
|
||||
|
||||
parse_common (header: READABLE_STRING_8): COMMON_RESULTS
|
||||
-- Parses `header' charset/encoding into its component parts.
|
||||
-- For example, the charset 'iso-8889-5' would get parsed
|
||||
-- into:
|
||||
-- ('iso-8889-5', {'q':'1.0'})
|
||||
local
|
||||
l_parts: LIST [READABLE_STRING_8]
|
||||
sub_parts: LIST [READABLE_STRING_8]
|
||||
p: READABLE_STRING_8
|
||||
i: INTEGER
|
||||
l_header: READABLE_STRING_8
|
||||
do
|
||||
create Result.make
|
||||
l_parts := header.split (';')
|
||||
if l_parts.count = 1 then
|
||||
Result.put ("1.0", "q")
|
||||
else
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > l_parts.count
|
||||
loop
|
||||
p := l_parts.at (i)
|
||||
sub_parts := p.split ('=')
|
||||
if sub_parts.count = 2 then
|
||||
Result.put (trim (sub_parts [2]), trim (sub_parts [1]))
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
l_header := trim (l_parts [1])
|
||||
Result.set_field (trim (l_header))
|
||||
end
|
||||
|
||||
fitness_and_quality_parsed (a_field: READABLE_STRING_8; parsed_charsets: LIST [COMMON_RESULTS]): FITNESS_AND_QUALITY
|
||||
-- Find the best match for a given charset/encoding against a list of charsets/encodings
|
||||
-- that have already been parsed by parse_common. Returns a
|
||||
-- tuple of the fitness value and the value of the 'q' quality parameter of
|
||||
-- the best match, or (-1, 0) if no match was found. Just as for
|
||||
-- quality_parsed().
|
||||
local
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: COMMON_RESULTS
|
||||
range: COMMON_RESULTS
|
||||
element: detachable READABLE_STRING_8
|
||||
l_fitness: INTEGER
|
||||
do
|
||||
best_fitness := -1
|
||||
best_fit_q := 0.0
|
||||
target := parse_common (a_field)
|
||||
if attached target.item ("q") as q and then q.is_double then
|
||||
target_q := q.to_double
|
||||
if target_q < 0.0 then
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
if attached target.field as l_target_field then
|
||||
from
|
||||
parsed_charsets.start
|
||||
until
|
||||
parsed_charsets.after
|
||||
loop
|
||||
range := parsed_charsets.item_for_iteration
|
||||
if attached range.field as l_range_common then
|
||||
if l_target_field.same_string (l_range_common) or l_target_field.same_string ("*") or l_range_common.same_string ("*") then
|
||||
if l_range_common.same_string (l_target_field) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.item ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
parsed_charsets.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
quality_parsed (a_field: READABLE_STRING_8; parsed_common: LIST [COMMON_RESULTS]): REAL_64
|
||||
-- Find the best match for a given charset/encoding against a list of charsets/encodings that
|
||||
-- have already been parsed by parse_charsets(). Returns the 'q' quality
|
||||
-- parameter of the best match, 0 if no match was found. This function
|
||||
-- bahaves the same as quality()
|
||||
do
|
||||
Result := fitness_and_quality_parsed (a_field, parsed_common).quality
|
||||
end
|
||||
|
||||
quality (a_field: READABLE_STRING_8; commons: READABLE_STRING_8): REAL_64
|
||||
-- Returns the quality 'q' of a charset/encoding when compared against the
|
||||
-- a list of charsets/encodings/
|
||||
local
|
||||
l_commons: LIST [READABLE_STRING_8]
|
||||
res: ARRAYED_LIST [COMMON_RESULTS]
|
||||
p_res: COMMON_RESULTS
|
||||
do
|
||||
l_commons := commons.split (',')
|
||||
from
|
||||
create res.make (10);
|
||||
l_commons.start
|
||||
until
|
||||
l_commons.after
|
||||
loop
|
||||
p_res := parse_common (l_commons.item_for_iteration)
|
||||
res.put_left (p_res)
|
||||
l_commons.forth
|
||||
end
|
||||
Result := quality_parsed (a_field, res)
|
||||
end
|
||||
|
||||
best_match (supported: LIST [READABLE_STRING_8]; header: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- Choose the accept with the highest fitness score and quality ('q') from a list of candidates.
|
||||
local
|
||||
l_header_results: LIST [COMMON_RESULTS]
|
||||
weighted_matches: LIST [FITNESS_AND_QUALITY]
|
||||
l_res: LIST [READABLE_STRING_8]
|
||||
p_res: COMMON_RESULTS
|
||||
fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
|
||||
do
|
||||
l_res := header.split (',')
|
||||
create {ARRAYED_LIST [COMMON_RESULTS]} l_header_results.make (l_res.count)
|
||||
from
|
||||
l_res.start
|
||||
until
|
||||
l_res.after
|
||||
loop
|
||||
p_res := parse_common (l_res.item_for_iteration)
|
||||
l_header_results.force (p_res)
|
||||
l_res.forth
|
||||
end
|
||||
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count)
|
||||
from
|
||||
supported.start
|
||||
until
|
||||
supported.after
|
||||
loop
|
||||
fitness_and_quality := fitness_and_quality_parsed (supported.item_for_iteration, l_header_results)
|
||||
fitness_and_quality.set_mime_type (mime_type (supported.item_for_iteration))
|
||||
weighted_matches.force (fitness_and_quality)
|
||||
supported.forth
|
||||
end
|
||||
|
||||
--| Keep only top quality+fitness types
|
||||
--| TODO extract method
|
||||
from
|
||||
weighted_matches.start
|
||||
first_one := weighted_matches.item
|
||||
weighted_matches.forth
|
||||
until
|
||||
weighted_matches.after
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if first_one < fitness_and_quality then
|
||||
first_one := fitness_and_quality
|
||||
if not weighted_matches.isfirst then
|
||||
from
|
||||
weighted_matches.back
|
||||
until
|
||||
weighted_matches.before
|
||||
loop
|
||||
weighted_matches.remove
|
||||
weighted_matches.back
|
||||
end
|
||||
weighted_matches.forth
|
||||
end
|
||||
check
|
||||
weighted_matches.item = fitness_and_quality
|
||||
end
|
||||
weighted_matches.forth
|
||||
elseif first_one.is_equal (fitness_and_quality) then
|
||||
weighted_matches.forth
|
||||
else
|
||||
check
|
||||
first_one > fitness_and_quality
|
||||
end
|
||||
weighted_matches.remove
|
||||
end
|
||||
end
|
||||
if first_one /= Void and then first_one.quality /= 0.0 then
|
||||
if weighted_matches.count = 1 then
|
||||
Result := first_one.mime_type
|
||||
else
|
||||
from
|
||||
fitness_and_quality := Void
|
||||
l_header_results.start
|
||||
until
|
||||
l_header_results.after or fitness_and_quality /= Void
|
||||
loop
|
||||
if attached l_header_results.item.field as l_field then
|
||||
from
|
||||
weighted_matches.start
|
||||
until
|
||||
weighted_matches.after or fitness_and_quality /= Void
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if fitness_and_quality.mime_type.same_string (l_field) then
|
||||
--| Found
|
||||
else
|
||||
fitness_and_quality := Void
|
||||
weighted_matches.forth
|
||||
end
|
||||
end
|
||||
else
|
||||
check
|
||||
has_field: False
|
||||
end
|
||||
end
|
||||
l_header_results.forth
|
||||
end
|
||||
if fitness_and_quality /= Void then
|
||||
Result := fitness_and_quality.mime_type
|
||||
else
|
||||
Result := first_one.mime_type
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,81 @@
|
||||
note
|
||||
description: "Summary description for {FITNESS_AND_QUALITY}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FITNESS_AND_QUALITY
|
||||
|
||||
inherit
|
||||
COMPARABLE
|
||||
|
||||
DEBUG_OUTPUT
|
||||
undefine
|
||||
is_equal
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (a_fitness: INTEGER; a_quality: REAL_64)
|
||||
do
|
||||
fitness := a_fitness
|
||||
quality := a_quality
|
||||
create mime_type.make_empty
|
||||
ensure
|
||||
fitness_assigned : fitness = a_fitness
|
||||
quality_assigned : quality = a_quality
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
fitness: INTEGER
|
||||
|
||||
quality: REAL_64
|
||||
|
||||
mime_type: STRING
|
||||
-- optionally used
|
||||
-- empty by default
|
||||
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
create Result.make_from_string (mime_type)
|
||||
Result.append (" (")
|
||||
Result.append ("quality=" + quality.out)
|
||||
Result.append (" ; fitness=" + fitness.out)
|
||||
Result.append (" )")
|
||||
end
|
||||
|
||||
feature -- Element Change
|
||||
|
||||
set_mime_type (a_mime_type: STRING)
|
||||
-- set mime_type with `a_mime_type'
|
||||
do
|
||||
mime_type := a_mime_type
|
||||
ensure
|
||||
mime_type_assigned : mime_type.same_string (a_mime_type)
|
||||
end
|
||||
|
||||
feature -- Comparision
|
||||
|
||||
is_less alias "<" (other: like Current): BOOLEAN
|
||||
-- Is current object less than `other'?
|
||||
do
|
||||
if fitness = other.fitness then
|
||||
Result := quality < other.quality
|
||||
else
|
||||
Result := fitness < other.fitness
|
||||
end
|
||||
end
|
||||
note
|
||||
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
note
|
||||
description: "[
|
||||
{LANGUAGE_PARSE} is encharge to parse language tags defined as follow:
|
||||
|
||||
Accept-Language = "Accept-Language" ":"
|
||||
1#( language-range [ ";" "q" "=" qvalue ] )
|
||||
language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
|
||||
|
||||
Example:
|
||||
Accept-Language: da, en-gb;q=0.8, en;q=0.7
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=Accept-Language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri"
|
||||
|
||||
class
|
||||
LANGUAGE_PARSE
|
||||
|
||||
inherit {NONE}
|
||||
|
||||
MIME_TYPE_PARSER_UTILITIES
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature -- Parser
|
||||
|
||||
parse_language (a_accept_language: READABLE_STRING_8): LANGUAGE_RESULTS
|
||||
-- Parses `a_accept_language' request-header field into its component parts.
|
||||
-- For example, the language range 'en-gb;q=0.8' would get parsed
|
||||
-- into:
|
||||
-- ('en-gb', {'q':'0.8',})
|
||||
local
|
||||
l_parts: LIST [READABLE_STRING_8]
|
||||
p: READABLE_STRING_8
|
||||
sub_parts: LIST [READABLE_STRING_8]
|
||||
i: INTEGER
|
||||
l_full_type: READABLE_STRING_8
|
||||
l_types: LIST [READABLE_STRING_8]
|
||||
do
|
||||
fixme ("Improve code!!!")
|
||||
create Result.make
|
||||
l_parts := a_accept_language.split (';')
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > l_parts.count
|
||||
loop
|
||||
p := l_parts.at (i)
|
||||
sub_parts := p.split ('=')
|
||||
if sub_parts.count = 2 then
|
||||
Result.put (trim (sub_parts [2]), trim (sub_parts [1]))
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
|
||||
l_full_type := trim (l_parts [1])
|
||||
if l_full_type.same_string ("*") then
|
||||
l_full_type := "*"
|
||||
end
|
||||
l_types := l_full_type.split ('-')
|
||||
if l_types.count = 1 then
|
||||
Result.set_type (trim (l_types [1]))
|
||||
else
|
||||
Result.set_type (trim (l_types [1]))
|
||||
Result.set_sub_type (trim (l_types [2]))
|
||||
end
|
||||
end
|
||||
|
||||
parse_language_range (a_language_range: READABLE_STRING_8): LANGUAGE_RESULTS
|
||||
-- Languages-ranges are languages with wild-cards and a 'q' quality parameter.
|
||||
-- For example, the language range ('en-* ;q=0.5') would get parsed into:
|
||||
-- ('en', '*', {'q', '0.5'})
|
||||
-- In addition this function also guarantees that there is a value for 'q'
|
||||
-- in the params dictionary, filling it in with a proper default if
|
||||
-- necessary.
|
||||
do
|
||||
fixme ("Improve the code!!!")
|
||||
Result := parse_language (a_language_range)
|
||||
if attached Result.item ("q") as q then
|
||||
if q.is_double and then attached {REAL_64} q.to_double as r and then (r >= 0.0 and r <= 1.0) then
|
||||
--| Keep current value
|
||||
if q.same_string ("1") then
|
||||
--| Use 1.0 formatting
|
||||
Result.put ("1.0", "q")
|
||||
end
|
||||
else
|
||||
Result.put ("1.0", "q")
|
||||
end
|
||||
else
|
||||
Result.put ("1.0", "q")
|
||||
end
|
||||
end
|
||||
|
||||
fitness_and_quality_parsed (a_language: READABLE_STRING_8; a_parsed_ranges: LIST [LANGUAGE_RESULTS]): FITNESS_AND_QUALITY
|
||||
-- Find the best match for a given `a_language' against a list of language ranges `a_parsed_ranges'
|
||||
-- that have already been parsed by parse_language_range.
|
||||
local
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: LANGUAGE_RESULTS
|
||||
range: LANGUAGE_RESULTS
|
||||
keys: LIST [READABLE_STRING_8]
|
||||
param_matches: INTEGER
|
||||
element: detachable READABLE_STRING_8
|
||||
l_fitness: INTEGER
|
||||
do
|
||||
best_fitness := -1
|
||||
best_fit_q := 0.0
|
||||
target := parse_language_range (a_language)
|
||||
if attached target.item ("q") as q and then q.is_double then
|
||||
target_q := q.to_double
|
||||
if target_q < 0.0 then
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
if attached target.type as l_target_type then
|
||||
from
|
||||
a_parsed_ranges.start
|
||||
until
|
||||
a_parsed_ranges.after
|
||||
loop
|
||||
range := a_parsed_ranges.item_for_iteration
|
||||
if (attached range.type as l_range_type and then (l_target_type.same_string (l_range_type) or l_range_type.same_string ("*") or l_target_type.same_string ("*"))) then
|
||||
from
|
||||
param_matches := 0
|
||||
keys := target.keys
|
||||
keys.start
|
||||
until
|
||||
keys.after
|
||||
loop
|
||||
element := keys.item_for_iteration
|
||||
if not element.same_string ("q") and then range.has_key (element) and then (attached target.item (element) as t_item and attached range.item (element) as r_item) and then t_item.same_string (r_item) then
|
||||
param_matches := param_matches + 1
|
||||
end
|
||||
keys.forth
|
||||
end
|
||||
if l_range_type.same_string (l_target_type) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
if (attached range.sub_type as l_range_sub_type and then attached target.sub_type as l_target_sub_type and then (l_target_sub_type.same_string (l_range_sub_type) or l_range_sub_type.same_string ("*") or l_target_sub_type.same_string ("*"))) then
|
||||
if l_range_sub_type.same_string (l_target_sub_type) then
|
||||
l_fitness := l_fitness + 10
|
||||
end
|
||||
end
|
||||
l_fitness := l_fitness + param_matches
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.item ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
a_parsed_ranges.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
quality_parsed (a_language: READABLE_STRING_8; a_parsed_ranges: LIST [LANGUAGE_RESULTS]): REAL_64
|
||||
-- Find the best match for a given `a_language' against a list of ranges `parsed_ranges' that
|
||||
-- have already been parsed by parse_language_range. Returns the 'q' quality
|
||||
-- parameter of the best match, 0 if no match was found. This function
|
||||
-- bahaves the same as quality except that 'a_parsed_ranges' must be a list
|
||||
-- of parsed language ranges.
|
||||
do
|
||||
Result := fitness_and_quality_parsed (a_language, a_parsed_ranges).quality
|
||||
end
|
||||
|
||||
quality (a_language: READABLE_STRING_8; a_ranges: READABLE_STRING_8): REAL_64
|
||||
-- Returns the quality 'q' of a `a_language' when compared against the
|
||||
-- language range in `a_ranges'.
|
||||
local
|
||||
l_ranges: LIST [READABLE_STRING_8]
|
||||
res: ARRAYED_LIST [LANGUAGE_RESULTS]
|
||||
p_res: LANGUAGE_RESULTS
|
||||
do
|
||||
l_ranges := a_ranges.split (',')
|
||||
from
|
||||
create res.make (10);
|
||||
l_ranges.start
|
||||
until
|
||||
l_ranges.after
|
||||
loop
|
||||
p_res := parse_language_range (l_ranges.item_for_iteration)
|
||||
res.put_left (p_res)
|
||||
l_ranges.forth
|
||||
end
|
||||
Result := quality_parsed (a_language, res)
|
||||
end
|
||||
|
||||
best_match (a_supported: LIST [READABLE_STRING_8]; a_header: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- Choose the `language' with the highest fitness score and quality ('q') from a list of candidates.
|
||||
local
|
||||
l_header_results: LIST [LANGUAGE_RESULTS]
|
||||
weighted_matches: LIST [FITNESS_AND_QUALITY]
|
||||
l_res: LIST [READABLE_STRING_8]
|
||||
p_res: LANGUAGE_RESULTS
|
||||
fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
l_res := a_header.split (',')
|
||||
create {ARRAYED_LIST [LANGUAGE_RESULTS]} l_header_results.make (l_res.count)
|
||||
fixme ("Extract method!!!")
|
||||
from
|
||||
l_res.start
|
||||
until
|
||||
l_res.after
|
||||
loop
|
||||
p_res := parse_language_range (l_res.item_for_iteration)
|
||||
l_header_results.force (p_res)
|
||||
l_res.forth
|
||||
end
|
||||
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (a_supported.count)
|
||||
from
|
||||
a_supported.start
|
||||
until
|
||||
a_supported.after
|
||||
loop
|
||||
fitness_and_quality := fitness_and_quality_parsed (a_supported.item_for_iteration, l_header_results)
|
||||
fitness_and_quality.set_mime_type (mime_type (a_supported.item_for_iteration))
|
||||
weighted_matches.force (fitness_and_quality)
|
||||
a_supported.forth
|
||||
end
|
||||
|
||||
--| Keep only top quality+fitness types
|
||||
from
|
||||
weighted_matches.start
|
||||
first_one := weighted_matches.item
|
||||
weighted_matches.forth
|
||||
until
|
||||
weighted_matches.after
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if first_one < fitness_and_quality then
|
||||
first_one := fitness_and_quality
|
||||
if not weighted_matches.isfirst then
|
||||
from
|
||||
weighted_matches.back
|
||||
until
|
||||
weighted_matches.before
|
||||
loop
|
||||
weighted_matches.remove
|
||||
weighted_matches.back
|
||||
end
|
||||
weighted_matches.forth
|
||||
end
|
||||
check
|
||||
weighted_matches.item = fitness_and_quality
|
||||
end
|
||||
weighted_matches.forth
|
||||
elseif first_one.is_equal (fitness_and_quality) then
|
||||
weighted_matches.forth
|
||||
else
|
||||
check
|
||||
first_one > fitness_and_quality
|
||||
end
|
||||
weighted_matches.remove
|
||||
end
|
||||
end
|
||||
if first_one /= Void and then first_one.quality /= 0.0 then
|
||||
if weighted_matches.count = 1 then
|
||||
Result := first_one.mime_type
|
||||
else
|
||||
from
|
||||
fitness_and_quality := Void
|
||||
l_header_results.start
|
||||
until
|
||||
l_header_results.after or fitness_and_quality /= Void
|
||||
loop
|
||||
s := l_header_results.item.mime_type
|
||||
from
|
||||
weighted_matches.start
|
||||
until
|
||||
weighted_matches.after or fitness_and_quality /= Void
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if fitness_and_quality.mime_type.same_string (s) then
|
||||
--| Found
|
||||
else
|
||||
fitness_and_quality := Void
|
||||
weighted_matches.forth
|
||||
end
|
||||
end
|
||||
l_header_results.forth
|
||||
end
|
||||
if fitness_and_quality /= Void then
|
||||
Result := fitness_and_quality.mime_type
|
||||
else
|
||||
Result := first_one.mime_type
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,306 @@
|
||||
note
|
||||
description: "[
|
||||
{MIME_PARSE}. is encharge to parse Accept request-header field defined as follow:
|
||||
|
||||
Accept = "Accept" ":"
|
||||
#( media-range [ accept-params ] )
|
||||
media-range = ( "*/*"
|
||||
| ( type "/" "*" )
|
||||
| ( type "/" subtype )
|
||||
) *( ";" parameter )
|
||||
accept-params = ";" "q" "=" qvalue *( accept-extension )
|
||||
accept-extension = ";" token [ "=" ( token | quoted-string ) ]
|
||||
|
||||
Example:
|
||||
|
||||
Accept: text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=Accept", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri"
|
||||
|
||||
class
|
||||
MIME_PARSE
|
||||
|
||||
inherit {NONE}
|
||||
|
||||
MIME_TYPE_PARSER_UTILITIES
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature -- Parser
|
||||
|
||||
parse_mime_type (a_mime_type: READABLE_STRING_8): HTTP_MEDIA_TYPE
|
||||
-- Parses a mime-type into its component parts.
|
||||
-- For example, the media range 'application/xhtml;q=0.5' would get parsed
|
||||
-- into:
|
||||
-- ('application', 'xhtml', {'q', '0.5'})
|
||||
do
|
||||
create Result.make_from_string (a_mime_type)
|
||||
end
|
||||
|
||||
parse_media_range (a_range: READABLE_STRING_8): HTTP_MEDIA_TYPE
|
||||
-- Media-ranges are mime-types with wild-cards and a 'q' quality parameter.
|
||||
-- For example, the media range 'application/*;q=0.5' would get parsed into:
|
||||
-- ('application', '*', {'q', '0.5'})
|
||||
-- In addition this function also guarantees that there is a value for 'q'
|
||||
-- in the params dictionary, filling it in with a proper default if
|
||||
-- necessary.
|
||||
do
|
||||
fixme ("Improve the code!!!")
|
||||
Result := parse_mime_type (a_range)
|
||||
if attached Result.parameter ("q") as q then
|
||||
if
|
||||
q.is_double and then
|
||||
attached {REAL_64} q.to_double as r and then
|
||||
(r >= 0.0 and r <= 1.0)
|
||||
then
|
||||
--| Keep current value
|
||||
if q.same_string ("1") then
|
||||
--| Use 1.0 formatting
|
||||
Result.add_parameter ("q", "1.0")
|
||||
end
|
||||
else
|
||||
Result.add_parameter ("q", "1.0")
|
||||
end
|
||||
else
|
||||
Result.add_parameter ("q", "1.0")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
fitness_and_quality_parsed (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): FITNESS_AND_QUALITY
|
||||
-- Find the best match for a given mimeType against a list of media_ranges
|
||||
-- that have already been parsed by parse_media_range.
|
||||
local
|
||||
best_fitness: INTEGER
|
||||
target_q: REAL_64
|
||||
best_fit_q: REAL_64
|
||||
target: HTTP_MEDIA_TYPE
|
||||
range: HTTP_MEDIA_TYPE
|
||||
param_matches: INTEGER
|
||||
element: detachable READABLE_STRING_8
|
||||
l_fitness: INTEGER
|
||||
do
|
||||
best_fitness := -1
|
||||
best_fit_q := 0.0
|
||||
target := parse_media_range (a_mime_type)
|
||||
if attached target.parameter ("q") as q and then q.is_double then
|
||||
target_q := q.to_double
|
||||
if target_q < 0.0 then
|
||||
target_q := 0.0
|
||||
elseif target_q > 1.0 then
|
||||
target_q := 1.0
|
||||
end
|
||||
else
|
||||
target_q := 1.0
|
||||
end
|
||||
|
||||
if
|
||||
attached target.type as l_target_type and
|
||||
attached target.subtype as l_target_sub_type
|
||||
then
|
||||
from
|
||||
parsed_ranges.start
|
||||
until
|
||||
parsed_ranges.after
|
||||
loop
|
||||
range := parsed_ranges.item_for_iteration
|
||||
if
|
||||
(
|
||||
attached range.type as l_range_type and then
|
||||
(l_target_type.same_string (l_range_type) or l_range_type.same_string ("*") or l_target_type.same_string ("*"))
|
||||
) and
|
||||
(
|
||||
attached range.subtype as l_range_sub_type and then
|
||||
(l_target_sub_type.same_string (l_range_sub_type) or l_range_sub_type.same_string ("*") or l_target_sub_type.same_string ("*"))
|
||||
)
|
||||
then
|
||||
if attached target.parameters as l_keys then
|
||||
from
|
||||
param_matches := 0
|
||||
l_keys.start
|
||||
until
|
||||
l_keys.after
|
||||
loop
|
||||
element := l_keys.key_for_iteration
|
||||
if
|
||||
not element.same_string ("q") and then
|
||||
range.has_parameter (element) and then
|
||||
(attached target.parameter (element) as t_item and attached range.parameter (element) as r_item) and then
|
||||
t_item.same_string (r_item)
|
||||
then
|
||||
param_matches := param_matches + 1
|
||||
end
|
||||
l_keys.forth
|
||||
end
|
||||
end
|
||||
if l_range_type.same_string (l_target_type) then
|
||||
l_fitness := 100
|
||||
else
|
||||
l_fitness := 0
|
||||
end
|
||||
|
||||
if l_range_sub_type.same_string (l_target_sub_type) then
|
||||
l_fitness := l_fitness + 10
|
||||
end
|
||||
|
||||
l_fitness := l_fitness + param_matches
|
||||
|
||||
if l_fitness > best_fitness then
|
||||
best_fitness := l_fitness
|
||||
element := range.parameter ("q")
|
||||
if element /= Void then
|
||||
best_fit_q := element.to_double.min (target_q)
|
||||
else
|
||||
best_fit_q := 0.0
|
||||
end
|
||||
end
|
||||
end
|
||||
parsed_ranges.forth
|
||||
end
|
||||
end
|
||||
create Result.make (best_fitness, best_fit_q)
|
||||
end
|
||||
|
||||
quality_parsed (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): REAL_64
|
||||
-- Find the best match for a given mime-type against a list of ranges that
|
||||
-- have already been parsed by parse_media_range. Returns the 'q' quality
|
||||
-- parameter of the best match, 0 if no match was found. This function
|
||||
-- bahaves the same as quality except that 'parsed_ranges' must be a list
|
||||
-- of parsed media ranges.
|
||||
do
|
||||
Result := fitness_and_quality_parsed (a_mime_type, parsed_ranges).quality
|
||||
end
|
||||
|
||||
quality (a_mime_type: READABLE_STRING_8; ranges: READABLE_STRING_8): REAL_64
|
||||
-- Returns the quality 'q' of a mime-type when compared against the
|
||||
-- mediaRanges in ranges.
|
||||
local
|
||||
l_ranges : LIST [READABLE_STRING_8]
|
||||
res : ARRAYED_LIST [HTTP_MEDIA_TYPE]
|
||||
p_res : HTTP_MEDIA_TYPE
|
||||
do
|
||||
l_ranges := ranges.split (',')
|
||||
from
|
||||
create res.make (10);
|
||||
l_ranges.start
|
||||
until
|
||||
l_ranges.after
|
||||
loop
|
||||
p_res := parse_media_range (l_ranges.item_for_iteration)
|
||||
res.put_left (p_res)
|
||||
l_ranges.forth
|
||||
end
|
||||
Result := quality_parsed (a_mime_type, res)
|
||||
end
|
||||
|
||||
best_match (supported: LIST [READABLE_STRING_8]; header: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- Choose the mime-type with the highest fitness score and quality ('q') from a list of candidates.
|
||||
local
|
||||
l_header_results: LIST [HTTP_MEDIA_TYPE]
|
||||
weighted_matches: LIST [FITNESS_AND_QUALITY]
|
||||
l_res: LIST [READABLE_STRING_8]
|
||||
p_res: HTTP_MEDIA_TYPE
|
||||
fitness_and_quality, first_one: detachable FITNESS_AND_QUALITY
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
l_res := header.split (',')
|
||||
create {ARRAYED_LIST [HTTP_MEDIA_TYPE]} l_header_results.make (l_res.count)
|
||||
|
||||
fixme("Extract method!!!")
|
||||
from
|
||||
l_res.start
|
||||
until
|
||||
l_res.after
|
||||
loop
|
||||
p_res := parse_media_range (l_res.item_for_iteration)
|
||||
l_header_results.force (p_res)
|
||||
l_res.forth
|
||||
end
|
||||
|
||||
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} weighted_matches.make (supported.count)
|
||||
|
||||
from
|
||||
supported.start
|
||||
until
|
||||
supported.after
|
||||
loop
|
||||
fitness_and_quality := fitness_and_quality_parsed (supported.item_for_iteration, l_header_results)
|
||||
fitness_and_quality.set_mime_type (mime_type (supported.item_for_iteration))
|
||||
weighted_matches.force (fitness_and_quality)
|
||||
supported.forth
|
||||
end
|
||||
|
||||
--| Keep only top quality+fitness types
|
||||
from
|
||||
weighted_matches.start
|
||||
first_one := weighted_matches.item
|
||||
weighted_matches.forth
|
||||
until
|
||||
weighted_matches.after
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if first_one < fitness_and_quality then
|
||||
first_one := fitness_and_quality
|
||||
if not weighted_matches.isfirst then
|
||||
from
|
||||
weighted_matches.back
|
||||
until
|
||||
weighted_matches.before
|
||||
loop
|
||||
weighted_matches.remove
|
||||
weighted_matches.back
|
||||
end
|
||||
weighted_matches.forth
|
||||
end
|
||||
check weighted_matches.item = fitness_and_quality end
|
||||
weighted_matches.forth
|
||||
elseif first_one.is_equal (fitness_and_quality) then
|
||||
weighted_matches.forth
|
||||
else
|
||||
check first_one > fitness_and_quality end
|
||||
weighted_matches.remove
|
||||
end
|
||||
end
|
||||
if first_one /= Void and then first_one.quality /= 0.0 then
|
||||
if weighted_matches.count = 1 then
|
||||
Result := first_one.mime_type
|
||||
else
|
||||
from
|
||||
fitness_and_quality := Void
|
||||
l_header_results.start
|
||||
until
|
||||
l_header_results.after or fitness_and_quality /= Void
|
||||
loop
|
||||
s := l_header_results.item.simple_type
|
||||
from
|
||||
weighted_matches.start
|
||||
until
|
||||
weighted_matches.after or fitness_and_quality /= Void
|
||||
loop
|
||||
fitness_and_quality := weighted_matches.item
|
||||
if fitness_and_quality.mime_type.same_string (s) then
|
||||
--| Found
|
||||
else
|
||||
fitness_and_quality := Void
|
||||
weighted_matches.forth
|
||||
end
|
||||
end
|
||||
l_header_results.forth
|
||||
end
|
||||
if fitness_and_quality /= Void then
|
||||
Result := fitness_and_quality.mime_type
|
||||
else
|
||||
Result := first_one.mime_type
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
note
|
||||
description: "{MIME_TYPE_PARSER_UTILITIES}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
MIME_TYPE_PARSER_UTILITIES
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
mime_type (a_str: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- `s' with any trailing parameters stripped
|
||||
local
|
||||
p: INTEGER
|
||||
do
|
||||
p := a_str.index_of (';', 1)
|
||||
if p > 0 then
|
||||
Result := trim (a_str.substring (1, p - 1))
|
||||
else
|
||||
Result := trim (a_str.string)
|
||||
end
|
||||
end
|
||||
|
||||
trim (a_string: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- trim whitespace from the beginning and end of a string
|
||||
-- `a_string'
|
||||
require
|
||||
valid_argument : a_string /= Void
|
||||
local
|
||||
l_result: STRING
|
||||
do
|
||||
l_result := a_string.as_string_8
|
||||
l_result.left_adjust
|
||||
l_result.right_adjust
|
||||
Result := l_result
|
||||
ensure
|
||||
result_same_as_argument: Result.same_string_general (a_string)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,120 @@
|
||||
note
|
||||
description: "Summary description for {COMMON_RESULTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
COMMON_RESULTS
|
||||
|
||||
inherit
|
||||
|
||||
ANY
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
DEBUG_OUTPUT
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create params.make (2)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
field: detachable STRING
|
||||
|
||||
item (a_key: STRING): detachable STRING
|
||||
-- Item associated with `a_key', if present
|
||||
-- otherwise default value of type `STRING'
|
||||
do
|
||||
Result := params.item (a_key)
|
||||
end
|
||||
|
||||
keys: LIST [STRING]
|
||||
-- arrays of currents keys
|
||||
local
|
||||
res: ARRAYED_LIST [STRING]
|
||||
do
|
||||
create res.make_from_array (params.current_keys)
|
||||
Result := res
|
||||
end
|
||||
|
||||
has_key (a_key: STRING): BOOLEAN
|
||||
-- Is there an item in the table with key `a_key'?
|
||||
do
|
||||
Result := params.has_key (a_key)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_field (a_field: STRING)
|
||||
-- Set type with `a_charset'
|
||||
do
|
||||
field := a_field
|
||||
ensure
|
||||
field_set: attached field as l_field implies l_field = a_field
|
||||
end
|
||||
|
||||
put (new: STRING; key: STRING)
|
||||
-- Insert `new' with `key' if there is no other item
|
||||
-- associated with the same key. If present, replace
|
||||
-- the old value with `new'
|
||||
do
|
||||
if params.has_key (key) then
|
||||
params.replace (new, key)
|
||||
else
|
||||
params.force (new, key)
|
||||
end
|
||||
ensure
|
||||
has_key: params.has_key (key)
|
||||
has_item: params.has_item (new)
|
||||
end
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
out: STRING
|
||||
-- Representation of the current object
|
||||
do
|
||||
create Result.make_from_string ("(")
|
||||
if attached field as t then
|
||||
Result.append_string ("'" + t + "',")
|
||||
end
|
||||
Result.append_string (" {")
|
||||
from
|
||||
params.start
|
||||
until
|
||||
params.after
|
||||
loop
|
||||
Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
|
||||
params.forth
|
||||
end
|
||||
Result.append ("})")
|
||||
end
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := out
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
params: HASH_TABLE [STRING, STRING]
|
||||
--dictionary of all the parameters for the media range
|
||||
|
||||
;
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,146 @@
|
||||
note
|
||||
description: "Summary description for {LANGUAGE_RESULTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
LANGUAGE_RESULTS
|
||||
|
||||
inherit
|
||||
|
||||
ANY
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
DEBUG_OUTPUT
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create params.make (2)
|
||||
create mime_type.make_from_string ("*")
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
type: detachable STRING
|
||||
|
||||
sub_type: detachable STRING
|
||||
|
||||
mime_type: STRING
|
||||
|
||||
item (a_key: STRING): detachable STRING
|
||||
-- Item associated with `a_key', if present
|
||||
-- otherwise default value of type `STRING'
|
||||
do
|
||||
Result := params.item (a_key)
|
||||
end
|
||||
|
||||
keys: LIST [STRING]
|
||||
-- arrays of currents keys
|
||||
local
|
||||
res: ARRAYED_LIST [STRING]
|
||||
do
|
||||
create res.make_from_array (params.current_keys)
|
||||
Result := res
|
||||
end
|
||||
|
||||
has_key (a_key: STRING): BOOLEAN
|
||||
-- Is there an item in the table with key `a_key'?
|
||||
do
|
||||
Result := params.has_key (a_key)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_type (a_type: STRING)
|
||||
-- Set type with `a_type'
|
||||
do
|
||||
type := a_type
|
||||
if attached sub_type as st then
|
||||
mime_type := a_type + "-" + st
|
||||
else
|
||||
mime_type := a_type
|
||||
end
|
||||
ensure
|
||||
type_assigned: type ~ a_type
|
||||
end
|
||||
|
||||
set_sub_type (a_sub_type: STRING)
|
||||
-- Set sub_type with `a_sub_type
|
||||
do
|
||||
sub_type := a_sub_type
|
||||
if attached type as t then
|
||||
mime_type := t + "-" + a_sub_type
|
||||
else
|
||||
mime_type := "*"
|
||||
end
|
||||
ensure
|
||||
sub_type_assigned: sub_type ~ a_sub_type
|
||||
end
|
||||
|
||||
put (new: STRING; key: STRING)
|
||||
-- Insert `new' with `key' if there is no other item
|
||||
-- associated with the same key. If present, replace
|
||||
-- the old value with `new'
|
||||
do
|
||||
if params.has_key (key) then
|
||||
params.replace (new, key)
|
||||
else
|
||||
params.force (new, key)
|
||||
end
|
||||
ensure
|
||||
has_key: params.has_key (key)
|
||||
has_item: params.has_item (new)
|
||||
end
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
out: STRING
|
||||
-- Representation of the current object
|
||||
do
|
||||
create Result.make_from_string ("(")
|
||||
if attached type as t then
|
||||
Result.append_string ("'" + t + "',")
|
||||
end
|
||||
if attached sub_type as st then
|
||||
Result.append_string (" '" + st + "',")
|
||||
end
|
||||
Result.append_string (" {")
|
||||
from
|
||||
params.start
|
||||
until
|
||||
params.after
|
||||
loop
|
||||
Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
|
||||
params.forth
|
||||
end
|
||||
Result.append ("})")
|
||||
end
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := out
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
params: HASH_TABLE [STRING, STRING]
|
||||
--dictionary of all the parameters for the media range
|
||||
|
||||
;
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,146 @@
|
||||
note
|
||||
description: "Summary description for {PARSE_RESULTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
PARSE_RESULTS
|
||||
|
||||
inherit
|
||||
|
||||
ANY
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
DEBUG_OUTPUT
|
||||
redefine
|
||||
out
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create params.make (2)
|
||||
create mime_type.make_from_string ("*/*")
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
type: detachable STRING
|
||||
|
||||
sub_type: detachable STRING
|
||||
|
||||
mime_type: STRING
|
||||
|
||||
item (a_key: STRING): detachable STRING
|
||||
-- Item associated with `a_key', if present
|
||||
-- otherwise default value of type `STRING'
|
||||
do
|
||||
Result := params.item (a_key)
|
||||
end
|
||||
|
||||
keys: LIST [STRING]
|
||||
-- arrays of currents keys
|
||||
local
|
||||
res: ARRAYED_LIST [STRING]
|
||||
do
|
||||
create res.make_from_array (params.current_keys)
|
||||
Result := res
|
||||
end
|
||||
|
||||
has_key (a_key: STRING): BOOLEAN
|
||||
-- Is there an item in the table with key `a_key'?
|
||||
do
|
||||
Result := params.has_key (a_key)
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_type (a_type: STRING)
|
||||
-- Set type with `a_type'
|
||||
do
|
||||
type := a_type
|
||||
if attached sub_type as st then
|
||||
mime_type := a_type + "/" + st
|
||||
else
|
||||
mime_type := a_type + "/*"
|
||||
end
|
||||
ensure
|
||||
type_assigned: type ~ a_type
|
||||
end
|
||||
|
||||
set_sub_type (a_sub_type: STRING)
|
||||
-- Set sub_type with `a_sub_type
|
||||
do
|
||||
sub_type := a_sub_type
|
||||
if attached type as t then
|
||||
mime_type := t + "/" + a_sub_type
|
||||
else
|
||||
mime_type := "*/" + a_sub_type
|
||||
end
|
||||
ensure
|
||||
sub_type_assigned: sub_type ~ a_sub_type
|
||||
end
|
||||
|
||||
put (new: STRING; key: STRING)
|
||||
-- Insert `new' with `key' if there is no other item
|
||||
-- associated with the same key. If present, replace
|
||||
-- the old value with `new'
|
||||
do
|
||||
if params.has_key (key) then
|
||||
params.replace (new, key)
|
||||
else
|
||||
params.force (new, key)
|
||||
end
|
||||
ensure
|
||||
has_key: params.has_key (key)
|
||||
has_item: params.has_item (new)
|
||||
end
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
out: STRING
|
||||
-- Representation of the current object
|
||||
do
|
||||
create Result.make_from_string ("(")
|
||||
if attached type as t then
|
||||
Result.append_string ("'" + t + "',")
|
||||
end
|
||||
if attached sub_type as st then
|
||||
Result.append_string (" '" + st + "',")
|
||||
end
|
||||
Result.append_string (" {")
|
||||
from
|
||||
params.start
|
||||
until
|
||||
params.after
|
||||
loop
|
||||
Result.append ("'" + params.key_for_iteration + "':'" + params.item_for_iteration + "',");
|
||||
params.forth
|
||||
end
|
||||
Result.append ("})")
|
||||
end
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := out
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
params: HASH_TABLE [STRING, STRING]
|
||||
--dictionary of all the parameters for the media range
|
||||
|
||||
;
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
note
|
||||
description: "Summary description for {SHARED_CONNEG}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SHARED_CONNEG
|
||||
|
||||
feature
|
||||
|
||||
Mime: MIME_PARSE
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
Common: COMMON_ACCEPT_HEADER_PARSER
|
||||
-- Charset and Encoding
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
Language: LANGUAGE_PARSE
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
note
|
||||
description: "[
|
||||
{CHARACTER_ENCODING_VARIANT_RESULTS}
|
||||
Represent the character sets results between client preferences and character sets variants supported by the server.
|
||||
If the server is unable to supports the requested Accept-Charset values, the server can build
|
||||
a response with the list of supported character sets.
|
||||
]"
|
||||
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
CHARACTER_ENCODING_VARIANT_RESULTS
|
||||
|
||||
inherit
|
||||
|
||||
VARIANT_RESULTS
|
||||
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
|
||||
set_variant_header
|
||||
-- Set variant header as `Accept-Charset'
|
||||
do
|
||||
variant_header := "Accept-Charset"
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
note
|
||||
description: "[
|
||||
{COMPRESSION_VARIANT_RESULTS}
|
||||
Represent the compression results between client preferences and ccompression variants supported by the server.
|
||||
If the server is unable to supports the requested Accept-Encoding values, the server can build
|
||||
a response with the list of supported encodings/compressions
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name= Compression", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
|
||||
|
||||
class
|
||||
COMPRESSION_VARIANT_RESULTS
|
||||
|
||||
inherit
|
||||
|
||||
VARIANT_RESULTS
|
||||
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
set_variant_header
|
||||
-- Set variant_header as `Accept-Encoding'
|
||||
do
|
||||
variant_header := "Accept-Encoding"
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
note
|
||||
description: "[
|
||||
{LANGUAGE_VARIANT_RESULTS}.
|
||||
Represent the language results between client preferences and language variants supported by the server.
|
||||
If the server is unable to supports the requested Accept-Language values, the server can build
|
||||
a response with the list of supported languages
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
LANGUAGE_VARIANT_RESULTS
|
||||
|
||||
inherit
|
||||
|
||||
VARIANT_RESULTS
|
||||
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
|
||||
set_variant_header
|
||||
-- Set variant header as 'Accept-Language'
|
||||
do
|
||||
variant_header := "Accept-Language"
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,32 @@
|
||||
note
|
||||
description: "[
|
||||
{MEDIA_TYPE_VARIANT_RESULTS}.
|
||||
Represent the media type results between client preferences and media type variants supported by the server..
|
||||
If the server is unable to supports the requested Accept values, the server can build
|
||||
a response with the list of supported representations
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
MEDIA_TYPE_VARIANT_RESULTS
|
||||
|
||||
inherit
|
||||
|
||||
VARIANT_RESULTS
|
||||
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
set_variant_header
|
||||
-- Set variant header as `Accept'
|
||||
do
|
||||
variant_header := "Accept"
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,83 @@
|
||||
note
|
||||
description: "Generic {VARIANT_RESULTS}.with common functionality to most header variants.."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
VARIANT_RESULTS
|
||||
|
||||
feature -- Access
|
||||
|
||||
variant_header: detachable READABLE_STRING_8
|
||||
-- Name of variant header to be added to the Vary header of the response
|
||||
|
||||
supported_variants: detachable LIST [READABLE_STRING_8]
|
||||
-- Set of supported variants for the response
|
||||
|
||||
is_acceptable: BOOLEAN
|
||||
-- is the current variant accepted?
|
||||
|
||||
type: detachable READABLE_STRING_8
|
||||
-- the type could be: media type, language, chracter_sets and encoding.
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
accept_headers_set: ARRAY[READABLE_STRING_8]
|
||||
-- Set of valid accept headers headers
|
||||
note
|
||||
EIS:"name=Accept", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri"
|
||||
EIS:"name=Accept-Charset", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2", "protocol=uri"
|
||||
EIS:"name=Accept-Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
|
||||
EIS:"name=Accept-Language", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4", "protocol=uri"
|
||||
once
|
||||
Result:= <<"Accept","Accept-Language","Accept-Encoding","Accept-Charset">>
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
feature -- Status_Report
|
||||
|
||||
is_valid_header (a_header: READABLE_STRING_8): BOOLEAN
|
||||
-- is `a_header' a valid accept header?
|
||||
do
|
||||
Result := accept_headers_set.has (a_header)
|
||||
end
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
|
||||
set_type (a_type: READABLE_STRING_8)
|
||||
-- Set `type' as `a_type'
|
||||
do
|
||||
type := a_type
|
||||
ensure
|
||||
type_set: attached type as l_type implies l_type = a_type
|
||||
end
|
||||
|
||||
set_acceptable (acceptable: BOOLEAN)
|
||||
-- Set `is_acceptable' with `acceptable'
|
||||
do
|
||||
is_acceptable := acceptable
|
||||
ensure
|
||||
is_acceptable_set: is_acceptable = acceptable
|
||||
end
|
||||
|
||||
set_variant_header
|
||||
-- Set variant header
|
||||
deferred
|
||||
ensure
|
||||
is_valid_header_set : attached variant_header as l_header implies is_valid_header (l_header)
|
||||
end
|
||||
|
||||
set_supported_variants (a_supported: LIST [READABLE_STRING_8])
|
||||
-- Set `supported variants' with `a_supported'
|
||||
do
|
||||
supported_variants := a_supported
|
||||
ensure
|
||||
set_supported_variants: attached supported_variants as l_supported_variants implies l_supported_variants = a_supported
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user