Updated content_negotiation with better class names and feature names.

Minor semantic changes in VARIANTS classes
Factorized some code in HTTP_ACCEPT_LANGUAGE
This commit is contained in:
2013-10-15 23:19:12 +02:00
parent 3072ce7dec
commit d376f99832
26 changed files with 1185 additions and 1052 deletions

View File

@@ -1,7 +1,6 @@
CONNEG is a library that provides utilities to select the best repesentation of a resource for a client CONNEG is a library that provides utilities to select the best repesentation of a resource for a client where there are multiple representations available.
where there are multiple representations available.
Using this labrary you can retrieve the best Variant for media type, language preference, enconding and compression. Using this library you can retrieve the best variant for media type, language preference, enconding and compression.
The library is based on eMIME Eiffel MIME library based on Joe Gregorio code The library is based on eMIME Eiffel MIME library based on Joe Gregorio code
Take into account that the library is under development so is expected that the API change. Take into account that the library is under development so is expected that the API change.
@@ -9,7 +8,7 @@ Take into account that the library is under development so is expected that the
The library contains utilities that deal with content negotiation (server driven negotiation).This utility class The library contains utilities that deal with content negotiation (server driven negotiation).This utility class
is based on ideas taken from the Book Restful WebServices Cookbook is based on ideas taken from the Book Restful WebServices Cookbook
The class CONNEG_SERVER_SIDE contains several features that helps to write different type of negotiations (media type, language, The class CONNEG_SERVER_SIDE contains several features that helps to write different types of negotiation (media type, language,
charset and compression). charset and compression).
So for each of the following questions, you will have a corresponding method to help in the solution. So for each of the following questions, you will have a corresponding method to help in the solution.

View File

@@ -13,6 +13,12 @@
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http" location="..\http\http-safe.ecf" readonly="false"/> <library name="http" location="..\http\http-safe.ecf" readonly="false"/>
<cluster name="conneg" location=".\src\" recursive="true"/> <cluster name="conneg" location=".\src\" recursive="true">
<file_rule>
<exclude>/implementation</exclude>
</file_rule>
</cluster>
<cluster name="conneg_imp" location=".\src\implementation" recursive="true" hidden="true">
</cluster>
</target> </target>
</system> </system>

View File

@@ -13,6 +13,12 @@
</option> </option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="http" location="../http/http.ecf"/> <library name="http" location="../http/http.ecf"/>
<cluster name="conneg" location=".\src" recursive="true"/> <cluster name="conneg" location=".\src\" recursive="true">
<file_rule>
<exclude>/implementation</exclude>
</file_rule>
</cluster>
<cluster name="conneg_imp" location=".\src\implementation" recursive="true" hidden="true">
</cluster>
</target> </target>
</system> </system>

View File

@@ -17,81 +17,96 @@ class
CONNEG_SERVER_SIDE CONNEG_SERVER_SIDE
inherit inherit
SHARED_CONNEG
REFACTORING_HELPER REFACTORING_HELPER
create create
make make
feature -- Initialization feature {NONE} -- Initialization
make (a_mime: READABLE_STRING_8; a_language: READABLE_STRING_8; a_charset: READABLE_STRING_8; a_encoding: READABLE_STRING_8) make (a_mediatype_dft: READABLE_STRING_8; a_language_dft: READABLE_STRING_8; a_charset_dft: READABLE_STRING_8; a_encoding_dft: READABLE_STRING_8)
-- Initialize Current with default Media type, language, charset and encoding.
do do
set_mime_default (a_mime) initialize
set_language_default (a_language) set_default_media_type (a_mediatype_dft)
set_charset_default (a_charset) set_default_language (a_language_dft)
set_encoding_default (a_encoding) set_default_charset (a_charset_dft)
set_default_encoding (a_encoding_dft)
ensure ensure
mime_default_set: mime_default = a_mime default_media_type_set: default_media_type = a_mediatype_dft
language_default_set: language_default = a_language default_language_set: default_language = a_language_dft
charset_default_set: charset_default = a_charset default_charset_set: default_charset = a_charset_dft
encoding_default_set: encoding_default = a_encoding default_encoding_set: default_encoding = a_encoding_dft
end end
feature -- AccessServer Side Defaults Formats initialize
-- Initialize Current
do
create accept_media_type_parser
create any_header_parser
create accept_language_parser
end
mime_default: READABLE_STRING_8 accept_media_type_parser: HTTP_ACCEPT_MEDIA_TYPE_PARSER
-- MIME
any_header_parser: HTTP_ANY_ACCEPT_HEADER_PARSER
-- Charset and Encoding
accept_language_parser: HTTP_ACCEPT_LANGUAGE_PARSER
-- Language
feature -- Access: Server Side Defaults Formats
default_media_type: READABLE_STRING_8
-- Media type which is acceptable for the response. -- Media type which is acceptable for the response.
language_default: READABLE_STRING_8 default_language: READABLE_STRING_8
-- Natural language that is preferred as a response to the request. -- Natural language that is preferred as a response to the request.
charset_default: READABLE_STRING_8 default_charset: READABLE_STRING_8
-- Character set that is acceptable for the response. -- Character set that is acceptable for the response.
encoding_default: READABLE_STRING_8 default_encoding: READABLE_STRING_8
-- Content-coding that is acceptable in the response. -- Content-coding that is acceptable in the response.
feature -- Change Element feature -- Change Element
set_mime_default (a_mime: READABLE_STRING_8) set_default_media_type (a_mediatype: READABLE_STRING_8)
-- Set the mime_default with `a_mime' -- Set `default_media_type' with `a_mediatype'
do do
mime_default := a_mime default_media_type := a_mediatype
ensure ensure
mime_default_set: a_mime = mime_default default_media_type_set: a_mediatype = default_media_type
end end
set_language_default (a_language: READABLE_STRING_8) set_default_language (a_language: READABLE_STRING_8)
-- Set the language_default with `a_language' -- Set `default_language' with `a_language'
do do
language_default := a_language default_language := a_language
ensure ensure
language_default_set: a_language = language_default default_language_set: a_language = default_language
end end
set_charset_default (a_charset: READABLE_STRING_8) set_default_charset (a_charset: READABLE_STRING_8)
-- Set the charset_default with `a_charset' -- Set `default_charset' with `a_charset'
do do
charset_default := a_charset default_charset := a_charset
ensure ensure
charset_default_set: a_charset = charset_default default_charset_set: a_charset = default_charset
end end
set_encoding_default (a_encoding: READABLE_STRING_8) set_default_encoding (a_encoding: READABLE_STRING_8)
-- Set `default_encoding' with `a_encoding'
do do
encoding_default := a_encoding default_encoding := a_encoding
ensure ensure
encoding_default_set: a_encoding = encoding_default default_encoding_set: a_encoding = default_encoding
end end
feature -- Media Type Negotiation 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 media_type_preference (a_mime_types_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): HTTP_ACCEPT_MEDIA_TYPE_VARIANTS
-- `a_mime_types_supported' represent media types supported by the server. -- `a_mime_types_supported' represent media types supported by the server.
-- `a_header represent' the Accept header, ie, the client preferences. -- `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 -- Return which media type to use for representation in a response, if the server supports
@@ -101,30 +116,31 @@ feature -- Media Type Negotiation
local local
l_mime_match: READABLE_STRING_8 l_mime_match: READABLE_STRING_8
do do
create Result create Result.make
Result.set_supported_variants (a_mime_types_supported)
if a_header = Void or else a_header.is_empty then 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 -- 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_acceptable (True)
Result.set_type (mime_default) Result.set_variant_value (default_media_type)
else else
Result.set_vary_header_value
-- select the best match, server support, client preferences -- select the best match, server support, client preferences
l_mime_match := mime.best_match (a_mime_types_supported, a_header) l_mime_match := accept_media_type_parser.best_match (a_mime_types_supported, a_header)
if l_mime_match.is_empty then if l_mime_match.is_empty then
-- The server does not support any of the media types preferred by the client -- The server does not support any of the media types preferred by the client
Result.set_acceptable (False) Result.set_acceptable (False)
Result.set_supported_variants (a_mime_types_supported)
else else
-- Set the best match -- Set the best match
Result.set_type (l_mime_match) Result.set_variant_value (l_mime_match)
Result.set_acceptable (True) Result.set_acceptable (True)
Result.set_variant_header
end end
end end
end end
feature -- Encoding Negotiation feature -- Encoding Negotiation
charset_preference (a_server_charset_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): CHARACTER_ENCODING_VARIANT_RESULTS charset_preference (a_server_charset_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): HTTP_ACCEPT_CHARSET_VARIANTS
-- `a_server_charset_supported' represent a list of character sets supported by the server. -- `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. -- `a_header' represents the Accept-Charset header, ie, the client preferences.
-- Return which Charset to use in a response, if the server supports -- Return which Charset to use in a response, if the server supports
@@ -134,32 +150,33 @@ feature -- Encoding Negotiation
local local
l_charset_match: READABLE_STRING_8 l_charset_match: READABLE_STRING_8
do do
create Result create Result.make
Result.set_supported_variants (a_server_charset_supported)
if a_header = Void or else a_header.is_empty then 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 -- 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_acceptable (True)
Result.set_type (charset_default) Result.set_variant_value (default_charset)
else else
Result.set_vary_header_value
-- select the best match, server support, client preferences -- select the best match, server support, client preferences
l_charset_match := common.best_match (a_server_charset_supported, a_header) l_charset_match := any_header_parser.best_match (a_server_charset_supported, a_header)
if l_charset_match.is_empty then if l_charset_match.is_empty then
-- The server does not support any of the compression types prefered by the client -- The server does not support any of the compression types prefered by the client
Result.set_acceptable (False) Result.set_acceptable (False)
Result.set_supported_variants (a_server_charset_supported)
else else
-- Set the best match -- Set the best match
Result.set_type (l_charset_match) Result.set_variant_value (l_charset_match)
Result.set_acceptable (True) Result.set_acceptable (True)
Result.set_variant_header
end end
end end
end end
feature -- Compression Negotiation feature -- Compression Negotiation
encoding_preference (a_server_encoding_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): COMPRESSION_VARIANT_RESULTS encoding_preference (a_server_encoding_supported: LIST [READABLE_STRING_8]; a_header_value: detachable READABLE_STRING_8): HTTP_ACCEPT_ENCODING_VARIANTS
-- `a_server_encoding_supported' represent a list of encoding supported by the server. -- `a_server_encoding_supported' represent a list of encoding supported by the server.
-- `a_header' represent the Accept-Encoding header, ie, the client preferences. -- `a_header_value' represent the Accept-Encoding header, ie, the client preferences.
-- Return which Encoding to use in a response, if the server supports -- Return which Encoding to use in a response, if the server supports
-- the requested Encoding, or empty in other case. -- the requested Encoding, or empty in other case.
note note
@@ -167,32 +184,33 @@ feature -- Compression Negotiation
local local
l_compression_match: READABLE_STRING_8 l_compression_match: READABLE_STRING_8
do do
create Result create Result.make
if a_header = Void or else a_header.is_empty then Result.set_supported_variants (a_server_encoding_supported)
if a_header_value = Void or else a_header_value.is_empty then
-- the request has no Accept-Encoding header, ie the header is empty, in this case do not compress representations -- 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_acceptable (True)
Result.set_type (encoding_default) Result.set_variant_value (default_encoding)
else else
Result.set_vary_header_value
-- select the best match, server support, client preferences -- select the best match, server support, client preferences
l_compression_match := common.best_match (a_server_encoding_supported, a_header) l_compression_match := any_header_parser.best_match (a_server_encoding_supported, a_header_value)
if l_compression_match.is_empty then if l_compression_match.is_empty then
-- The server does not support any of the compression types prefered by the client -- The server does not support any of the compression types prefered by the client
Result.set_acceptable (False) Result.set_acceptable (False)
Result.set_supported_variants (a_server_encoding_supported)
else else
-- Set the best match -- Set the best match
Result.set_type (l_compression_match) Result.set_variant_value (l_compression_match)
Result.set_acceptable (True) Result.set_acceptable (True)
Result.set_variant_header
end end
end end
end end
feature -- Language Negotiation feature -- Language Negotiation
language_preference (a_server_language_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): LANGUAGE_VARIANT_RESULTS language_preference (a_server_language_supported: LIST [READABLE_STRING_8]; a_header_value: detachable READABLE_STRING_8): HTTP_ACCEPT_LANGUAGE_VARIANTS
-- `a_server_language_supported' represent a list of languages supported by the server. -- `a_server_language_supported' represent a list of languages supported by the server.
-- `a_header' represent the Accept-Language header, ie, the client preferences. -- `a_header_value' represent the Accept-Language header, ie, the client preferences.
-- Return which Language to use in a response, if the server supports -- Return which Language to use in a response, if the server supports
-- the requested Language, or empty in other case. -- the requested Language, or empty in other case.
note note
@@ -201,28 +219,29 @@ feature -- Language Negotiation
local local
l_language_match: READABLE_STRING_8 l_language_match: READABLE_STRING_8
do do
create Result create Result.make
if a_header = Void or else a_header.is_empty then Result.set_supported_variants (a_server_language_supported)
if a_header_value = Void or else a_header_value.is_empty then
-- the request has no Accept header, ie the header is empty, in this case we use the default format -- 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_acceptable (True)
Result.set_type (language_default) Result.set_variant_value (default_language)
else else
Result.set_vary_header_value
-- select the best match, server support, client preferences -- select the best match, server support, client preferences
l_language_match := language.best_match (a_server_language_supported, a_header) l_language_match := accept_language_parser.best_match (a_server_language_supported, a_header_value)
if l_language_match.is_empty then if l_language_match.is_empty then
-- The server does not support any of the media types prefered by the client -- The server does not support any of the media types prefered by the client
Result.set_acceptable (False) Result.set_acceptable (False)
Result.set_supported_variants (a_server_language_supported)
else else
-- Set the best match -- Set the best match
Result.set_type (l_language_match) Result.set_variant_value (l_language_match)
Result.set_acceptable (True) Result.set_acceptable (True)
Result.set_variant_header
end end
end end
end end
note note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -1,6 +1,5 @@
note note
description: "Summary description for {FITNESS_AND_QUALITY}." description: "Summary description for {FITNESS_AND_QUALITY}."
author: ""
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
@@ -24,7 +23,7 @@ feature -- Initialization
do do
fitness := a_fitness fitness := a_fitness
quality := a_quality quality := a_quality
create mime_type.make_empty create {STRING_8} entity.make_empty
ensure ensure
fitness_assigned : fitness = a_fitness fitness_assigned : fitness = a_fitness
quality_assigned : quality = a_quality quality_assigned : quality = a_quality
@@ -36,17 +35,17 @@ feature -- Access
quality: REAL_64 quality: REAL_64
mime_type: STRING entity: READABLE_STRING_8
-- optionally used -- optionally used
-- empty by default -- empty by default
--| Could be a mime type, an encoding, ...
feature -- Status report feature -- Status report
debug_output: STRING debug_output: STRING
-- String that should be displayed in debugger to represent `Current'. -- String that should be displayed in debugger to represent `Current'.
do do
create Result.make_from_string (mime_type) create Result.make_from_string (entity)
Result.append (" (") Result.append (" (")
Result.append ("quality=" + quality.out) Result.append ("quality=" + quality.out)
Result.append (" ; fitness=" + fitness.out) Result.append (" ; fitness=" + fitness.out)
@@ -55,12 +54,12 @@ feature -- Status report
feature -- Element Change feature -- Element Change
set_mime_type (a_mime_type: STRING) set_entity (a_entity: READABLE_STRING_8)
-- set mime_type with `a_mime_type' -- set `entity' with `a_entity'
do do
mime_type := a_mime_type entity := a_entity
ensure ensure
mime_type_assigned : mime_type.same_string (a_mime_type) entity_assigned : entity.same_string (a_entity)
end end
feature -- Comparision feature -- Comparision
@@ -75,7 +74,7 @@ feature -- Comparision
end end
end end
note note
copyright: "2011-2011, Javier Velilla, Jocelyn Fiat and others" copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end end

