diff --git a/library/network/protocol/content_negotiation/README.md b/library/network/protocol/content_negotiation/README.md index d86bcedb..09a53547 100644 --- a/library/network/protocol/content_negotiation/README.md +++ b/library/network/protocol/content_negotiation/README.md @@ -1,7 +1,6 @@ -CONNEG is a library that provides utilities to select the best repesentation of a resource for a client -where there are multiple representations available. +CONNEG is a library that provides utilities to select the best repesentation of a resource for a client 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 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 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). So for each of the following questions, you will have a corresponding method to help in the solution. diff --git a/library/network/protocol/content_negotiation/conneg-safe.ecf b/library/network/protocol/content_negotiation/conneg-safe.ecf index 06a7bda6..2bb7d7b2 100644 --- a/library/network/protocol/content_negotiation/conneg-safe.ecf +++ b/library/network/protocol/content_negotiation/conneg-safe.ecf @@ -13,6 +13,12 @@ - + + + /implementation + + + diff --git a/library/network/protocol/content_negotiation/conneg.ecf b/library/network/protocol/content_negotiation/conneg.ecf index a4db8e63..6677144a 100644 --- a/library/network/protocol/content_negotiation/conneg.ecf +++ b/library/network/protocol/content_negotiation/conneg.ecf @@ -13,6 +13,12 @@ - + + + /implementation + + + diff --git a/library/network/protocol/content_negotiation/src/conneg_server_side.e b/library/network/protocol/content_negotiation/src/conneg_server_side.e index 7440a799..37e4dbdc 100644 --- a/library/network/protocol/content_negotiation/src/conneg_server_side.e +++ b/library/network/protocol/content_negotiation/src/conneg_server_side.e @@ -17,81 +17,96 @@ class CONNEG_SERVER_SIDE inherit - - SHARED_CONNEG - REFACTORING_HELPER create 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 - set_mime_default (a_mime) - set_language_default (a_language) - set_charset_default (a_charset) - set_encoding_default (a_encoding) + initialize + set_default_media_type (a_mediatype_dft) + set_default_language (a_language_dft) + set_default_charset (a_charset_dft) + set_default_encoding (a_encoding_dft) ensure - mime_default_set: mime_default = a_mime - language_default_set: language_default = a_language - charset_default_set: charset_default = a_charset - encoding_default_set: encoding_default = a_encoding + default_media_type_set: default_media_type = a_mediatype_dft + default_language_set: default_language = a_language_dft + default_charset_set: default_charset = a_charset_dft + default_encoding_set: default_encoding = a_encoding_dft 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. - language_default: READABLE_STRING_8 + default_language: READABLE_STRING_8 -- 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. - encoding_default: READABLE_STRING_8 + default_encoding: READABLE_STRING_8 -- Content-coding that is acceptable in the response. feature -- Change Element - set_mime_default (a_mime: READABLE_STRING_8) - -- Set the mime_default with `a_mime' + set_default_media_type (a_mediatype: READABLE_STRING_8) + -- Set `default_media_type' with `a_mediatype' do - mime_default := a_mime + default_media_type := a_mediatype ensure - mime_default_set: a_mime = mime_default + default_media_type_set: a_mediatype = default_media_type end - set_language_default (a_language: READABLE_STRING_8) - -- Set the language_default with `a_language' + set_default_language (a_language: READABLE_STRING_8) + -- Set `default_language' with `a_language' do - language_default := a_language + default_language := a_language ensure - language_default_set: a_language = language_default + default_language_set: a_language = default_language end - set_charset_default (a_charset: READABLE_STRING_8) - -- Set the charset_default with `a_charset' + set_default_charset (a_charset: READABLE_STRING_8) + -- Set `default_charset' with `a_charset' do - charset_default := a_charset + default_charset := a_charset ensure - charset_default_set: a_charset = charset_default + default_charset_set: a_charset = default_charset end - set_encoding_default (a_encoding: READABLE_STRING_8) + set_default_encoding (a_encoding: READABLE_STRING_8) + -- Set `default_encoding' with `a_encoding' do - encoding_default := a_encoding + default_encoding := a_encoding ensure - encoding_default_set: a_encoding = encoding_default + default_encoding_set: a_encoding = default_encoding end - - feature -- Media Type Negotiation - media_type_preference (a_mime_types_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): MEDIA_TYPE_VARIANT_RESULTS + 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_header represent' the Accept header, ie, the client preferences. -- Return which media type to use for representation in a response, if the server supports @@ -101,30 +116,31 @@ feature -- Media Type Negotiation local l_mime_match: READABLE_STRING_8 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 -- the request has no Accept header, ie the header is empty, in this case we use the default format - Result.set_acceptable (TRUE) - Result.set_type (mime_default) + Result.set_acceptable (True) + Result.set_variant_value (default_media_type) else + Result.set_vary_header_value + -- 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 -- The server does not support any of the media types preferred by the client Result.set_acceptable (False) - Result.set_supported_variants (a_mime_types_supported) else -- Set the best match - Result.set_type (l_mime_match) + Result.set_variant_value (l_mime_match) Result.set_acceptable (True) - Result.set_variant_header end end end feature -- Encoding Negotiation - charset_preference (a_server_charset_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): CHARACTER_ENCODING_VARIANT_RESULTS + 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_header' represents the Accept-Charset header, ie, the client preferences. -- Return which Charset to use in a response, if the server supports @@ -134,32 +150,33 @@ feature -- Encoding Negotiation local l_charset_match: READABLE_STRING_8 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 -- the request has no Accept-Charset header, ie the header is empty, in this case use default charset encoding - Result.set_acceptable (TRUE) - Result.set_type (charset_default) + Result.set_acceptable (True) + Result.set_variant_value (default_charset) else + Result.set_vary_header_value + -- 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 -- The server does not support any of the compression types prefered by the client Result.set_acceptable (False) - Result.set_supported_variants (a_server_charset_supported) else -- Set the best match - Result.set_type (l_charset_match) + Result.set_variant_value (l_charset_match) Result.set_acceptable (True) - Result.set_variant_header end end end feature -- Compression Negotiation - encoding_preference (a_server_encoding_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): COMPRESSION_VARIANT_RESULTS + 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_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 -- the requested Encoding, or empty in other case. note @@ -167,32 +184,33 @@ feature -- Compression Negotiation local l_compression_match: READABLE_STRING_8 do - create Result - if a_header = Void or else a_header.is_empty then + create Result.make + 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 - Result.set_acceptable (TRUE) - Result.set_type (encoding_default) + Result.set_acceptable (True) + Result.set_variant_value (default_encoding) else + Result.set_vary_header_value + -- 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 -- The server does not support any of the compression types prefered by the client Result.set_acceptable (False) - Result.set_supported_variants (a_server_encoding_supported) else -- Set the best match - Result.set_type (l_compression_match) + Result.set_variant_value (l_compression_match) Result.set_acceptable (True) - Result.set_variant_header end end end feature -- Language Negotiation - language_preference (a_server_language_supported: LIST [READABLE_STRING_8]; a_header: detachable READABLE_STRING_8): LANGUAGE_VARIANT_RESULTS + 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_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 -- the requested Language, or empty in other case. note @@ -201,28 +219,29 @@ feature -- Language Negotiation local l_language_match: READABLE_STRING_8 do - create Result - if a_header = Void or else a_header.is_empty then + create Result.make + 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 - Result.set_acceptable (TRUE) - Result.set_type (language_default) + Result.set_acceptable (True) + Result.set_variant_value (default_language) else + Result.set_vary_header_value + -- 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 -- The server does not support any of the media types prefered by the client Result.set_acceptable (False) - Result.set_supported_variants (a_server_language_supported) else -- Set the best match - Result.set_type (l_language_match) + Result.set_variant_value (l_language_match) Result.set_acceptable (True) - Result.set_variant_header end end end - note copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/library/network/protocol/content_negotiation/src/parsers/fitness_and_quality.e b/library/network/protocol/content_negotiation/src/implementation/fitness_and_quality.e similarity index 75% rename from library/network/protocol/content_negotiation/src/parsers/fitness_and_quality.e rename to library/network/protocol/content_negotiation/src/implementation/fitness_and_quality.e index a851ecde..0b7ab39f 100644 --- a/library/network/protocol/content_negotiation/src/parsers/fitness_and_quality.e +++ b/library/network/protocol/content_negotiation/src/implementation/fitness_and_quality.e @@ -1,6 +1,5 @@ note description: "Summary description for {FITNESS_AND_QUALITY}." - author: "" date: "$Date$" revision: "$Revision$" @@ -24,7 +23,7 @@ feature -- Initialization do fitness := a_fitness quality := a_quality - create mime_type.make_empty + create {STRING_8} entity.make_empty ensure fitness_assigned : fitness = a_fitness quality_assigned : quality = a_quality @@ -36,17 +35,17 @@ feature -- Access quality: REAL_64 - mime_type: STRING + entity: READABLE_STRING_8 -- optionally used -- empty by default - + --| Could be a mime type, an encoding, ... feature -- Status report debug_output: STRING -- String that should be displayed in debugger to represent `Current'. do - create Result.make_from_string (mime_type) + create Result.make_from_string (entity) Result.append (" (") Result.append ("quality=" + quality.out) Result.append (" ; fitness=" + fitness.out) @@ -55,12 +54,12 @@ feature -- Status report feature -- Element Change - set_mime_type (a_mime_type: STRING) - -- set mime_type with `a_mime_type' + set_entity (a_entity: READABLE_STRING_8) + -- set `entity' with `a_entity' do - mime_type := a_mime_type + entity := a_entity ensure - mime_type_assigned : mime_type.same_string (a_mime_type) + entity_assigned : entity.same_string (a_entity) end feature -- Comparision @@ -75,7 +74,7 @@ feature -- Comparision end end 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)" end diff --git a/library/network/protocol/content_negotiation/src/parsers/http_accept_language_parser.e b/library/network/protocol/content_negotiation/src/parsers/http_accept_language_parser.e new file mode 100644 index 00000000..4f7b08d2 --- /dev/null +++ b/library/network/protocol/content_negotiation/src/parsers/http_accept_language_parser.e @@ -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 diff --git a/library/network/protocol/content_negotiation/src/parsers/mime_parse.e b/library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_parser.e similarity index 83% rename from library/network/protocol/content_negotiation/src/parsers/mime_parse.e rename to library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_parser.e index 8be798a8..b29369be 100644 --- a/library/network/protocol/content_negotiation/src/parsers/mime_parse.e +++ b/library/network/protocol/content_negotiation/src/parsers/http_accept_media_type_parser.e @@ -1,6 +1,6 @@ note 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" ":" #( 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" class - MIME_PARSE + HTTP_ACCEPT_MEDIA_TYPE_PARSER -inherit {NONE} +inherit - MIME_TYPE_PARSER_UTILITIES + HTTP_HEADER_PARSER REFACTORING_HELPER feature -- Parser - parse_mime_type (a_mime_type: READABLE_STRING_8): HTTP_MEDIA_TYPE - -- Parses a mime-type into its component parts. - -- For example, the media range 'application/xhtml;q=0.5' would get parsed - -- into: - -- ('application', 'xhtml', {'q', '0.5'}) - do - create Result.make_from_string (a_mime_type) - end - - parse_media_range (a_range: READABLE_STRING_8): HTTP_MEDIA_TYPE + media_type (a_range: READABLE_STRING_8): HTTP_MEDIA_TYPE -- Media-ranges are mime-types with wild-cards and a 'q' quality parameter. -- For example, the media range 'application/*;q=0.5' would get parsed into: -- ('application', '*', {'q', '0.5'}) @@ -48,11 +39,11 @@ feature -- Parser -- necessary. do 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 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) then --| Keep current value @@ -68,8 +59,136 @@ feature -- Parser 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 -- that have already been parsed by parse_media_range. local @@ -84,7 +203,7 @@ feature -- Parser do best_fitness := -1 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 target_q := q.to_double if target_q < 0.0 then @@ -163,142 +282,16 @@ feature -- Parser create Result.make (best_fitness, best_fit_q) end - quality_parsed (a_mime_type: READABLE_STRING_8; parsed_ranges: LIST [HTTP_MEDIA_TYPE]): REAL_64 + 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 -- have already been parsed by parse_media_range. Returns the 'q' quality -- parameter of the best match, 0 if no match was found. This function -- bahaves the same as quality except that 'parsed_ranges' must be a list -- of parsed media ranges. do - Result := fitness_and_quality_parsed (a_mime_type, parsed_ranges).quality + Result := fitness_and_quality_from_list (a_mime_type, parsed_ranges).quality end - quality (a_mime_type: READABLE_STRING_8; ranges: READABLE_STRING_8): REAL_64 - -- Returns the quality 'q' of a mime-type when compared against the - -- mediaRanges in ranges. - local - l_ranges : LIST [READABLE_STRING_8] - res : ARRAYED_LIST [HTTP_MEDIA_TYPE] - p_res : HTTP_MEDIA_TYPE - do - l_ranges := ranges.split (',') - from - create res.make (10); - l_ranges.start - until - l_ranges.after - loop - p_res := parse_media_range (l_ranges.item_for_iteration) - res.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 copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" diff --git a/library/network/protocol/content_negotiation/src/parsers/common_accept_header_parser.e b/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_parser.e similarity index 56% rename from library/network/protocol/content_negotiation/src/parsers/common_accept_header_parser.e rename to library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_parser.e index de3e8040..b0d4931f 100644 --- a/library/network/protocol/content_negotiation/src/parsers/common_accept_header_parser.e +++ b/library/network/protocol/content_negotiation/src/parsers/http_any_accept_header_parser.e @@ -1,6 +1,6 @@ note 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$" @@ -9,17 +9,15 @@ note EIS: "name=Encoding", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" class - COMMON_ACCEPT_HEADER_PARSER - -inherit {NONE} - - MIME_TYPE_PARSER_UTILITIES + HTTP_ANY_ACCEPT_HEADER_PARSER +inherit + HTTP_HEADER_PARSER feature -- Parser - parse_common (header: READABLE_STRING_8): COMMON_RESULTS - -- Parses `header' charset/encoding into its component parts. + header (a_header: READABLE_STRING_8): HTTP_ANY_ACCEPT_HEADER + -- Parses `a_header' charset/encoding into its component parts. -- For example, the charset 'iso-8889-5' would get parsed -- into: -- ('iso-8889-5', {'q':'1.0'}) @@ -31,7 +29,7 @@ feature -- Parser l_header: READABLE_STRING_8 do create Result.make - l_parts := header.split (';') + l_parts := a_header.split (';') if l_parts.count = 1 then Result.put ("1.0", "q") else @@ -40,7 +38,7 @@ feature -- Parser until i > l_parts.count loop - p := l_parts.at (i) + p := l_parts [i] sub_parts := p.split ('=') if sub_parts.count = 2 then Result.put (trim (sub_parts [2]), trim (sub_parts [1])) @@ -52,7 +50,141 @@ feature -- Parser Result.set_field (trim (l_header)) 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 -- that have already been parsed by parse_common. Returns a -- tuple of the fitness value and the value of the 'q' quality parameter of @@ -62,14 +194,14 @@ feature -- Parser best_fitness: INTEGER target_q: REAL_64 best_fit_q: REAL_64 - target: COMMON_RESULTS - range: COMMON_RESULTS + target: HTTP_ANY_ACCEPT_HEADER + range: HTTP_ANY_ACCEPT_HEADER element: detachable READABLE_STRING_8 l_fitness: INTEGER do best_fitness := -1 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 target_q := q.to_double if target_q < 0.0 then @@ -111,146 +243,15 @@ feature -- Parser create Result.make (best_fitness, best_fit_q) 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 -- have already been parsed by parse_charsets(). Returns the 'q' quality -- parameter of the best match, 0 if no match was found. This function -- bahaves the same as quality() do - Result := fitness_and_quality_parsed (a_field, parsed_common).quality + Result := fitness_and_quality_from_list (a_field, a_parsed_common).quality end - quality (a_field: READABLE_STRING_8; commons: READABLE_STRING_8): REAL_64 - -- Returns the quality 'q' of a charset/encoding when compared against the - -- a list of charsets/encodings/ - local - l_commons: LIST [READABLE_STRING_8] - res: ARRAYED_LIST [COMMON_RESULTS] - p_res: COMMON_RESULTS - do - l_commons := commons.split (',') - from - create res.make (10) - l_commons.start - until - l_commons.after - loop - p_res := parse_common (l_commons.item_for_iteration) - res.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 copyright: "2011-2013, Javier Velilla, Jocelyn Fiat, Eiffel Software and others" diff --git a/library/network/protocol/content_negotiation/src/parsers/mime_type_parser_utilities.e b/library/network/protocol/content_negotiation/src/parsers/http_header_parser.e similarity index 58% rename from library/network/protocol/content_negotiation/src/parsers/mime_type_parser_utilities.e rename to library/network/protocol/content_negotiation/src/parsers/http_header_parser.e index 7011a93b..0d08278e 100644 --- a/library/network/protocol/content_negotiation/src/parsers/mime_type_parser_utilities.e +++ b/library/network/protocol/content_negotiation/src/parsers/http_header_parser.e @@ -1,14 +1,15 @@ note - description: "{MIME_TYPE_PARSER_UTILITIES}." + description: "Summary description for {HTTP_HEADER_PARSER}." + author: "" date: "$Date$" revision: "$Revision$" -class - MIME_TYPE_PARSER_UTILITIES +deferred class + 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 local p: INTEGER @@ -21,20 +22,17 @@ feature {NONE} -- Implementation 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 -- `a_string' require valid_argument : a_string /= Void - local - l_result: STRING do - l_result := a_string.as_string_8 - l_result.left_adjust - l_result.right_adjust - Result := l_result + create Result.make_from_string (a_string) + Result.left_adjust + Result.right_adjust ensure - result_same_as_argument: Result.same_string_general (a_string) + result_trimmed: a_string.has_substring (Result) end note diff --git a/library/network/protocol/content_negotiation/src/parsers/language_parse.e b/library/network/protocol/content_negotiation/src/parsers/language_parse.e deleted file mode 100644 index b5ef3584..00000000 --- a/library/network/protocol/content_negotiation/src/parsers/language_parse.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/content_negotiation/src/results/http_accept_language.e b/library/network/protocol/content_negotiation/src/results/http_accept_language.e new file mode 100644 index 00000000..afbf8a9c --- /dev/null +++ b/library/network/protocol/content_negotiation/src/results/http_accept_language.e @@ -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 diff --git a/library/network/protocol/content_negotiation/src/results/common_results.e b/library/network/protocol/content_negotiation/src/results/http_any_accept_header.e similarity index 88% rename from library/network/protocol/content_negotiation/src/results/common_results.e rename to library/network/protocol/content_negotiation/src/results/http_any_accept_header.e index 627ba520..502ee3cc 100644 --- a/library/network/protocol/content_negotiation/src/results/common_results.e +++ b/library/network/protocol/content_negotiation/src/results/http_any_accept_header.e @@ -1,10 +1,10 @@ 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$" revision: "$Revision$" class - COMMON_RESULTS + HTTP_ANY_ACCEPT_HEADER create make @@ -36,9 +36,8 @@ feature -- Access Result := res end - 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 @@ -51,7 +50,7 @@ feature -- Status Report feature -- Element change set_field (a_field: STRING) - -- Set type with `a_charset' + -- Set type with `a_field' do field := a_field ensure diff --git a/library/network/protocol/content_negotiation/src/results/language_results.e b/library/network/protocol/content_negotiation/src/results/language_results.e deleted file mode 100644 index 23211c03..00000000 --- a/library/network/protocol/content_negotiation/src/results/language_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/content_negotiation/src/shared_conneg.e b/library/network/protocol/content_negotiation/src/shared_conneg.e index 08c8d174..fdfd9fdd 100644 --- a/library/network/protocol/content_negotiation/src/shared_conneg.e +++ b/library/network/protocol/content_negotiation/src/shared_conneg.e @@ -8,18 +8,18 @@ class feature - Mime: MIME_PARSE + Mime: HTTP_ACCEPT_MEDIA_TYPE_PARSER once create Result end - Common: COMMON_ACCEPT_HEADER_PARSER + Common: HTTP_ANY_ACCEPT_HEADER_PARSER -- Charset and Encoding once create Result end - Language: LANGUAGE_PARSE + Language: HTTP_ACCEPT_LANGUAGE_PARSER once create Result end diff --git a/library/network/protocol/content_negotiation/src/variants/character_encoding_variant_results.e b/library/network/protocol/content_negotiation/src/variants/http_accept_charset_variants.e similarity index 67% rename from library/network/protocol/content_negotiation/src/variants/character_encoding_variant_results.e rename to library/network/protocol/content_negotiation/src/variants/http_accept_charset_variants.e index 862507cd..3a36e0a8 100644 --- a/library/network/protocol/content_negotiation/src/variants/character_encoding_variant_results.e +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_charset_variants.e @@ -1,6 +1,6 @@ note 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. 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. @@ -10,20 +10,22 @@ note revision: "$Revision$" class - CHARACTER_ENCODING_VARIANT_RESULTS + HTTP_ACCEPT_CHARSET_VARIANTS inherit + HTTP_ACCEPT_VARIANTS + rename + variant_value as charset + end - VARIANT_RESULTS +create + make +feature -- Change -feature -- Change Element - - - set_variant_header - -- Set variant header as `Accept-Charset' + set_vary_header_value do - variant_header := {HTTP_HEADER_NAMES}.header_accept_charset -- "Accept-Charset" + vary_header_value := {HTTP_HEADER_NAMES}.header_accept_charset -- "Accept-Charset" end note diff --git a/library/network/protocol/content_negotiation/src/variants/compression_variant_results.e b/library/network/protocol/content_negotiation/src/variants/http_accept_encoding_variants.e similarity index 52% rename from library/network/protocol/content_negotiation/src/variants/compression_variant_results.e rename to library/network/protocol/content_negotiation/src/variants/http_accept_encoding_variants.e index 22d0893d..805bf1a9 100644 --- a/library/network/protocol/content_negotiation/src/variants/compression_variant_results.e +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_encoding_variants.e @@ -1,28 +1,31 @@ note description: "[ - {COMPRESSION_VARIANT_RESULTS} - Represent the compression results between client preferences and ccompression variants supported by the server. + {HTTP_ACCEPT_ENCODING_VARIANTS} + 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 - a response with the list of supported encodings/compressions + a response with the list of supported encodings ]" date: "$Date$" revision: "$Revision$" EIS: "name= Compression", "src=http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3", "protocol=uri" class - COMPRESSION_VARIANT_RESULTS + HTTP_ACCEPT_ENCODING_VARIANTS inherit + HTTP_ACCEPT_VARIANTS + rename + variant_value as encoding + end - VARIANT_RESULTS +create + make +feature -- Change -feature -- Change Element - - set_variant_header - -- Set variant_header as `Accept-Encoding' + set_vary_header_value do - variant_header := {HTTP_HEADER_NAMES}.header_accept_encoding -- "Accept-Encoding" + vary_header_value := {HTTP_HEADER_NAMES}.header_accept_encoding -- "Accept-Encoding" end note diff --git a/library/network/protocol/content_negotiation/src/variants/language_variant_results.e b/library/network/protocol/content_negotiation/src/variants/http_accept_language_variants.e similarity index 65% rename from library/network/protocol/content_negotiation/src/variants/language_variant_results.e rename to library/network/protocol/content_negotiation/src/variants/http_accept_language_variants.e index 7bfb6059..f51b1f87 100644 --- a/library/network/protocol/content_negotiation/src/variants/language_variant_results.e +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_language_variants.e @@ -1,6 +1,6 @@ note description: "[ - {LANGUAGE_VARIANT_RESULTS}. + {HTTP_ACCEPT_LANGUAGE_VARIANTS}. Represent the language results between client preferences and language variants supported by the server. If the server is unable to supports the requested Accept-Language values, the server can build a response with the list of supported languages @@ -9,18 +9,22 @@ note revision: "$Revision$" class - LANGUAGE_VARIANT_RESULTS + HTTP_ACCEPT_LANGUAGE_VARIANTS inherit + HTTP_ACCEPT_VARIANTS + rename + variant_value as language + end - VARIANT_RESULTS +create + make -feature -- Change Element +feature -- Change - set_variant_header - -- Set variant header as 'Accept-Language' + set_vary_header_value do - variant_header := {HTTP_HEADER_NAMES}.header_accept_language -- "Accept-Language" + vary_header_value := {HTTP_HEADER_NAMES}.header_accept_language -- "Accept-Language" end note diff --git a/library/network/protocol/content_negotiation/src/variants/media_type_variant_results.e b/library/network/protocol/content_negotiation/src/variants/http_accept_media_type_variants.e similarity index 52% rename from library/network/protocol/content_negotiation/src/variants/media_type_variant_results.e rename to library/network/protocol/content_negotiation/src/variants/http_accept_media_type_variants.e index 988f8cad..00900ce3 100644 --- a/library/network/protocol/content_negotiation/src/variants/media_type_variant_results.e +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_media_type_variants.e @@ -1,7 +1,7 @@ note description: "[ - {MEDIA_TYPE_VARIANT_RESULTS}. - Represent the media type results between client preferences and media type variants supported by the server.. + {HTTP_ACCEPT_MEDIA_TYPE_VARIANTS}. + 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 a response with the list of supported representations ]" @@ -9,18 +9,22 @@ note revision: "$Revision$" class - MEDIA_TYPE_VARIANT_RESULTS + HTTP_ACCEPT_MEDIA_TYPE_VARIANTS inherit + HTTP_ACCEPT_VARIANTS + rename + variant_value as media_type + end - VARIANT_RESULTS +create + make -feature -- Change Element +feature -- Change - set_variant_header - -- Set variant header as `Accept' + set_vary_header_value do - variant_header := {HTTP_HEADER_NAMES}.header_accept -- "Accept" + vary_header_value := {HTTP_HEADER_NAMES}.header_accept -- "Accept" end note diff --git a/library/network/protocol/content_negotiation/src/variants/http_accept_variants.e b/library/network/protocol/content_negotiation/src/variants/http_accept_variants.e new file mode 100644 index 00000000..86295d8b --- /dev/null +++ b/library/network/protocol/content_negotiation/src/variants/http_accept_variants.e @@ -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 diff --git a/library/network/protocol/content_negotiation/src/variants/variant_results.e b/library/network/protocol/content_negotiation/src/variants/variant_results.e deleted file mode 100644 index bb48cacf..00000000 --- a/library/network/protocol/content_negotiation/src/variants/variant_results.e +++ /dev/null @@ -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 diff --git a/library/network/protocol/content_negotiation/test/application.e b/library/network/protocol/content_negotiation/test/application.e index a7078001..b7152129 100644 --- a/library/network/protocol/content_negotiation/test/application.e +++ b/library/network/protocol/content_negotiation/test/application.e @@ -16,10 +16,10 @@ feature {NONE} -- Initialization make local - mime_parse : MIME_PARSE + mime_parse : HTTP_ACCEPT_MEDIA_TYPE_PARSER accept : STRING - charset_parse : COMMON_ACCEPT_HEADER_PARSER - language : LANGUAGE_PARSE + charset_parse : HTTP_ANY_ACCEPT_HEADER_PARSER + language : HTTP_ACCEPT_LANGUAGE_PARSER do create mime_parse -- 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) accept := "application/atom+xml" - print ("%N"+mime_parse.parse_mime_type (accept).out) + print ("%N"+mime_parse.media_type (accept).out) create charset_parse 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" - 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" @@ -78,10 +78,10 @@ feature {NONE} -- Initialization print (language.best_match (accept.split (','), "da")) print (language.best_match (accept.split (','), "en-*")) - print ("%N"+language.parse_language_range ("da").out) - print ("%N"+language.parse_language_range ("en-gb;q=0.8").out) - print ("%N"+language.parse_language_range ("en;q=0.7").out) - print ("%N"+language.parse_language_range ("en-*").out) + print ("%N"+language.accept_language ("da").out) + print ("%N"+language.accept_language ("en-gb;q=0.8").out) + print ("%N"+language.accept_language ("en;q=0.7").out) + print ("%N"+language.accept_language ("en-*").out) end end diff --git a/library/network/protocol/content_negotiation/test/common_accept_header_parser_test.e b/library/network/protocol/content_negotiation/test/common_accept_header_parser_test.e index 9cd88ad5..3c3dcbf6 100644 --- a/library/network/protocol/content_negotiation/test/common_accept_header_parser_test.e +++ b/library/network/protocol/content_negotiation/test/common_accept_header_parser_test.e @@ -23,7 +23,7 @@ feature {NONE} -- Events feature -- Helpers - format (a_common: COMMON_RESULTS): STRING + format (a_common: HTTP_ANY_ACCEPT_HEADER): STRING -- Representation of the current object do create Result.make_from_string ("(") @@ -47,9 +47,9 @@ feature -- Test routines test_parse_charsets 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 ('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 ('*', {'q':'1.0',})", format (parser.parse_common("*")).same_string("('*', {'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.header("unicode-1-1;q=0.8")).same_string("('unicode-1-1', {'q':'0.8',})") ) + assert ("Expected ('*', {'q':'1.0',})", format (parser.header("*")).same_string("('*', {'q':'1.0',})") ) 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")) end - parser : COMMON_ACCEPT_HEADER_PARSER + parser : HTTP_ANY_ACCEPT_HEADER_PARSER end diff --git a/library/network/protocol/content_negotiation/test/conneg_server_side_test.e b/library/network/protocol/content_negotiation/test/conneg_server_side_test.e index 1ff829d2..d0c1e518 100644 --- a/library/network/protocol/content_negotiation/test/conneg_server_side_test.e +++ b/library/network/protocol/content_negotiation/test/conneg_server_side_test.e @@ -24,7 +24,7 @@ feature {NONE} -- Events feature -- Test routines test_media_type_negotiation local - media_variants : MEDIA_TYPE_VARIANT_RESULTS + media_variants : HTTP_ACCEPT_MEDIA_TYPE_VARIANTS mime_types_supported : LIST [STRING] l_types : STRING do @@ -39,22 +39,22 @@ feature -- Test routines else assert ("Has supported_variants results", False) end - assert ("Variant header is void",media_variants.variant_header = Void) - assert ("Media type is void",media_variants.type = 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.media_type = Void) -- Scenario 2, the client doesnt send values in the header, Accept: media_variants := conneg.media_type_preference (mime_types_supported, "") assert ("Expected Acceptable", media_variants.is_acceptable) - assert ("Variants is dettached",media_variants.supported_variants = Void) - assert ("Mime is default", attached media_variants.type as l_type and then conneg.mime_default.is_equal (l_type)) - assert ("Variant header", media_variants.variant_header = Void) + assert ("Variants is set",media_variants.supported_variants = mime_types_supported) + 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.vary_header_value = Void) --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 ("Variants is dettached",media_variants.supported_variants = Void) - assert ("Variant Header", attached media_variants.variant_header 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 ("Variants is set",media_variants.supported_variants = mime_types_supported) + 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.media_type as l_media_type and then l_media_type.same_string ("application/json")) end @@ -62,13 +62,13 @@ feature -- Test routines test_charset_negotiation local - charset_variants : CHARACTER_ENCODING_VARIANT_RESULTS + charset_variants : HTTP_ACCEPT_CHARSET_VARIANTS charset_supported : LIST [STRING] - l_charset : STRING + l_charset_value : STRING do -- Scenario 1, the server side does not support client preferences - l_charset := "UTF-8, iso-8859-5" - charset_supported := l_charset.split(',') + l_charset_value := "UTF-8, iso-8859-5" + charset_supported := l_charset_value.split(',') charset_variants := conneg.charset_preference (charset_supported, "unicode-1-1") assert ("Expected Not Acceptable", not charset_variants.is_acceptable) if attached charset_variants.supported_variants as l_supported_variants then @@ -77,29 +77,29 @@ feature -- Test routines else assert("Has supported_variants results", False) end - assert ("Variant header is void",charset_variants.variant_header = Void) - assert ("Character type is void",charset_variants.type = 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.charset = Void) -- Scenario 2, the client doesnt send values in the header, Accept-Charset: charset_variants := conneg.charset_preference (charset_supported, "") assert ("Expected Acceptable", charset_variants.is_acceptable) - assert ("Variants is dettached",charset_variants.supported_variants = Void) - assert ("Charset is defaul", attached charset_variants.type as l_type and then conneg.charset_default.is_equal (l_type)) - assert ("Variant header", charset_variants.variant_header = Void) + assert ("Variants is set",charset_variants.supported_variants = charset_supported) + 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.vary_header_value = Void) --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") assert ("Expected Acceptable", charset_variants.is_acceptable) - assert ("Variants is dettached",charset_variants.supported_variants = Void) - assert ("Variant Header", attached charset_variants.variant_header 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 ("Variants is set",charset_variants.supported_variants = charset_supported) + 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.charset as l_charset and then l_charset.same_string ("iso-8859-5")) end test_compression_negotiation local - compression_variants : COMPRESSION_VARIANT_RESULTS + compression_variants : HTTP_ACCEPT_ENCODING_VARIANTS compression_supported : LIST [STRING] l_compression : STRING do @@ -109,39 +109,39 @@ feature -- Test routines compression_variants := conneg.encoding_preference (compression_supported, "gzip") assert ("Expected Not Acceptable", not compression_variants.is_acceptable) 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) else assert ("Has supported_variants results", False) end - assert ("Variant header is void",compression_variants.variant_header = Void) - assert ("Compression type is void",compression_variants.type = 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.encoding = Void) -- Scenario 2, the client doesnt send values in the header, Accept-Encoding compression_variants := conneg.encoding_preference (compression_supported, "") assert ("Expected Acceptable", compression_variants.is_acceptable) - assert ("Variants is dettached",compression_variants.supported_variants = Void) - assert ("Compression is defaul", attached compression_variants.type as l_type and then conneg.encoding_default.same_string (l_type)) - assert ("Variant header", compression_variants.variant_header = Void) + assert ("Variants is set",compression_variants.supported_variants = compression_supported) + 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.vary_header_value = Void) --Scenario 3, the server select the best match, and set the vary header l_compression := "gzip" 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") assert ("Expected Acceptable", compression_variants.is_acceptable) - assert ("Variants is dettached",compression_variants.supported_variants = Void) - assert ("Variant Header", attached compression_variants.variant_header 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 ("Variants is set",compression_variants.supported_variants = compression_supported) + 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.encoding as l_type and then l_type.same_string ("gzip")) end test_language_negotiation local - language_variants : LANGUAGE_VARIANT_RESULTS + language_variants : HTTP_ACCEPT_LANGUAGE_VARIANTS languages_supported : LIST [STRING] l_languages : STRING do @@ -150,8 +150,8 @@ feature -- Test routines languages_supported := l_languages.split(',') language_variants := conneg.language_preference (languages_supported, "de") assert ("Expected Not Acceptable", not language_variants.is_acceptable) - assert ("Variant header is void",language_variants.variant_header = Void) - assert ("Language type is void",language_variants.type = 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.language = Void) 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 count",languages_supported.count = l_supported_variants.count) @@ -159,21 +159,19 @@ feature -- Test routines assert ("Has supported variants results", False) end - -- Scenario 2, the client doesnt send values in the header, Accept-Language: language_variants := conneg.language_preference (languages_supported, "") assert ("Expected Acceptable", language_variants.is_acceptable) - assert ("Variants is dettached",language_variants.supported_variants = Void) - assert ("Language is default", attached language_variants.type as l_type and then conneg.language_default.same_string (l_type)) - assert ("Variant header", language_variants.variant_header = Void) - + assert ("Variants is attached",language_variants.supported_variants = languages_supported) + 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.vary_header_value = Void) --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") assert ("Expected Acceptable", language_variants.is_acceptable) - assert ("Variants is dettached",language_variants.supported_variants = Void) - assert ("Variant Header", attached language_variants.variant_header 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 ("Variants is detached",language_variants.supported_variants = languages_supported) + 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.language as l_lang and then l_lang.same_string ("fr")) end feature -- Implementation diff --git a/library/network/protocol/content_negotiation/test/exception_trace.log b/library/network/protocol/content_negotiation/test/exception_trace.log new file mode 100644 index 00000000..3d5d5442 --- /dev/null +++ b/library/network/protocol/content_negotiation/test/exception_trace.log @@ -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 diff --git a/library/network/protocol/content_negotiation/test/language_parser_test.e b/library/network/protocol/content_negotiation/test/language_parser_test.e index fe557164..42acbbb8 100644 --- a/library/network/protocol/content_negotiation/test/language_parser_test.e +++ b/library/network/protocol/content_negotiation/test/language_parser_test.e @@ -23,14 +23,14 @@ feature {NONE} -- Events feature -- Helpers - format (a_language: LANGUAGE_RESULTS): STRING + format (a_language: HTTP_ACCEPT_LANGUAGE): STRING -- Representation of the current object do 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 + "',") end - if attached a_language.sub_type as st then + if attached a_language.specialization as st then Result.append_string (" '" + st + "',") end Result.append_string (" {") @@ -52,10 +52,10 @@ feature -- Test routines test_parse_language do - assert ("Expected ('da', {'q':'1.0',})", format (parser.parse_language_range ("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', {'q':'0.7',})", format (parser.parse_language_range ("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 ('da', {'q':'1.0',})", format (parser.accept_language ("da")).same_string ("('da', {'q':'1.0',})")); + 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.accept_language ("en;q=0.7")).same_string ("('en', {'q':'0.7',})")); + assert ("Expected ('en', '*', {'q':'1.0',})", format (parser.accept_language ("en-*")).same_string ("('en', '*', {'q':'1.0',})")); end @@ -73,53 +73,53 @@ feature -- Test routines test_best_match local - mime_types_supported : LIST [STRING] + langs_supported : LIST [STRING] l_types : STRING do l_types := "en-gb,en-us" - mime_types_supported := l_types.split(',') - assert ("Expected en-us", parser.best_match (mime_types_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 second choice with a q parameter", parser.best_match (mime_types_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 ("Match using a type wildcard", parser.best_match (mime_types_supported, "*").same_string ("en-gb")) + langs_supported := l_types.split(',') + 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 (langs_supported, "en-gb;q=1").same_string ("en-gb")) + 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 (langs_supported, "en-*;q=1").is_equal ("en-gb")) + assert ("Match using a type wildcard", parser.best_match (langs_supported, "*").same_string ("en-gb")) l_types := "en-gb,es" - mime_types_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 ("Fail to match anything",parser.best_match (mime_types_supported, "fr; q=0.9").same_string ("")) + langs_supported := l_types.split(',') + 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 (langs_supported, "fr; q=0.9").same_string ("")) l_types := "en-gb,en-us" - mime_types_supported := l_types.split(',') - assert ("Test 1 verify fitness ordering", parser.best_match (mime_types_supported, "en-gb,en-us,*").same_string ("en-gb")) + langs_supported := l_types.split(',') + 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" - mime_types_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")) + langs_supported := l_types.split(',') + 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" - mime_types_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")) + langs_supported := l_types.split(',') + 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" - mime_types_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")) + langs_supported := l_types.split(',') + 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" - mime_types_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")) + langs_supported := l_types.split(',') + 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" - mime_types_supported := l_types.split(',') - assert ("Test 1", parser.best_match (mime_types_supported, "es,*/*;q=0.1,en").same_string ("es")) + langs_supported := l_types.split(',') + assert ("Test 1", parser.best_match (langs_supported, "es,*/*;q=0.1,en").same_string ("es")) l_types := "fr;q=1.0,en,es" - mime_types_supported := l_types.split(',') - assert ("Test 2", parser.best_match (mime_types_supported, "en,es,*/*;q=0.1").same_string ("en")) + langs_supported := l_types.split(',') + assert ("Test 2", parser.best_match (langs_supported, "en,es,*/*;q=0.1").same_string ("en")) l_types := "es,en;q=0.6" - mime_types_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")) + langs_supported := l_types.split(',') + assert ("Test 2", parser.best_match (langs_supported, "fr;q=1.0, en;q=0.6, es").same_string ("es")) end @@ -137,7 +137,7 @@ feature -- Test routines - parser : LANGUAGE_PARSE + parser : HTTP_ACCEPT_LANGUAGE_PARSER end diff --git a/library/network/protocol/content_negotiation/test/mime_parser_test.e b/library/network/protocol/content_negotiation/test/mime_parser_test.e index a63b8505..0dbe8034 100644 --- a/library/network/protocol/content_negotiation/test/mime_parser_test.e +++ b/library/network/protocol/content_negotiation/test/mime_parser_test.e @@ -54,15 +54,15 @@ feature -- Test routines test_parse_media_range 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.parse_media_range("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','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.parse_media_range("application/xml ; q=2;b=other")).same_string("('application', 'xml', {'q':'1.0','b':'other',})") ) + 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.media_type("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.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.media_type("application/xml ; q=2;b=other")).same_string("('application', 'xml', {'q':'1.0','b':'other',})") ) -- 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 @@ -148,7 +148,7 @@ feature -- Test routines - parser : MIME_PARSE + parser : HTTP_ACCEPT_MEDIA_TYPE_PARSER end