View File

@@ -0,0 +1,269 @@
note
description: "[
{HTTP_ACCEPT_LANGUAGE_PARSER} is encharge to parse language tags defined as follow:
Accept-Language = "Accept-Language" ":"
1#( language-l_range [ ";" "q" "=" qvalue ] )
language-l_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
HTTP_ACCEPT_LANGUAGE_PARSER
inherit
HTTP_HEADER_PARSER
REFACTORING_HELPER
feature -- Parser
accept_language_list (a_header_value: READABLE_STRING_8): LIST [HTTP_ACCEPT_LANGUAGE]
-- Languages-ranges are languages with wild-cards and a 'q' quality parameter.
-- For example, the language l_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.
local
l_res: LIST [READABLE_STRING_8]
l_lang: HTTP_ACCEPT_LANGUAGE
do
l_res := a_header_value.split (',')
create {ARRAYED_LIST [HTTP_ACCEPT_LANGUAGE]} Result.make (l_res.count)
from
l_res.start
until
l_res.after
loop
create l_lang.make_from_string (l_res.item_for_iteration)
Result.force (l_lang)
l_res.forth
end
end
accept_language (a_accept_language_item: READABLE_STRING_8): HTTP_ACCEPT_LANGUAGE
-- Languages-ranges are languages with wild-cards and a 'q' quality parameter.
-- For example, the language l_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
create Result.make_from_string (a_accept_language_item)
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 l_range in `a_ranges'.
do
Result := quality_from_list (a_language, accept_language_list (a_ranges))
end
best_match (a_supported: LIST [READABLE_STRING_8]; a_header_value: READABLE_STRING_8): READABLE_STRING_8
-- Choose the `parse_language' with the highest fitness score and quality ('q') from a list of candidates.
local
l_header_results: LIST [HTTP_ACCEPT_LANGUAGE]
l_weighted_matches: LIST [FITNESS_AND_QUALITY]
l_res: LIST [READABLE_STRING_8]
p_res: HTTP_ACCEPT_LANGUAGE
l_fitness_and_quality, l_first_one: detachable FITNESS_AND_QUALITY
s: READABLE_STRING_8
do
l_header_results := accept_language_list (a_header_value)
--| weighted matches
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} l_weighted_matches.make (a_supported.count)
from
a_supported.start
until
a_supported.after
loop
l_fitness_and_quality := fitness_and_quality_from_list (a_supported.item_for_iteration, l_header_results)
l_fitness_and_quality.set_entity (entity_value (a_supported.item_for_iteration))
l_weighted_matches.force (l_fitness_and_quality)
a_supported.forth
end
--| Keep only top quality+fitness types
from
l_weighted_matches.start
l_first_one := l_weighted_matches.item
l_weighted_matches.forth
until
l_weighted_matches.after
loop
l_fitness_and_quality := l_weighted_matches.item
if l_first_one < l_fitness_and_quality then
l_first_one := l_fitness_and_quality
if not l_weighted_matches.isfirst then
from
l_weighted_matches.back
until
l_weighted_matches.before
loop
l_weighted_matches.remove
l_weighted_matches.back
end
l_weighted_matches.forth
end
check
l_weighted_matches.item = l_fitness_and_quality
end
l_weighted_matches.forth
elseif l_first_one ~ l_fitness_and_quality then
l_weighted_matches.forth
else
check
l_first_one > l_fitness_and_quality
end
l_weighted_matches.remove
end
end
if l_first_one /= Void and then l_first_one.quality /= 0.0 then
if l_weighted_matches.count = 1 then
Result := l_first_one.entity
else
from
l_fitness_and_quality := Void
l_header_results.start
until
l_header_results.after or l_fitness_and_quality /= Void
loop
s := l_header_results.item.language_range
from
l_weighted_matches.start
until
l_weighted_matches.after or l_fitness_and_quality /= Void
loop
l_fitness_and_quality := l_weighted_matches.item
if l_fitness_and_quality.entity.same_string (s) then
--| Found
else
l_fitness_and_quality := Void
l_weighted_matches.forth
end
end
l_header_results.forth
end
if l_fitness_and_quality /= Void then
Result := l_fitness_and_quality.entity
else
Result := l_first_one.entity
end
end
else
Result := ""
end
end
feature {NONE} -- Implementation
fitness_and_quality_from_list (a_language: READABLE_STRING_8; a_parsed_ranges: LIST [HTTP_ACCEPT_LANGUAGE]): 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
l_best_fitness: INTEGER
l_target_q: REAL_64
l_best_fit_q: REAL_64
l_target: HTTP_ACCEPT_LANGUAGE
l_target_type: READABLE_STRING_8
l_range: HTTP_ACCEPT_LANGUAGE
l_keys: LIST [READABLE_STRING_8]
l_param_matches: INTEGER
l_element: detachable READABLE_STRING_8
l_fitness: INTEGER
do
l_best_fitness := -1
l_best_fit_q := 0.0
create l_target.make_from_string (a_language)
l_target_q := l_target.quality
l_target_type := l_target.language
from
a_parsed_ranges.start
until
a_parsed_ranges.after
loop
l_range := a_parsed_ranges.item_for_iteration
if
attached l_range.language 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
l_param_matches := 0
l_keys := l_target.keys
l_keys.start
until
l_keys.after
loop
l_element := l_keys.item_for_iteration
if
not l_element.same_string ("q") and then
l_range.has_key (l_element) and then
(attached l_target.item (l_element) as t_item and attached l_range.item (l_element) as r_item) and then
t_item.same_string (r_item)
then
l_param_matches := l_param_matches + 1
end
l_keys.forth
end
if l_range_type.same_string (l_target_type) then
l_fitness := 100
else
l_fitness := 0
end
if
attached l_range.specialization as l_range_sub_type and then
attached l_target.specialization 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 + l_param_matches
if l_fitness > l_best_fitness then
l_best_fitness := l_fitness
l_element := l_range.item ("q")
if l_element /= Void then
l_best_fit_q := l_element.to_real_64.min (l_target_q)
else
l_best_fit_q := 0.0
end
end
end
a_parsed_ranges.forth
end
create Result.make (l_best_fitness, l_best_fit_q)
end
quality_from_list (a_language: READABLE_STRING_8; a_parsed_ranges: LIST [HTTP_ACCEPT_LANGUAGE]): 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_from_list (a_language, a_parsed_ranges).quality
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

View File

@@ -1,6 +1,6 @@
note note
description: "[ description: "[
{MIME_PARSE}. is encharge to parse Accept request-header field defined as follow: {HTTP_ACCEPT_MEDIA_TYPE_PARSER}. is encharge to parse Accept request-header field defined as follow:
Accept = "Accept" ":" Accept = "Accept" ":"
#( media-range [ accept-params ] ) #( media-range [ accept-params ] )
@@ -20,26 +20,17 @@
EIS: "name=Accept", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri" EIS: "name=Accept", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1", "protocol=uri"
class class
MIME_PARSE HTTP_ACCEPT_MEDIA_TYPE_PARSER
inherit {NONE} inherit
MIME_TYPE_PARSER_UTILITIES HTTP_HEADER_PARSER
REFACTORING_HELPER REFACTORING_HELPER
feature -- Parser feature -- Parser
parse_mime_type (a_mime_type: READABLE_STRING_8): HTTP_MEDIA_TYPE media_type (a_range: 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. -- 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: -- For example, the media range 'application/*;q=0.5' would get parsed into:
-- ('application', '*', {'q', '0.5'}) -- ('application', '*', {'q', '0.5'})
@@ -48,11 +39,11 @@ feature -- Parser
-- necessary. -- necessary.
do do
fixme ("Improve the code!!!") fixme ("Improve the code!!!")
Result := parse_mime_type (a_range) create Result.make_from_string (a_range)
if attached Result.parameter ("q") as q then if attached Result.parameter ("q") as q then
if if
q.is_double and then q.is_double and then
attached {REAL_64} q.to_double as r and then attached {REAL_64} q.to_real_64 as r and then
(r >= 0.0 and r <= 1.0) (r >= 0.0 and r <= 1.0)
then then
--| Keep current value --| Keep current value
@@ -68,8 +59,136 @@ feature -- Parser
end end
end 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 := media_type (l_ranges.item_for_iteration)
res.force (p_res)
l_ranges.forth
end
Result := quality_from_list (a_mime_type, res)
end
fitness_and_quality_parsed (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): FITNESS_AND_QUALITY 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 := media_type (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_from_list (supported.item_for_iteration, l_header_results)
fitness_and_quality.set_entity (entity_value (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.entity
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.entity.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.entity
else
Result := first_one.entity
end
end
else
Result := ""
end
end
feature {NONE} -- Implementation
fitness_and_quality_from_list (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 -- Find the best match for a given mimeType against a list of media_ranges
-- that have already been parsed by parse_media_range. -- that have already been parsed by parse_media_range.
local local
@@ -84,7 +203,7 @@ feature -- Parser
do do
best_fitness := -1 best_fitness := -1
best_fit_q := 0.0 best_fit_q := 0.0
target := parse_media_range (a_mime_type) target := media_type (a_mime_type)
if attached target.parameter ("q") as q and then q.is_double then if attached target.parameter ("q") as q and then q.is_double then
target_q := q.to_double target_q := q.to_double
if target_q < 0.0 then if target_q < 0.0 then
@@ -163,142 +282,16 @@ feature -- Parser
create Result.make (best_fitness, best_fit_q) create Result.make (best_fitness, best_fit_q)
end end
quality_parsed (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): REAL_64 quality_from_list (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 -- 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 -- 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 -- 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 -- bahaves the same as quality except that 'parsed_ranges' must be a list
-- of parsed media ranges. -- of parsed media ranges.
do do
Result := fitness_and_quality_parsed (a_mime_type, parsed_ranges).quality Result := fitness_and_quality_from_list (a_mime_type, parsed_ranges).quality
end 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.force (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 note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"

View File

@@ -1,6 +1,6 @@
note note
description: "[ description: "[
COMMON_ACCEPT_HEADER_PARSER, this class allows to parse Accept-Charset and Accept-Encoding headers HTTP_ANY_ACCEPT_HEADER_PARSER, this class allows to parse Accept-* headers
]" ]"
date: "$Date$" date: "$Date$"
@@ -9,17 +9,15 @@ note
EIS: "name=Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" EIS: "name=Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
class class
COMMON_ACCEPT_HEADER_PARSER HTTP_ANY_ACCEPT_HEADER_PARSER
inherit {NONE}
MIME_TYPE_PARSER_UTILITIES
inherit
HTTP_HEADER_PARSER
feature -- Parser feature -- Parser
parse_common (header: READABLE_STRING_8): COMMON_RESULTS header (a_header: READABLE_STRING_8): HTTP_ANY_ACCEPT_HEADER
-- Parses `header' charset/encoding into its component parts. -- Parses `a_header' charset/encoding into its component parts.
-- For example, the charset 'iso-8889-5' would get parsed -- For example, the charset 'iso-8889-5' would get parsed
-- into: -- into:
-- ('iso-8889-5', {'q':'1.0'}) -- ('iso-8889-5', {'q':'1.0'})
@@ -31,7 +29,7 @@ feature -- Parser
l_header: READABLE_STRING_8 l_header: READABLE_STRING_8
do do
create Result.make create Result.make
l_parts := header.split (';') l_parts := a_header.split (';')
if l_parts.count = 1 then if l_parts.count = 1 then
Result.put ("1.0", "q") Result.put ("1.0", "q")
else else
@@ -40,7 +38,7 @@ feature -- Parser
until until
i > l_parts.count i > l_parts.count
loop loop
p := l_parts.at (i) p := l_parts [i]
sub_parts := p.split ('=') sub_parts := p.split ('=')
if sub_parts.count = 2 then if sub_parts.count = 2 then
Result.put (trim (sub_parts [2]), trim (sub_parts [1])) Result.put (trim (sub_parts [2]), trim (sub_parts [1]))
@@ -52,7 +50,141 @@ feature -- Parser
Result.set_field (trim (l_header)) Result.set_field (trim (l_header))
end end
fitness_and_quality_parsed (a_field: READABLE_STRING_8; a_parsed_charsets: LIST [COMMON_RESULTS]): FITNESS_AND_QUALITY quality (a_field: READABLE_STRING_8; a_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 [HTTP_ANY_ACCEPT_HEADER]
p_res: HTTP_ANY_ACCEPT_HEADER
do
l_commons := a_commons.split (',')
from
create res.make (10)
l_commons.start
until
l_commons.after
loop
p_res := header (l_commons.item_for_iteration)
res.force (p_res)
l_commons.forth
end
Result := quality_from_list (a_field, res)
end
best_match (a_supported: LIST [READABLE_STRING_8]; a_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 [HTTP_ANY_ACCEPT_HEADER]
l_weighted_matches: LIST [FITNESS_AND_QUALITY]
l_res: LIST [READABLE_STRING_8]
p_res: HTTP_ANY_ACCEPT_HEADER
l_fitness_and_quality, l_first_one: detachable FITNESS_AND_QUALITY
do
l_res := a_header.split (',')
create {ARRAYED_LIST [HTTP_ANY_ACCEPT_HEADER]} l_header_results.make (l_res.count)
from
l_res.start
until
l_res.after
loop
p_res := header (l_res.item_for_iteration)
l_header_results.force (p_res)
l_res.forth
end
create {ARRAYED_LIST [FITNESS_AND_QUALITY]} l_weighted_matches.make (a_supported.count)
from
a_supported.start
until
a_supported.after
loop
l_fitness_and_quality := fitness_and_quality_from_list (a_supported.item_for_iteration, l_header_results)
l_fitness_and_quality.set_entity (entity_value (a_supported.item_for_iteration))
l_weighted_matches.force (l_fitness_and_quality)
a_supported.forth
end
--| Keep only top quality+fitness types
--| TODO extract method
from
l_weighted_matches.start
l_first_one := l_weighted_matches.item
l_weighted_matches.forth
until
l_weighted_matches.after
loop
l_fitness_and_quality := l_weighted_matches.item
if l_first_one < l_fitness_and_quality then
l_first_one := l_fitness_and_quality
if not l_weighted_matches.isfirst then
from
l_weighted_matches.back
until
l_weighted_matches.before
loop
l_weighted_matches.remove
l_weighted_matches.back
end
l_weighted_matches.forth
end
check
l_weighted_matches.item = l_fitness_and_quality
end
l_weighted_matches.forth
elseif l_first_one.is_equal (l_fitness_and_quality) then
l_weighted_matches.forth
else
check
l_first_one > l_fitness_and_quality
end
l_weighted_matches.remove
end
end
if l_first_one /= Void and then l_first_one.quality /= 0.0 then
if l_weighted_matches.count = 1 then
Result := l_first_one.entity
else
from
l_fitness_and_quality := Void
l_header_results.start
until
l_header_results.after or l_fitness_and_quality /= Void
loop
if attached l_header_results.item.field as l_field then
from
l_weighted_matches.start
until
l_weighted_matches.after or l_fitness_and_quality /= Void
loop
l_fitness_and_quality := l_weighted_matches.item
if l_fitness_and_quality.entity.same_string (l_field) then
--| Found
else
l_fitness_and_quality := Void
l_weighted_matches.forth
end
end
else
check
has_field: False
end
end
l_header_results.forth
end
if l_fitness_and_quality /= Void then
Result := l_fitness_and_quality.entity
else
Result := l_first_one.entity
end
end
else
Result := ""
end
end
feature {NONE} -- Implementation
fitness_and_quality_from_list (a_field: READABLE_STRING_8; a_parsed_charsets: LIST [HTTP_ANY_ACCEPT_HEADER]): FITNESS_AND_QUALITY
-- Find the best match for a given charset/encoding against a list of charsets/encodings -- 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 -- that have already been parsed by parse_common. Returns a
-- tuple of the fitness value and the value of the 'q' quality parameter of -- tuple of the fitness value and the value of the 'q' quality parameter of
@@ -62,14 +194,14 @@ feature -- Parser
best_fitness: INTEGER best_fitness: INTEGER
target_q: REAL_64 target_q: REAL_64
best_fit_q: REAL_64 best_fit_q: REAL_64
target: COMMON_RESULTS target: HTTP_ANY_ACCEPT_HEADER
range: COMMON_RESULTS range: HTTP_ANY_ACCEPT_HEADER
element: detachable READABLE_STRING_8 element: detachable READABLE_STRING_8
l_fitness: INTEGER l_fitness: INTEGER
do do
best_fitness := -1 best_fitness := -1
best_fit_q := 0.0 best_fit_q := 0.0
target := parse_common (a_field) target := header (a_field)
if attached target.item ("q") as q and then q.is_double then if attached target.item ("q") as q and then q.is_double then
target_q := q.to_double target_q := q.to_double
if target_q < 0.0 then if target_q < 0.0 then
@@ -111,146 +243,15 @@ feature -- Parser
create Result.make (best_fitness, best_fit_q) create Result.make (best_fitness, best_fit_q)
end end
quality_parsed (a_field: READABLE_STRING_8; parsed_common: LIST [COMMON_RESULTS]): REAL_64 quality_from_list (a_field: READABLE_STRING_8; a_parsed_common: LIST [HTTP_ANY_ACCEPT_HEADER]): REAL_64
-- Find the best match for a given charset/encoding against a list of charsets/encodings that -- 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 -- have already been parsed by parse_charsets(). Returns the 'q' quality
-- parameter of the best match, 0 if no match was found. This function -- parameter of the best match, 0 if no match was found. This function
-- bahaves the same as quality() -- bahaves the same as quality()
do do
Result := fitness_and_quality_parsed (a_field, parsed_common).quality Result := fitness_and_quality_from_list (a_field, a_parsed_common).quality
end 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.force (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 note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"

View File

@@ -1,14 +1,15 @@
note note
description: "{MIME_TYPE_PARSER_UTILITIES}." description: "Summary description for {HTTP_HEADER_PARSER}."
author: ""
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
class deferred class
MIME_TYPE_PARSER_UTILITIES HTTP_HEADER_PARSER
feature {NONE} -- Implementation feature {NONE} -- Helpers
mime_type (a_str: READABLE_STRING_8): READABLE_STRING_8 entity_value (a_str: READABLE_STRING_8): READABLE_STRING_8
-- `s' with any trailing parameters stripped -- `s' with any trailing parameters stripped
local local
p: INTEGER p: INTEGER
@@ -21,20 +22,17 @@ feature {NONE} -- Implementation
end end
end end
trim (a_string: READABLE_STRING_8): READABLE_STRING_8 trim (a_string: READABLE_STRING_8): STRING_8
-- trim whitespace from the beginning and end of a string -- trim whitespace from the beginning and end of a string
-- `a_string' -- `a_string'
require require
valid_argument : a_string /= Void valid_argument : a_string /= Void
local
l_result: STRING
do do
l_result := a_string.as_string_8 create Result.make_from_string (a_string)
l_result.left_adjust Result.left_adjust
l_result.right_adjust Result.right_adjust
Result := l_result
ensure ensure
result_same_as_argument: Result.same_string_general (a_string) result_trimmed: a_string.has_substring (Result)
end end
note note

View File

@@ -1,312 +0,0 @@
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.force (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

View File

@@ -0,0 +1,251 @@
note
description: "Object that represents a result after parsing Language Headers."
date: "$Date$"
revision: "$Revision$"
class
HTTP_ACCEPT_LANGUAGE
inherit
REFACTORING_HELPER
DEBUG_OUTPUT
create
make_from_string,
make,
make_with_language,
make_default
feature {NONE} -- Initialization
make_from_string (a_accept_language_item: READABLE_STRING_8)
-- Instantiate Current from part of accept-language header, i.e language tag and parameters.
--
-- Languages-ranges are languages with specialization and a 'q' quality parameter.
-- For example, the language l_range ('en-* ;q=0.5') would get parsed into:
-- ('en', '*', {'q', '0.5'})
-- In addition this also guarantees that there is a value for 'q'
-- in the params dictionary, filling it in with a proper default if
-- necessary.
require
a_accept_language_item_not_empty: not a_accept_language_item.is_empty
local
l_parts: LIST [READABLE_STRING_8]
p: READABLE_STRING_8
i: INTEGER
l_tag: STRING_8
do
fixme (generator + ".make_from_string: improve code!!!")
l_parts := a_accept_language_item.split (';')
from
l_parts.start
make_with_language (trimmed_string (l_parts.item))
if not l_parts.after then
l_parts.forth
end
until
l_parts.after
loop
p := l_parts.item
i := p.index_of ('=', 1)
if i > 0 then
put (trimmed_string (p.substring (i + 1, p.count)), trimmed_string (p.substring (1, i - 1)))
else
check is_well_formed_parameter: False end
end
l_parts.forth
end
check quality_initialized_to_1: quality = 1.0 end
-- Get quality from parameter if any, and format the value as expected.
if attached item ("q") as q then
if q.same_string ("1") then
--| Use 1.0 formatting
put ("1.0", "q")
elseif q.is_double and then attached q.to_real_64 as r then
if r <= 0.0 then
quality := 0.0 --| Should it be 1.0 ?
elseif r >= 1.0 then
quality := 1.0
else
quality := r
end
else
put ("1.0", "q")
quality := 1.0
end
else
put ("1.0", "q")
end
end
make_with_language (a_lang_tag: READABLE_STRING_8)
-- Instantiate Current from language tag `a_lang_tag'.
local
i: INTEGER
do
initialize
create language_range.make_from_string (a_lang_tag)
i := a_lang_tag.index_of ('-', 1)
if i > 0 then
language := a_lang_tag.substring (1, i - 1)
specialization := a_lang_tag.substring (i + 1, a_lang_tag.count)
else
language := a_lang_tag
end
ensure
language_range_set: language_range.same_string (a_lang_tag) and a_lang_tag /= language_range
end
make (a_root_lang: READABLE_STRING_8; a_specialization: detachable READABLE_STRING_8)
-- Instantiate Current with `a_root_lang' and `a_specialization'.
do
initialize
create language_range.make_empty
language := a_root_lang
specialization := a_specialization
update_language_range (a_root_lang, a_specialization)
end
make_default
-- Instantiate Current with default "*" language.
do
make ("*", Void)
end
initialize
-- Initialize Current
do
create params.make (2)
quality := 1.0
end
feature -- Access
language_range: STRING_8
-- language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
language: READABLE_STRING_8
-- First part of the language range, i.e the root language
specialization: detachable READABLE_STRING_8
-- Optional second part of the language range, i.e the dialect, or specialized language type
quality: REAL_64
-- Associated quality, by default 1.0
feature -- Status report
debug_output: STRING
-- String that should be displayed in debugger to represent `Current'.
do
create Result.make_from_string (language_range)
Result.append_character (';')
Result.append ("q=")
Result.append_double (quality)
end
feature -- Parameters
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
params: HASH_TABLE [STRING, STRING]
-- dictionary of all the parameters for the media range
feature -- Status Report
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_language (a_root_lang: READABLE_STRING_8)
-- Set `'anguage' with `a_root_lang'
require
a_root_lang_attached: a_root_lang /= Void
do
language := a_root_lang
update_language_range (a_root_lang, specialization)
ensure
type_assigned: language ~ a_root_lang
end
set_specialization (a_specialization: detachable READABLE_STRING_8)
-- Set `specialization' with `a_specialization'
do
specialization := a_specialization
update_language_range (language, a_specialization)
ensure
specialization_assigned: specialization ~ a_specialization
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 {NONE} -- Implementation
update_language_range (a_lang: like language; a_specialization: like specialization)
-- Update `language_range' with `a_lang' and `a_specialization'
local
l_language_range: like language_range
do
l_language_range := language_range -- Reuse same object, be careful not to keep reference on existing string at first.
l_language_range.wipe_out
l_language_range.append (a_lang)
if a_specialization /= Void then
l_language_range.append_character ('-')
l_language_range.append (a_specialization)
end
end
feature {NONE} -- Helper
trimmed_string (s: READABLE_STRING_8): STRING_8
-- Copy of `s', where whitespace were stripped from the beginning and end of the string
do
create Result.make_from_string (s)
Result.left_adjust
Result.right_adjust
end
invariant
valid_quality: 0.0 <= quality and quality <= 1.0
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

View File

@@ -1,10 +1,10 @@
note note
description: "Object that represents a results after parsing Charset or Encoding Accept headers." description: "Object that represents a results after parsing Accept-* headers."
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
class class
COMMON_RESULTS HTTP_ANY_ACCEPT_HEADER
create create
make make
@@ -36,9 +36,8 @@ feature -- Access
Result := res Result := res
end end
params: HASH_TABLE [STRING, STRING] params: HASH_TABLE [STRING, STRING]
--dictionary of all the parameters for the media range -- Table of all parameters for the media range
feature -- Status Report feature -- Status Report
@@ -51,7 +50,7 @@ feature -- Status Report
feature -- Element change feature -- Element change
set_field (a_field: STRING) set_field (a_field: STRING)
-- Set type with `a_charset' -- Set type with `a_field'
do do
field := a_field field := a_field
ensure ensure

View File

@@ -1,112 +0,0 @@
note
description: "Object that represents a result after parsing Language Headers."
date: "$Date$"
revision: "$Revision$"
class
LANGUAGE_RESULTS
create
make
feature -- Initialization
make
--Create an object LANGUAGE_RESULTS.
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
params: HASH_TABLE [STRING, STRING]
--dictionary of all the parameters for the media range
feature -- Status Report
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
debug_output: STRING
-- String that should be displayed in debugger to represent `Current'.
do
Result := out
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

View File

@@ -8,18 +8,18 @@ class
feature feature
Mime: MIME_PARSE Mime: HTTP_ACCEPT_MEDIA_TYPE_PARSER
once once
create Result create Result
end end
Common: COMMON_ACCEPT_HEADER_PARSER Common: HTTP_ANY_ACCEPT_HEADER_PARSER
-- Charset and Encoding -- Charset and Encoding
once once
create Result create Result
end end
Language: LANGUAGE_PARSE Language: HTTP_ACCEPT_LANGUAGE_PARSER
once once
create Result create Result
end end

View File

@@ -1,6 +1,6 @@
note note
description: "[ description: "[
{CHARACTER_ENCODING_VARIANT_RESULTS} {HTTP_ACCEPT_CHARSET_VARIANTS}
Represent the character sets results between client preferences and character sets variants supported by the server. 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 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. a response with the list of supported character sets.
@@ -10,20 +10,22 @@ note
revision: "$Revision$" revision: "$Revision$"
class class
CHARACTER_ENCODING_VARIANT_RESULTS HTTP_ACCEPT_CHARSET_VARIANTS
inherit inherit
HTTP_ACCEPT_VARIANTS
rename
variant_value as charset
end
VARIANT_RESULTS create
make
feature -- Change
feature -- Change Element set_vary_header_value
set_variant_header
-- Set variant header as `Accept-Charset'
do do
variant_header := {HTTP_HEADER_NAMES}.header_accept_charset -- "Accept-Charset" vary_header_value := {HTTP_HEADER_NAMES}.header_accept_charset -- "Accept-Charset"
end end
note note

View File

@@ -1,28 +1,31 @@
note note
description: "[ description: "[
{COMPRESSION_VARIANT_RESULTS} {HTTP_ACCEPT_ENCODING_VARIANTS}
Represent the compression results between client preferences and ccompression variants supported by the server. Represent the encoding results between client preferences and encoding variants supported by the server.
If the server is unable to supports the requested Accept-Encoding values, the server can build 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 a response with the list of supported encodings
]" ]"
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"
EIS: "name= Compression", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" EIS: "name= Compression", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri"
class class
COMPRESSION_VARIANT_RESULTS HTTP_ACCEPT_ENCODING_VARIANTS
inherit inherit
HTTP_ACCEPT_VARIANTS
rename
variant_value as encoding
end
VARIANT_RESULTS create
make
feature -- Change
feature -- Change Element set_vary_header_value
set_variant_header
-- Set variant_header as `Accept-Encoding'
do do
variant_header := {HTTP_HEADER_NAMES}.header_accept_encoding -- "Accept-Encoding" vary_header_value := {HTTP_HEADER_NAMES}.header_accept_encoding -- "Accept-Encoding"
end end
note note

View File

@@ -1,6 +1,6 @@
note note
description: "[ description: "[
{LANGUAGE_VARIANT_RESULTS}. {HTTP_ACCEPT_LANGUAGE_VARIANTS}.
Represent the language results between client preferences and language variants supported by the server. 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 If the server is unable to supports the requested Accept-Language values, the server can build
a response with the list of supported languages a response with the list of supported languages
@@ -9,18 +9,22 @@ note
revision: "$Revision$" revision: "$Revision$"
class class
LANGUAGE_VARIANT_RESULTS HTTP_ACCEPT_LANGUAGE_VARIANTS
inherit inherit
HTTP_ACCEPT_VARIANTS
rename
variant_value as language
end
VARIANT_RESULTS create
make
feature -- Change Element feature -- Change
set_variant_header set_vary_header_value
-- Set variant header as 'Accept-Language'
do do
variant_header := {HTTP_HEADER_NAMES}.header_accept_language -- "Accept-Language" vary_header_value := {HTTP_HEADER_NAMES}.header_accept_language -- "Accept-Language"
end end
note note

View File

@@ -1,7 +1,7 @@
note note
description: "[ description: "[
{MEDIA_TYPE_VARIANT_RESULTS}. {HTTP_ACCEPT_MEDIA_TYPE_VARIANTS}.
Represent the media type results between client preferences and media type variants supported by the server.. Represents 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 If the server is unable to supports the requested Accept values, the server can build
a response with the list of supported representations a response with the list of supported representations
]" ]"
@@ -9,18 +9,22 @@ note
revision: "$Revision$" revision: "$Revision$"
class class
MEDIA_TYPE_VARIANT_RESULTS HTTP_ACCEPT_MEDIA_TYPE_VARIANTS
inherit inherit
HTTP_ACCEPT_VARIANTS
rename
variant_value as media_type
end
VARIANT_RESULTS create
make
feature -- Change Element feature -- Change
set_variant_header set_vary_header_value
-- Set variant header as `Accept'
do do
variant_header := {HTTP_HEADER_NAMES}.header_accept -- "Accept" vary_header_value := {HTTP_HEADER_NAMES}.header_accept -- "Accept"
end end
note note

View File

@@ -0,0 +1,92 @@
note
description: "Generic {HTTP_ACCEPT_VARIANTS}.with common functionality to most header variants.."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTP_ACCEPT_VARIANTS
feature {NONE} -- Initialization
make
do
end
feature -- Change
set_vary_header_value
-- Set the `vary_header_value'
deferred
ensure
is_valid_header_set : is_valid_header_name (vary_header_value)
end
feature -- Access
vary_header_value: detachable READABLE_STRING_8
-- Name of header to be added to the Vary header of the response
-- this indicates the Accept-* header source of the matched `variant_value' if any,
-- if this is using the default, the `vary_header_value' is Void.
supported_variants: detachable LIST [READABLE_STRING_8]
-- Set of supported variants for the response
variant_value: detachable READABLE_STRING_8
-- Associated value, it could be value of:
-- content type
-- language
-- character set
-- encoding.
feature -- Status_Report
is_acceptable: BOOLEAN
-- is the current variant accepted?
is_valid_header_name (a_header_name: detachable READABLE_STRING_8): BOOLEAN
-- is `a_header_name' a valid accept header name?
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"
do
if a_header_name /= Void then
Result := a_header_name.same_string ({HTTP_HEADER_NAMES}.header_accept) -- "Accept",
or else a_header_name.same_string ({HTTP_HEADER_NAMES}.header_accept_language) -- "Accept-Language",
or else a_header_name.same_string ({HTTP_HEADER_NAMES}.header_accept_encoding) -- "Accept-Encoding",
or else a_header_name.same_string ({HTTP_HEADER_NAMES}.header_accept_charset) -- "Accept-Charset"
end
end
feature -- Change Element
set_variant_value (v: READABLE_STRING_8)
-- Set `variant_value' as `v'
do
variant_value := v
ensure
type_set: attached variant_value as l_variant implies l_variant = v
end
set_acceptable (b: BOOLEAN)
-- Set `is_acceptable' with `b'
do
is_acceptable := b
ensure
is_acceptable_set: is_acceptable = b
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

View File

@@ -1,91 +0,0 @@
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
-- Associated type, it could be:
-- media type
-- language
-- character_sets
-- 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:= <<
{HTTP_HEADER_NAMES}.header_accept, -- "Accept",
{HTTP_HEADER_NAMES}.header_accept_language, -- "Accept-Language",
{HTTP_HEADER_NAMES}.header_accept_encoding, -- "Accept-Encoding",
{HTTP_HEADER_NAMES}.header_accept_charset --"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

View File

@@ -16,10 +16,10 @@ feature {NONE} -- Initialization
make make
local local
mime_parse : MIME_PARSE mime_parse : HTTP_ACCEPT_MEDIA_TYPE_PARSER
accept : STRING accept : STRING
charset_parse : COMMON_ACCEPT_HEADER_PARSER charset_parse : HTTP_ANY_ACCEPT_HEADER_PARSER
language : LANGUAGE_PARSE language : HTTP_ACCEPT_LANGUAGE_PARSER
do do
create mime_parse create mime_parse
-- parse_result := mime_parse.parse_mime_type ("application/xhtml;q=0.5") -- parse_result := mime_parse.parse_mime_type ("application/xhtml;q=0.5")
@@ -59,12 +59,12 @@ feature {NONE} -- Initialization
print ("%N"+mime_parse.quality ("*/*;q=0.1", accept).out) print ("%N"+mime_parse.quality ("*/*;q=0.1", accept).out)
accept := "application/atom+xml" accept := "application/atom+xml"
print ("%N"+mime_parse.parse_mime_type (accept).out) print ("%N"+mime_parse.media_type (accept).out)
create charset_parse create charset_parse
accept := "iso-8859-5" accept := "iso-8859-5"
print ("%N" + charset_parse.parse_common (accept).out) print ("%N" + charset_parse.header (accept).out)
accept := "unicode-1-1;q=0.8" accept := "unicode-1-1;q=0.8"
print ("%N" + charset_parse.parse_common (accept).out) print ("%N" + charset_parse.header (accept).out)
accept:= "iso-8859-5, unicode-1-1;q=0.8" accept:= "iso-8859-5, unicode-1-1;q=0.8"
@@ -78,10 +78,10 @@ feature {NONE} -- Initialization
print (language.best_match (accept.split (','), "da")) print (language.best_match (accept.split (','), "da"))
print (language.best_match (accept.split (','), "en-*")) print (language.best_match (accept.split (','), "en-*"))
print ("%N"+language.parse_language_range ("da").out) print ("%N"+language.accept_language ("da").out)
print ("%N"+language.parse_language_range ("en-gb;q=0.8").out) print ("%N"+language.accept_language ("en-gb;q=0.8").out)
print ("%N"+language.parse_language_range ("en;q=0.7").out) print ("%N"+language.accept_language ("en;q=0.7").out)
print ("%N"+language.parse_language_range ("en-*").out) print ("%N"+language.accept_language ("en-*").out)
end end
end end

View File

@@ -23,7 +23,7 @@ feature {NONE} -- Events
feature -- Helpers feature -- Helpers
format (a_common: COMMON_RESULTS): STRING format (a_common: HTTP_ANY_ACCEPT_HEADER): STRING
-- Representation of the current object -- Representation of the current object
do do
create Result.make_from_string ("(") create Result.make_from_string ("(")
@@ -47,9 +47,9 @@ feature -- Test routines
test_parse_charsets test_parse_charsets
do do
assert ("Expected ('iso-8859-5', {'q':'1.0',})", format (parser.parse_common("iso-8859-5")).same_string("('iso-8859-5', {'q':'1.0',})") ) assert ("Expected ('iso-8859-5', {'q':'1.0',})", format (parser.header("iso-8859-5")).same_string("('iso-8859-5', {'q':'1.0',})") )
assert ("Expected ('unicode-1-1', {'q':'0.8',})", format (parser.parse_common("unicode-1-1;q=0.8")).same_string("('unicode-1-1', {'q':'0.8',})") ) assert ("Expected ('unicode-1-1', {'q':'0.8',})", format (parser.header("unicode-1-1;q=0.8")).same_string("('unicode-1-1', {'q':'0.8',})") )
assert ("Expected ('*', {'q':'1.0',})", format (parser.parse_common("*")).same_string("('*', {'q':'1.0',})") ) assert ("Expected ('*', {'q':'1.0',})", format (parser.header("*")).same_string("('*', {'q':'1.0',})") )
end end
@@ -74,6 +74,6 @@ feature -- Test routines
assert ("Expected unicode-1-1", parser.best_match (charset_supported, "unicode-1-1;q=1").same_string ("unicode-1-1")) assert ("Expected unicode-1-1", parser.best_match (charset_supported, "unicode-1-1;q=1").same_string ("unicode-1-1"))
end end
parser : COMMON_ACCEPT_HEADER_PARSER parser : HTTP_ANY_ACCEPT_HEADER_PARSER
end end

View File

@@ -24,7 +24,7 @@ feature {NONE} -- Events
feature -- Test routines feature -- Test routines
test_media_type_negotiation test_media_type_negotiation
local local
media_variants : MEDIA_TYPE_VARIANT_RESULTS media_variants : HTTP_ACCEPT_MEDIA_TYPE_VARIANTS
mime_types_supported : LIST [STRING] mime_types_supported : LIST [STRING]
l_types : STRING l_types : STRING
do do
@@ -39,22 +39,22 @@ feature -- Test routines
else else
assert ("Has supported_variants results", False) assert ("Has supported_variants results", False)
end end
assert ("Variant header is void",media_variants.variant_header = Void) assert ("Variant Header", attached media_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept"))
assert ("Media type is void",media_variants.type = Void) assert ("Media type is void",media_variants.media_type = Void)
-- Scenario 2, the client doesnt send values in the header, Accept: -- Scenario 2, the client doesnt send values in the header, Accept:
media_variants := conneg.media_type_preference (mime_types_supported, "") media_variants := conneg.media_type_preference (mime_types_supported, "")
assert ("Expected Acceptable", media_variants.is_acceptable) assert ("Expected Acceptable", media_variants.is_acceptable)
assert ("Variants is dettached",media_variants.supported_variants = Void) assert ("Variants is set",media_variants.supported_variants = mime_types_supported)
assert ("Mime is default", attached media_variants.type as l_type and then conneg.mime_default.is_equal (l_type)) assert ("Mime is default", attached media_variants.media_type as l_media_type and then conneg.default_media_type.same_string (l_media_type))
assert ("Variant header", media_variants.variant_header = Void) assert ("Variant header", media_variants.vary_header_value = Void)
--Scenario 3, the server select the best match, and set the vary header --Scenario 3, the server select the best match, and set the vary header
media_variants := conneg.media_type_preference (mime_types_supported, "text/*,application/json;q=0.5") media_variants := conneg.media_type_preference (mime_types_supported, "text/*,application/xml;q=0.5,application/json;q=0.6")
assert ("Expected Acceptable", media_variants.is_acceptable) assert ("Expected Acceptable", media_variants.is_acceptable)
assert ("Variants is dettached",media_variants.supported_variants = Void) assert ("Variants is set",media_variants.supported_variants = mime_types_supported)
assert ("Variant Header", attached media_variants.variant_header as l_variant_header and then l_variant_header.same_string ("Accept")) assert ("Variant Header", attached media_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept"))
assert ("Media Type is application/json", attached media_variants.type as l_type and then l_type.same_string ("application/json")) assert ("Media Type is application/json", attached media_variants.media_type as l_media_type and then l_media_type.same_string ("application/json"))
end end
@@ -62,13 +62,13 @@ feature -- Test routines
test_charset_negotiation test_charset_negotiation
local local
charset_variants : CHARACTER_ENCODING_VARIANT_RESULTS charset_variants : HTTP_ACCEPT_CHARSET_VARIANTS
charset_supported : LIST [STRING] charset_supported : LIST [STRING]
l_charset : STRING l_charset_value : STRING
do do
-- Scenario 1, the server side does not support client preferences -- Scenario 1, the server side does not support client preferences
l_charset := "UTF-8, iso-8859-5" l_charset_value := "UTF-8, iso-8859-5"
charset_supported := l_charset.split(',') charset_supported := l_charset_value.split(',')
charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1") charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1")
assert ("Expected Not Acceptable", not charset_variants.is_acceptable) assert ("Expected Not Acceptable", not charset_variants.is_acceptable)
if attached charset_variants.supported_variants as l_supported_variants then if attached charset_variants.supported_variants as l_supported_variants then
@@ -77,29 +77,29 @@ feature -- Test routines
else else
assert("Has supported_variants results", False) assert("Has supported_variants results", False)
end end
assert ("Variant header is void",charset_variants.variant_header = Void) assert ("Variant Header", attached charset_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Charset"))
assert ("Character type is void",charset_variants.type = Void) assert ("Character type is void",charset_variants.charset = Void)
-- Scenario 2, the client doesnt send values in the header, Accept-Charset: -- Scenario 2, the client doesnt send values in the header, Accept-Charset:
charset_variants := conneg.charset_preference (charset_supported, "") charset_variants := conneg.charset_preference (charset_supported, "")
assert ("Expected Acceptable", charset_variants.is_acceptable) assert ("Expected Acceptable", charset_variants.is_acceptable)
assert ("Variants is dettached",charset_variants.supported_variants = Void) assert ("Variants is set",charset_variants.supported_variants = charset_supported)
assert ("Charset is defaul", attached charset_variants.type as l_type and then conneg.charset_default.is_equal (l_type)) assert ("Charset is default", attached charset_variants.charset as l_charset and then conneg.default_charset.same_string (l_charset))
assert ("Variant header", charset_variants.variant_header = Void) assert ("Variant header", charset_variants.vary_header_value = Void)
--Scenario 3, the server select the best match, and set the vary header --Scenario 3, the server select the best match, and set the vary header
charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1, UTF-8;q=0.3, iso-8859-5") charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1, UTF-8;q=0.3, iso-8859-5")
assert ("Expected Acceptable", charset_variants.is_acceptable) assert ("Expected Acceptable", charset_variants.is_acceptable)
assert ("Variants is dettached",charset_variants.supported_variants = Void) assert ("Variants is set",charset_variants.supported_variants = charset_supported)
assert ("Variant Header", attached charset_variants.variant_header as l_variant_header and then l_variant_header.same_string ("Accept-Charset")) assert ("Variant Header", attached charset_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Charset"))
assert ("Character Type is iso-8859-5", attached charset_variants.type as l_type and then l_type.same_string ("iso-8859-5")) assert ("Character Type is iso-8859-5", attached charset_variants.charset as l_charset and then l_charset.same_string ("iso-8859-5"))
end end
test_compression_negotiation test_compression_negotiation
local local
compression_variants : COMPRESSION_VARIANT_RESULTS compression_variants : HTTP_ACCEPT_ENCODING_VARIANTS
compression_supported : LIST [STRING] compression_supported : LIST [STRING]
l_compression : STRING l_compression : STRING
do do
@@ -109,39 +109,39 @@ feature -- Test routines
compression_variants := conneg.encoding_preference (compression_supported, "gzip") compression_variants := conneg.encoding_preference (compression_supported, "gzip")
assert ("Expected Not Acceptable", not compression_variants.is_acceptable) assert ("Expected Not Acceptable", not compression_variants.is_acceptable)
if attached compression_variants.supported_variants as l_supported_variants then if attached compression_variants.supported_variants as l_supported_variants then
assert ("Same Value at 1",compression_supported.at (1).is_equal (l_supported_variants.first)) assert ("Same Value at 1",compression_supported.first.same_string (l_supported_variants.first))
assert ("Same count",compression_supported.count = l_supported_variants.count) assert ("Same count",compression_supported.count = l_supported_variants.count)
else else
assert ("Has supported_variants results", False) assert ("Has supported_variants results", False)
end end
assert ("Variant header is void",compression_variants.variant_header = Void) assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding"))
assert ("Compression type is void",compression_variants.type = Void) assert ("Compression type is void",compression_variants.encoding = Void)
-- Scenario 2, the client doesnt send values in the header, Accept-Encoding -- Scenario 2, the client doesnt send values in the header, Accept-Encoding
compression_variants := conneg.encoding_preference (compression_supported, "") compression_variants := conneg.encoding_preference (compression_supported, "")
assert ("Expected Acceptable", compression_variants.is_acceptable) assert ("Expected Acceptable", compression_variants.is_acceptable)
assert ("Variants is dettached",compression_variants.supported_variants = Void) assert ("Variants is set",compression_variants.supported_variants = compression_supported)
assert ("Compression is defaul", attached compression_variants.type as l_type and then conneg.encoding_default.same_string (l_type)) assert ("Compression is default", attached compression_variants.encoding as l_encoding and then conneg.default_encoding.same_string (l_encoding))
assert ("Variant header", compression_variants.variant_header = Void) assert ("Variant header", compression_variants.vary_header_value = Void)
--Scenario 3, the server select the best match, and set the vary header --Scenario 3, the server select the best match, and set the vary header
l_compression := "gzip" l_compression := "gzip"
compression_supported := l_compression.split(',') compression_supported := l_compression.split(',')
conneg.set_encoding_default("gzip") conneg.set_default_encoding("gzip")
compression_variants := conneg.encoding_preference (compression_supported, "compress,gzip;q=0.7") compression_variants := conneg.encoding_preference (compression_supported, "compress,gzip;q=0.7")
assert ("Expected Acceptable", compression_variants.is_acceptable) assert ("Expected Acceptable", compression_variants.is_acceptable)
assert ("Variants is dettached",compression_variants.supported_variants = Void) assert ("Variants is set",compression_variants.supported_variants = compression_supported)
assert ("Variant Header", attached compression_variants.variant_header as l_variant_header and then l_variant_header.same_string ("Accept-Encoding")) assert ("Variant Header", attached compression_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Encoding"))
assert ("Encoding Type is gzip", attached compression_variants.type as l_type and then l_type.same_string ("gzip")) assert ("Encoding Type is gzip", attached compression_variants.encoding as l_type and then l_type.same_string ("gzip"))
end end
test_language_negotiation test_language_negotiation
local local
language_variants : LANGUAGE_VARIANT_RESULTS language_variants : HTTP_ACCEPT_LANGUAGE_VARIANTS
languages_supported : LIST [STRING] languages_supported : LIST [STRING]
l_languages : STRING l_languages : STRING
do do
@@ -150,8 +150,8 @@ feature -- Test routines
languages_supported := l_languages.split(',') languages_supported := l_languages.split(',')
language_variants := conneg.language_preference (languages_supported, "de") language_variants := conneg.language_preference (languages_supported, "de")
assert ("Expected Not Acceptable", not language_variants.is_acceptable) assert ("Expected Not Acceptable", not language_variants.is_acceptable)
assert ("Variant header is void",language_variants.variant_header = Void) assert ("Variant Header", attached language_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Language"))
assert ("Language type is void",language_variants.type = Void) assert ("Language type is Void",language_variants.language = Void)
if attached language_variants.supported_variants as l_supported_variants then if attached language_variants.supported_variants as l_supported_variants then
assert ("Same Value at 1", languages_supported.first.same_string (l_supported_variants.first)) assert ("Same Value at 1", languages_supported.first.same_string (l_supported_variants.first))
assert ("Same count",languages_supported.count = l_supported_variants.count) assert ("Same count",languages_supported.count = l_supported_variants.count)
@@ -159,21 +159,19 @@ feature -- Test routines
assert ("Has supported variants results", False) assert ("Has supported variants results", False)
end end
-- Scenario 2, the client doesnt send values in the header, Accept-Language: -- Scenario 2, the client doesnt send values in the header, Accept-Language:
language_variants := conneg.language_preference (languages_supported, "") language_variants := conneg.language_preference (languages_supported, "")
assert ("Expected Acceptable", language_variants.is_acceptable) assert ("Expected Acceptable", language_variants.is_acceptable)
assert ("Variants is dettached",language_variants.supported_variants = Void) assert ("Variants is attached",language_variants.supported_variants = languages_supported)
assert ("Language is default", attached language_variants.type as l_type and then conneg.language_default.same_string (l_type)) assert ("Language is default", attached language_variants.language as l_lang and then conneg.default_language.same_string (l_lang))
assert ("Variant header", language_variants.variant_header = Void) assert ("Variant header", language_variants.vary_header_value = Void)
--Scenario 3, the server select the best match, and set the vary header --Scenario 3, the server select the best match, and set the vary header
language_variants := conneg.language_preference (languages_supported, "fr,es;q=0.4") language_variants := conneg.language_preference (languages_supported, "fr,es;q=0.4")
assert ("Expected Acceptable", language_variants.is_acceptable) assert ("Expected Acceptable", language_variants.is_acceptable)
assert ("Variants is dettached",language_variants.supported_variants = Void) assert ("Variants is detached",language_variants.supported_variants = languages_supported)
assert ("Variant Header", attached language_variants.variant_header as l_variant_header and then l_variant_header.same_string ("Accept-Language")) assert ("Variant Header", attached language_variants.vary_header_value as l_variant_header and then l_variant_header.same_string ("Accept-Language"))
assert ("Language Type is fr", attached language_variants.type as l_type and then l_type.same_string ("fr")) assert ("Language Type is fr", attached language_variants.language as l_lang and then l_lang.same_string ("fr"))
end end
feature -- Implementation feature -- Implementation

View File

@@ -0,0 +1,5 @@
Catcall detected in {READABLE_STRING_8}.make_from_string for arg#1: expected READABLE_STRING_8 but got Void
Catcall detected in {READABLE_STRING_8}.make_from_string for arg#1: expected READABLE_STRING_8 but got Void
Catcall detected in {READABLE_STRING_8}.make_from_string for arg#1: expected READABLE_STRING_8 but got Void
Catcall detected in {READABLE_STRING_8}.make_from_string for arg#1: expected READABLE_STRING_8 but got Void
Catcall detected in {READABLE_STRING_8}.make_from_string for arg#1: expected READABLE_STRING_8 but got Void

View File

@@ -23,14 +23,14 @@ feature {NONE} -- Events
feature -- Helpers feature -- Helpers
format (a_language: LANGUAGE_RESULTS): STRING format (a_language: HTTP_ACCEPT_LANGUAGE): STRING
-- Representation of the current object -- Representation of the current object
do do
create Result.make_from_string ("(") create Result.make_from_string ("(")
if attached a_language.type as t then if attached a_language.language as t then
Result.append_string ("'" + t + "',") Result.append_string ("'" + t + "',")
end end
if attached a_language.sub_type as st then if attached a_language.specialization as st then
Result.append_string (" '" + st + "',") Result.append_string (" '" + st + "',")
end end
Result.append_string (" {") Result.append_string (" {")
@@ -52,10 +52,10 @@ feature -- Test routines
test_parse_language test_parse_language
do do
assert ("Expected ('da', {'q':'1.0',})", format (parser.parse_language_range ("da")).same_string ("('da', {'q':'1.0',})")); assert ("Expected ('da', {'q':'1.0',})", format (parser.accept_language ("da")).same_string ("('da', {'q':'1.0',})"));
assert ("Expected ('en', 'gb', {'q':'0.8',})", format (parser.parse_language_range ("en-gb;q=0.8")).same_string ("('en', 'gb', {'q':'0.8',})")); assert ("Expected ('en', 'gb', {'q':'0.8',})", format (parser.accept_language ("en-gb;q=0.8")).same_string ("('en', 'gb', {'q':'0.8',})"));
assert ("Expected ('en', {'q':'0.7',})", format (parser.parse_language_range ("en;q=0.7")).same_string ("('en', {'q':'0.7',})")); assert ("Expected ('en', {'q':'0.7',})", format (parser.accept_language ("en;q=0.7")).same_string ("('en', {'q':'0.7',})"));
assert ("Expected ('en', '*', {'q':'1.0',})", format (parser.parse_language_range ("en-*")).same_string ("('en', '*', {'q':'1.0',})")); assert ("Expected ('en', '*', {'q':'1.0',})", format (parser.accept_language ("en-*")).same_string ("('en', '*', {'q':'1.0',})"));
end end
@@ -73,53 +73,53 @@ feature -- Test routines
test_best_match test_best_match
local local
mime_types_supported : LIST [STRING] langs_supported : LIST [STRING]
l_types : STRING l_types : STRING
do do
l_types := "en-gb,en-us" l_types := "en-gb,en-us"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Expected en-us", parser.best_match (mime_types_supported, "en-us").same_string ("en-us")) assert ("Expected en-us", parser.best_match (langs_supported, "en-us").same_string ("en-us"))
assert ("Direct match with a q parameter", parser.best_match (mime_types_supported, "en-gb;q=1").same_string ("en-gb")) assert ("Direct match with a q parameter", parser.best_match (langs_supported, "en-gb;q=1").same_string ("en-gb"))
assert ("Direct match second choice with a q parameter", parser.best_match (mime_types_supported, "en-us;q=1").same_string ("en-us")) assert ("Direct match second choice with a q parameter", parser.best_match (langs_supported, "en-us;q=1").same_string ("en-us"))
assert ("Direct match using a subtype wildcard", parser.best_match (mime_types_supported, "en-*;q=1").is_equal ("en-gb")) assert ("Direct match using a subtype wildcard", parser.best_match (langs_supported, "en-*;q=1").is_equal ("en-gb"))
assert ("Match using a type wildcard", parser.best_match (mime_types_supported, "*").same_string ("en-gb")) assert ("Match using a type wildcard", parser.best_match (langs_supported, "*").same_string ("en-gb"))
l_types := "en-gb,es" l_types := "en-gb,es"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Match using a type versus a lower weighted subtype", parser.best_match (mime_types_supported, "es-*;q=0.5,*;q=0.1").same_string ("es")) assert ("Match using a type versus a lower weighted subtype", parser.best_match (langs_supported, "es-*;q=0.5,*;q=0.1").same_string ("es"))
assert ("Fail to match anything",parser.best_match (mime_types_supported, "fr; q=0.9").same_string ("")) assert ("Fail to match anything",parser.best_match (langs_supported, "fr; q=0.9").same_string (""))
l_types := "en-gb,en-us" l_types := "en-gb,en-us"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Test 1 verify fitness ordering", parser.best_match (mime_types_supported, "en-gb,en-us,*").same_string ("en-gb")) assert ("Test 1 verify fitness ordering", parser.best_match (langs_supported, "en-gb,en-us,*").same_string ("en-gb"))
l_types := "es,en-gb;q=1.0,fr;q=0.6" l_types := "es,en-gb;q=1.0,fr;q=0.6"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Match default es at first position", parser.best_match (mime_types_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es")) assert ("Match default es at first position", parser.best_match (langs_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es"))
l_types := "en-gb;q=1.0,fr;q=0.6,es" l_types := "en-gb;q=1.0,fr;q=0.6,es"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Match default es at last position", parser.best_match (mime_types_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es")) assert ("Match default es at last position", parser.best_match (langs_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es"))
l_types := "en-gb;q=1.0,fr,es" l_types := "en-gb;q=1.0,fr,es"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Match first top quality and fitness", parser.best_match (mime_types_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es")) assert ("Match first top quality and fitness", parser.best_match (langs_supported, "es;q=1.0,*;q=0.1,fr").same_string ("es"))
l_types := "fr;q=1.0,en,es" l_types := "fr;q=1.0,en,es"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Test 1", parser.best_match (mime_types_supported, "es;q=1.0,*/*;q=0.1,en;q=0.9").same_string ("es")) assert ("Test 1", parser.best_match (langs_supported, "es;q=1.0,*/*;q=0.1,en;q=0.9").same_string ("es"))
l_types := "fr;q=1.0,en,es" l_types := "fr;q=1.0,en,es"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Test 1", parser.best_match (mime_types_supported, "es,*/*;q=0.1,en").same_string ("es")) assert ("Test 1", parser.best_match (langs_supported, "es,*/*;q=0.1,en").same_string ("es"))
l_types := "fr;q=1.0,en,es" l_types := "fr;q=1.0,en,es"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Test 2", parser.best_match (mime_types_supported, "en,es,*/*;q=0.1").same_string ("en")) assert ("Test 2", parser.best_match (langs_supported, "en,es,*/*;q=0.1").same_string ("en"))
l_types := "es,en;q=0.6" l_types := "es,en;q=0.6"
mime_types_supported := l_types.split(',') langs_supported := l_types.split(',')
assert ("Test 2", parser.best_match (mime_types_supported, "fr;q=1.0, en;q=0.6, es").same_string ("es")) assert ("Test 2", parser.best_match (langs_supported, "fr;q=1.0, en;q=0.6, es").same_string ("es"))
end end
@@ -137,7 +137,7 @@ feature -- Test routines
parser : LANGUAGE_PARSE parser : HTTP_ACCEPT_LANGUAGE_PARSER
end end

View File

@@ -54,15 +54,15 @@ feature -- Test routines
test_parse_media_range test_parse_media_range
do do
assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.parse_media_range("application/xml;q=1")).same_string("('application', 'xml', {'q':'1.0',})") ) assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.media_type("application/xml;q=1")).same_string("('application', 'xml', {'q':'1.0',})") )
assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.parse_media_range("application/xml")).same_string("('application', 'xml', {'q':'1.0',})") ) assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.media_type("application/xml")).same_string("('application', 'xml', {'q':'1.0',})") )
assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.parse_media_range("application/xml;q=")).same_string("('application', 'xml', {'q':'1.0',})") ) assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.media_type("application/xml;q=")).same_string("('application', 'xml', {'q':'1.0',})") )
assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.parse_media_range("application/xml ; q=")).same_string("('application', 'xml', {'q':'1.0',})") ) assert ("Expected ('application', 'xml', {'q':'1',})", format (parser.media_type("application/xml ; q=")).same_string("('application', 'xml', {'q':'1.0',})") )
assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", format (parser.parse_media_range("application/xml ; q=1;b=other")).same_string("('application', 'xml', {'q':'1.0','b':'other',})") ) assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", format (parser.media_type("application/xml ; q=1;b=other")).same_string("('application', 'xml', {'q':'1.0','b':'other',})") )
assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", format (parser.parse_media_range("application/xml ; q=2;b=other")).same_string("('application', 'xml', {'q':'1.0','b':'other',})") ) assert ("Expected ('application', 'xml', {'q':'1','b':'other',})", format (parser.media_type("application/xml ; q=2;b=other")).same_string("('application', 'xml', {'q':'1.0','b':'other',})") )
-- Accept header that includes * -- Accept header that includes *
assert ("Expected ('*', '*', {'q':'.2',})", format (parser.parse_media_range(" *; q=.2")).same_string("('*', '*', {'q':'.2',})")) assert ("Expected ('*', '*', {'q':'.2',})", format (parser.media_type(" *; q=.2")).same_string("('*', '*', {'q':'.2',})"))
end end
@@ -148,7 +148,7 @@ feature -- Test routines
parser : MIME_PARSE parser : HTTP_ACCEPT_MEDIA_TYPE_PARSER
end end